Ошибка метода messages getlongpollserver

Python, API, Вконтакте API, Из песочницы


Рекомендация: подборка платных и бесплатных курсов Python — https://katalog-kursov.ru/

Доброго времени суток! Недавно я решил познакомиться с API крупнейшей социальной сети Европы — ВКонтакте. В разделе «Для разработчиков» содержится довольно подробная документация, а в интернете существует немалое количество статей, помогающих освоиться с VK API, поэтому я решил, что серьезных проблем в изучении быть не должно. Однако, когда я добрался до LongPoll сервера, обнаружилось, что статей по работе с ним практически нет, а официальная документация не настолько полна, чтобы полностью понять изучаемый материал. Пришлось методом проб и ошибок пытаться понять принцип работы LongPoll-а, что через некоторое время мне сделать все-таки удалось. Я решил поделиться изученным материалом с другими людьми, чтобы сократить их время изучения нового. Ниже вы можете ознакомиться с разделами, про которые мне удалось написать.

Основы работы с API (те, кто знаком с базовыми методами и запросами к ним, могут пропустить этот пункт)

Что же такое API ВКонтакте? Вот как трактует это официальная документация:

Callback API — это инструмент для отслеживания активности пользователей в Вашем сообществе ВКонтакте.

Другими словами, это способ отправлять запросы на серверы ВКонтакте, чтобы получить ответ, содержащий некоторую информацию. С помощью VK API можно сделать практически все: отправить сообщение пользователю, изменить пароль учетной записи, создать альбом с фотографиями и так далее. Лично я для работы с API использую библиотеку vk_api, но можно обойтись и библиотекой requests. Сейчас мы рассмотрим базовые принципы работы. Если вы решили использовать библиотеку vk_api, то пример запроса будет выглядеть так:

import vk_api
session = vk_api.VkApi(token='{ACCESS_TOKEN}')
print(session.method('users.get', {'user_ids': 210700286, 'fields': 'photo_50, city, verified'}))

Разберем по строчкам:

import vk_api

Здесь импортируется нужная нам библиотека.

session = vk_api.VkApi(token='{ACCESS_TOKEN}')

Этой строкой мы авторизируемся через access token (способы его получения доступно описаны в документации и интернете, я не буду заострять на них внимание). Заметьте: не для всех методов нужна авторизация. О том, когда она требуется, будет написано в документации метода на сайте ВКонтакте.

print(session.method('users.get', {'user_ids': 210700286, 'fields': 'photo_50, city'}))

Эта часть кода заслуживает отдельного внимания. Что же здесь происходит? Мы вызываем метод method, передавая ему два аргумента: первый — название метода API, второй — словарь из параметров этого метода. Список всех методов и их параметров находится в документации API. В данном случае мы вызываем метод «users.get» и передаем следующие параметры: «user_ids» — список id пользователей, для которых мы хотим получить данные, «fields»: дополнительную информацию о пользователях: аватарку и город проживания. Результатом выполнения программы будет следующая выведенная строка: [{'id': 210700286, 'first_name': 'Lindsey', 'last_name': 'Stirling', 'city': {'id': 5331, 'title': 'Los Angeles'}, 'photo_50': 'https://pp.userapi.com/c636821/v636821286/38a75/Ay-bEZoJZw8.jpg'}]

Если допустить какую-либо ошибку, например, указать неправильное название вызываемого метода, будет возбуждено исключение: vk_api.exceptions.ApiError: [5] User authorization failed: no access_token passed..
Примечание: если ошибка допущена в названии необязательного параметра, исключение сгенерировано не будет, а неизвестный параметр будет проигнорирован.

Если вы решили использовать библиотеку requests, придется немного углубиться в документацию API, чтобы узнать формат запроса и ответа.
Запрос должен выглядеть так: https://api.vk.com/method/{METHOD_NAME}?{PARAMS}&v={API_VERSION}, где {METHOD_NAME} — название метода, {PARAMS} — параметры вызываемого метода, а {API_VERSION} — версия API, которую должен использовать сервер при формировании ответа.
В качестве ответа нам будет возвращен JSON объект с информацией о результатах вызова метода. Давайте реализуем запрос к API с помощью библиотеки requests:

import requests
data = requests.get('https://api.vk.com/method/{METHOD_NAME}'.format(METHOD_NAME='users.get'),
                    params={'user_ids': 210700286, 'fields': 'photo_50, city'}).json()
print(data)

Итак, разберем написанное.

import requests

Импортирование библиотеки requests

data = requests.get('https://api.vk.com/method/{METHOD_NAME}'.format(METHOD_NAME='users.get'),
                    params={'user_ids': 210700286, 'fields': 'photo_50, city'}).json()

Эта строка записывает в переменную data ответ сервера. Мы передаем функции get два аргумента: адрес, к которому нужно сделать запрос (в нашем случае — форматированную строку), и словарь из параметров этого метода. Если метод не вызывается без access token-а, нужно добавить его в словарь с ключем ‘access_token’. Параметр «v» (версия API) не является обязательным, так как по умолчанию будет использоваться последняя версия, но, если нужно использовать иную версию, ее тоже нужно добавить в словарь с ключом ‘v’. К пришедшему от сервера ответу применяется метод json(), который обрабатывает JSON-объекты.

Последняя строка выводит результат работы, в нашем случае — {'response': [{'uid': 210700286, 'first_name': 'Lindsey', 'last_name': 'Stirling', 'city': 5331, 'photo_50': 'https://pp.userapi.com/c636821/v636821286/38a75/Ay-bEZoJZw8.jpg'}]}.

Если передать неправильное название метода, будет выведено следующее: {'error': {'error_code': 5, 'error_msg': 'User authorization failed: no access_token passed.', 'request_params': [{'key': 'oauth', 'value': '1'}, {'key': 'method', 'value': 'user.get'}, {'key': 'user_ids', 'value': '210700286'}, {'key': 'fields', 'value': 'photo_50, city'}]}}.

Что такое LongPoll?

Для начала обратимся за помощью к документации:

Long Polling — это технология, которая позволяет получать информацию о новых событиях с помощью «длинных запросов». Сервер получает запрос, но отправляет ответ на него не сразу, а лишь тогда, когда произойдет какое-либо событие (например, поступит новое входящее сообщение), либо истечет заданное время ожидания.

Другими словами, получая от вас запрос, сервер ждет, когда произойдет событие, о котором он должен вас уведомить, и, когда оно происходит, Long Poll сервер отправляет ответ на ваш запрос, содержащий информацию о случившемся событии.
Мы напишем программу, которая будет уведомлять пользователя о некоторых изменениях в его аккаунте, которые мы будем получать от Long Poll сервера. Чтобы начать получать ответы от сервера, необходимо получить три обязательных параметра, необходимых для работы Long Poll-a: server, key и ts.

key — секретный ключ сессии;
server — адрес сервера;
ts — номер последнего события, начиная с которого нужно получать данные.

Их не нужно получать каждый раз, вызывая Long Poll, — будет достаточно одного вызова. Из документации следует, что эти значения можно получить, вызвав метод messages.getLongPollServer. Напишем программу, которая сделает запрос с этим методом и будем использовать полученные данные для получения к Long Poll.

import requests
token = ''  # здесь вы должны написать свой access_token
data = requests.get('https://api.vk.com/method/messages.getLongPollServer',
                    params={'access_token': token}).json()['response']  # получение ответа от сервера
print(data)

Если вы все сделали правильно, программа выведет словарь с тремя ключами: {'key': '###############################', 'server': 'imv4.vk.com/im####', 'ts': 0000000000}

Запрос к Long Poll серверу

Теперь, используя значения, хранящиеся в словаре, который был создан в предыдущей части, мы можем сделать запрос к Long Poll серверу! Чтобы это сделать, нужно сделать запрос к следующему адресу: https://{$server}?act=a_check&key={$key}&ts={$ts}&wait=25&mode=2&version=2, где {$server}, {$key} и {$ts}- значения из словаря с ключами ‘server’, ‘key’ и ‘ts’ соответственно, wait — время, которое Long Poll сервер будет ожидать обновлений, а mode — дополнительные опции ответа. Напишем программу, которая сделает один запрос на Long Poll сервер

import requests
token = ''  # здесь вы должны написать свой access_token
params = requests.get('https://api.vk.com/method/messages.getLongPollServer',
                       params={'access_token': token}).json()['response']  # получение ответа от сервера
response = requests.get('https://{server}?act=a_check&key={key}&ts={ts}&wait=90&mode=2&version=2'.format(server=data['server'], key=data['key'], ts=data['ts'])).json()  # отправление запроса на Long Poll сервер со временем ожидания 90 секунд и опциями ответа 2
print(response)

Если за 90 секунд произошло какое-либо событие, которое попадает в список обрабатываемых Long Poll сервером, на экран выведется нечто похожее: {'ts': 0000000000, 'updates': [[9, -999999999, 0, 1501588841]]}. Что же значит пришедший ответ и как с ним дальше можно работать?

Во-первых, вы могли заметить, что в ответе содержится параметр ‘ts’, который мы использовали при отправлении запроса. Он здесь неспроста. Все события, попадающие в список обрабатываемых Long Poll сервера, нумеруются. Когда вы создаете новый аккаунт, первое такое событие имеет номер 1, второе — 2 и так далее. При отправлении запроса на Long Poll, нужно передавать этот параметр для того, чтобы сервер знал, начиная с какого номера нужно присылать обновления. В каждом ответе сервера также приходит этот параметр, чтобы вы использовали его при следующем вызове Long Poll сервера.

Во-вторых, в словаре содержится ключ ‘updates’ с говорящим названием. Не трудно догадаться, что значение по этому ключу хранит обновления, случившиеся после отправления запроса. Формат обновлений — массив массивов. Каждый массив в массиве — произошедшее обновление, которое нужно обработать. Если в первом массиве массивов больше одного, это значит, что несколько событий произошли одновременно. Параметр ‘ts’ содержит номер последнего из них. Если массив, доступный по ключу ‘updates’, пуст, то за время wait не произошло ни одного события. Вы спросите: «А что это за непонятные цифры в массивах?». Ответ довольно прост — это информация о случившемся событии, которую можно преобразовать в более понятный вид. Их обработкой мы займемся позже, а в следующей части напишем программу, которая будет постоянно обращаться к Long Poll серверу.

Циклические запросы к Long Poll и коды событий

Для того, чтобы постоянно отправлять запросы к Long Poll, я решил использовать цикл, чтобы не переполнять стек рекурсией. Ниже приведена реализация программы, которая обращается к Long Poll и выводит обновления на экран

import requests
token = ''  # здесь вы должны написать свой access_token
data = requests.get('https://api.vk.com/method/messages.getLongPollServer',
                    params={'access_token': token}).json()['response']  # получение ответа от сервера
while True:
    response = requests.get('https://{server}?act=a_check&key={key}&ts={ts}&wait=20&mode=2&version=2'.format(server=data['server'], key=data['key'], ts=data['ts'])).json()  # отправление запроса на Long Poll сервер со временем ожидания 20 и опциями ответа 2
    updates = response['updates']
    if updates:  # проверка, были ли обновления
        for element in updates:  # проход по всем обновлениям в ответе
            print(element)
    data['ts'] = response['ts']  # обновление номера последнего обновления

Данная программа циклически посылает запросы к Long Poll-у, проверяет, были ли обновления, и выводит пришедшие обновления на экран. Но выходные данные этой программы в том виде, в котором они есть сейчас, никуда не годятся. Сейчас вывод программы выглядит подобным образом:
[8, -999999999, 1, 1501592696]
[8, -999999999, 7, 1501592862]
[9, -999999999, 0, 1501592882]
[9, -999999999, 1, 1501592583]
[8, -999999999, 4, 1501592893]
[9, -999999999, 0, 1501592900]

Первая цифра в каждом массиве означает код события. Используя ее, можно понять, какое событие произошло. Вот список кодов событий с кратким описанием (из официальной документации):
1 — Замена флагов сообщения (FLAGS:=$flags);
2 — Установка флагов сообщения (FLAGS|=$mask);
3 — Сброс флагов сообщения (FLAGS&=~$mask);
4 — Добавление нового сообщения;
6 — Прочтение всех входящих сообщений в $peer_id, пришедших до сообщения с $local_id.
7 — Прочтение всех исходящих сообщений в $peer_id, пришедших до сообщения с $local_id.
8 — Друг $user_id стал онлайн. $extra не равен 0, если в mode был передан флаг 64. В младшем байте (остаток от деления на 256) числа extra лежит идентификатор платформы $timestamp — время последнего действия пользователя $user_id на сайте;
9 — Друг $user_id стал оффлайн ($flags равен 0, если пользователь покинул сайт (например, нажал выход) и 1, если оффлайн по таймауту (например, статус away)). $timestamp — время последнего действия пользователя $user_id на сайте;
10 — Сброс флагов диалога $peer_id. Соответствует операции (PEER_FLAGS &= ~$flags). Только для диалогов сообществ;
11 — Замена флагов диалога $peer_id. Соответствует операции (PEER_FLAGS:= $flags). Только для диалогов сообществ;
12 — Установка флагов диалога $peer_id. Соответствует операции (PEER_FLAGS|= $flags). Только для диалогов сообществ;
13 — Удаление всех сообщений в диалоге $peer_id с идентификаторами вплоть до $local_id;
14 — Восстановление недавно (менее 20 дней назад) удаленных сообщений в диалоге $peer_id с идентификаторами вплоть до $local_id;
51 — Один из параметров (состав, тема) беседы $chat_id были изменены. $self — 1 или 0 (вызваны ли изменения самим пользователем);
61 — Пользователь $user_id набирает текст в диалоге. Событие приходит раз в ~5 секунд при постоянном наборе текста. $flags = 1;
62 — Пользователь $user_id набирает текст в беседе $chat_id;
70 — Пользователь $user_id совершил звонок с идентификатором $call_id;
80 — Счетчик непрочитанных в левом меню стал равен $count;
112 — Изменились настройки оповещений. $peer_id — идентификатор чата/собеседника.

Таким образом, если первое число в массиве — 8, то кто-то из ваших друзей стал онлайн, если 9 — оффлайн и так далее. Остальные числа в массиве тоже имеют значение, но к ним мы подберемся позже, так как их значение зависит от кода события.

Обработка приходящих событий

В этой части мы реализуем обработку событий, которые приходят нам в качестве ответа. Напомню, что эти события представлены в виде массивов с информацией, которую нужно обработать. Начать предлагаю с кода 80 — обновление счетчика непрочитанных сообщений, так как по моему мнению это событие является наименее сложным. Вот пример событий с кодом 80:
[80, 0, 0]
[80, 1, 0]

Первый параметр, как вы уже знаете, — код события. Остаются 2 элемента: 0 и 0 в первом случае и 1 0 во втором. Последний параметр при коде 80 должен быть всегда равен нулю, поэтому его можно игнорировать; для нас важен лишь второй. Во втором параметре указано, сколько на данный момент у пользователя непрочитанных сообщений. Минимальное значение — 0 (нет новых сообщений), максимальное — не ограничено. Этого достаточно, чтобы обрабатывать все события с кодом 80. Реализуем это в код:

# часть кода в этом примере опущена для сокращения места, без нее код работать не будет
while True:
    response = requests.get('https://{server}?act=a_check&key={key}&ts={ts}&wait=20&mode=2&version=2'.format(server=data['server'], key=data['key'], ts=data['ts'])).json()  # отправление запроса на Long Poll сервер со временем ожидания 20 и опциями ответа 2
    updates = response['updates']
    if updates:  # проверка, были ли обновления
        for element in updates:  # проход по всем обновлениям в ответе
            action_code = element[0]  # запись в переменную кода события
            if action_code == 80:  # проверка кода события
                print('количество непрочитанных сообщений стало равно', element[1])  # вывод
    data['ts'] = response['ts']  # обновление номера последнего обновления

Если запустить эту программу, она будет выводить на экран сообщения об изменении количества непрочитанных сообщений. Следующие относительно несложные обновления имеют коды 8 и 9 — друг стал онлайн и оффлайн соответственно. Дополним нашу программу, чтобы она смогла обрабатывать и их. Начнем с кода 9. Напишем строку, которая будет проверять код события:

elif action_code == 9:

Далее рассмотрим формат приходящих обновлений, имеющих код 9:
[9, -000000000, 1, 1501744865]
С индексом 0, как вы уже знаете, хранится код обновления — 9. Далее идет отрицательное id пользователя, ставшего оффлайн (чтобы получить действительное id, нужно умножить на -1, дабы избавиться от минуса). Элемент с индексом 3 может принимать лишь 2 значения: 0 или 1. 1 означает, что отметка «оффлайн» поставлена по истечении тайм-аута неактивности, а 0 — что пользователь покинул сайт явно, например, нажав кнопку «выход». Последнее значение в ответе — время последнего действия пользователя на сайте в Unix time. Запишем все полученные сведения в переменные:

user_id = element[1] * -1  # id пользователя, ставшего оффлайн
user = requests.get('https://api.vk.com/method/users.get', params={'user_ids': user_id, 'fields': 'sex'}).json()['response'][0]  # имя, фамилия и пол пользователя с id = user_id 
timeout = bool(element[2])  # был ли поставлен статус оффлайн по истечении тайм-аута
last_visit = element[3]  # время последнего действия пользователя на сайте в Unix time

Как вы могли заметить, помимо имени и фамилии пользователя, мы получаем еще и его пол. Это нужно, чтобы программа правильно использовала рода в предложениях и не писала что-то вроде «Иван Иванов вышела из ВКонтакте». Переменная timeout хранит False, если пользователь явно покинул сайт, и True, если истекло время тайм-аута. Теперь можно вывести на экран полученные данные:

# не забудьте написать вначале "import time", здесь используется эта библиотека
if user['sex'] == 1:
    verb = ['стала', 'вышла']
else:
    verb = ['стал', 'вышел']
if timeout:
    print(user['first_name'], user['last_name'], verb[0], 'оффлайн по истечении тайм-аута. Время последнего действия на сайте:', time.ctime(last_visit).split()[3])
else:
    print(user['first_name'], user['last_name'], verb[1], 'из ВКонтакте. Время последнего действия на сайте:', time.ctime(last_visit).split()[3])

Единственное, что я вижу неочевидным в этом коде — вывод времени:

time.ctime(last_visit).split()[3]
Давайте разберемся с этой частью кода. Функция ctime библиотеки time принимает в качестве аргумента число — время в Unix time и возвращает строку вида 'Thu Jan 1 03:00:00 1970'
. Так как нам нужно лишь время последнего действия на сайте, мы разбиваем эту строку методом split по пробелам и получаем массив, где в индексе 0 хранится день недели, в индексе 1 — месяц, 2 — число, 3 — время, а 4 — год. Мы извлекаем элемент с индексом 3, то есть время.

Теперь напишем реализацию обработки обновлений с кодом 8. Их формат выглядит так: [8, -6892937, 4, 1501750273]. Второй элемент в массиве — отрицательное id пользователя, ставшего онлайн, третий — платформа, с которой зашел пользователь, а четвертый — время последнего действия пользователя на сайте в Unix time. Реализуем полученные данные в код:

user_id = element[1] * -1  # id пользователя, ставшего онлайн
user = requests.get('https://api.vk.com/method/users.get', params={'user_ids': user_id, 'fields': 'sex'}).json()['response'][0]  # имя, фамилия и пол пользователя с id = user_id
platform = element[2]  # код платформы пользователя
last_visit = element[3]  # время последнего визита в Unix time
if user['sex'] == 1:
    verb = 'стала'
else:
    verb = 'стал'

Здесь мы записали данные из ответа в переменные, а также поставили глагол в нужный род. Параметр platform сейчас содержит число в диапазоне от 1 до 7 включительно. Каждое число обозначает платформу, с которой пользователь совершил действие. Переведем это число в текст:

# определение платформы по ее коду
if platform == 1:
    platform = 'официальную мобильную версию web-сайта VK'
elif platform == 2:
    platform = 'официальное приложение VK для iPhone'
elif platform == 3:
    platform = 'официальное приложение VK для iPad'
elif platform == 4:
    platform = 'официальное приложение VK для Android'
elif platform == 5:
    platform = 'официальное приложение VK для Windows Phone'
elif platform == 6:
    platform = 'официальное приложение VK для Windows'
elif platform == 7:
  platform = 'официальную web-версию VK'

Теперь можно вывести информацию на экран:

print(user['first_name'], user['last_name'], verb, 'онлайн через', platform, 'в', time.ctime(last_visit).split()[3])

Далее мы рассмотрим коды 61 и 62. Они сообщают о том, что кто-то набирает сообщение. Разница в том, что обновления с кодом 61 оповещают о наборе текста в личных сообщениях, а 62 — в беседах. Обновления с кодом 62 выглядят так: [62, 000000000, 000]. Второй элемент массива — id пользователя, набирающего сообщения, а третий — идентификатор беседы. Обновления с кодом 61 имеют следующий вид: [61, 000000000, 1]. Здесь значение имеет лишь второй элемент — id пользователя, набирающего сообщение. Напишем обработчик этих событий:

elif action_code == 61:
    user = requests.get('https://api.vk.com/method/users.get', params={'user_ids': element[1]}).json()['response'][0]  # получение имени и фамилии пользователя
    print(user['first_name'], user['last_name'], 'набирает сообщение')
elif action_code == 62:
    user = requests.get('https://api.vk.com/method/users.get', params={'user_ids': element[1]}).json()['response'][0]  # получение имени и фамилии пользователя, набирающего сообщение
    chat = requests.get('https://api.vk.com/method/messages.getChat', params={'chat_id': element[2], 'access_token': token}).json()['response']['title']  # получение названия беседы
    print(user['first_name'], user['last_name'], 'набирает сообщение в беседе "{}"'.format(chat))

В следующей теме мы напишем обработчик входящих сообщений и рассмотрим флаги сообщений.

Обработка сообщений и флаги

Наиболее интересные обновления, как мне кажется, — добавление сообщений. Эти обновления имеют код 4 и возвращают информацию о входящих и исходящих сообщений. Вот пример обновления с кодом 4: [4, $ts, $flag, $id, $unixtime, $text, {'title': ' ... '}]. Здесь $ts — номер пришедшего события, $flag — флаги сообщения, $id — id собеседника или 2000000000 + id беседы (в случае с коллективными диалогами), $unixtime — время добавления сообщения в Unix time, а последний элемент — словарь, содержащий информацию о вложениях, отправителе и изменениях в настройках беседы. Для начала разберемся с тем, где было добавлено сообщение: в личной переписке или беседе. Если сообщение было отправлено в беседе, то, как я уже написал, в поле $id будет указано число, получившиеся при сложении 2000000000 и chat_id (идентификатор беседы). Если сообщение было добавлено в личной переписке, в поле $id будет значиться id собеседника, которое всегда меньше, чем 2000000000 + chat_id любой беседы. Из этого можно сделать вывод, что, если $id — 2000000000 > 0, то сообщение было отправлено в беседе, если меньше, то в личной переписке. Если сообщение было отправлено в беседе, то id пользователя, написавшего сообщение, будет указано в словаре под ключем ‘from’. Напишем обработчик сообщений:

elif action_code == 4:
    if element[3] - 2000000000 > 0:  # проверяем, было ли отправлено сообщение в беседе
        user_id = element[6]['from']  # id отправителя
        chat_id = element[3] - 2000000000  # id беседы
        chat = requests.get('https://api.vk.com/method/messages.getChat', params={'chat_id': chat_id, 'access_token': token}).json()['response']['title']  # получение названия беседы
        user = requests.get('https://api.vk.com/method/users.get', params={'user_ids': user_id, 'name_case': 'gen'}).json()['response'][0]  # получение имени и фамилии пользователя, отправившего сообщение
        time_ = element[4]  # время отправления сообщения
        text = element[5]  # текст сообщения
        if text:  # проверяем, что сообщение содержит текст
            print(time.ctime(time_).split()[3] + ':', 'Сообщение от', user['first_name'], user['last_name'], 'в беседе "{}"'.format(chat) + ':', text)
    else:
        user_id = element[3]  # id собеседника
        user = requests.get('https://api.vk.com/method/users.get', params={'user_ids': user_id, 'name_case': 'gen'}).json()['response'][0]  # получение имени и фамилии пользователя, отправившего сообщение
        time_ = element[4]  # время отправления сообщения
        text = element[5]  # текст сообщения
        if text:  # проверяем, что сообщение содержит текст
            print(time.ctime(time_).split()[3] + ':', 'Сообщение от', user['first_name'], user['last_name'] + ':', text)

Однако, у данной программы довольно много недостатков. Вот некоторые из них:

  • Неумение отличить исходящие сообщения от входящих;
  • игнорирования медиа-вложений;
  • игнорирование сообщений, сигнализирующих об изменении настроек беседы;
  • неумение обработать специальные сиволы в сообщениях

Две из четырех проблем можно решить, используя флаги сообщений, которые хранятся в массиве обновления под индексом 2. Этот элемент массива — число, получившиеся в результате сложения некоторых параметров, приведенных ниже (из официальной документации):

+1: сообщение не прочитано
+2: исходящее сообщение
+4: на сообщение был создан ответ
+8: помеченное сообщение
+16: сообщение отправлено через чат
+32: сообщение отправлено другом. Не применяется для сообщений из групповых бесед
+64: сообщение помечено как «Спам»
+128: сообщение удалено (в корзине)
+256: сообщение проверено пользователем на спам
+512: сообщение содержит медиаконтент
+65536: приветственное сообщение от сообщества. Диалог с таким сообщением не нужно поднимать в списке (отображать его только при открытии диалога напрямую). Флаг недоступен для версий <2.

Теперь нужно научить программу отличать исходящие сообщения от входящих. Чтобы проверить, есть ли среди слагаемых флага сообщения нужное нам число, мы будем использовать побитовое И. Создадим массив, в котором будут храниться слагаемые флага:

summands = []  # массив, где мы будем хранить слагаемые
flag = element[2]  # флаг сообщения
for number in [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 65536]:  # проходим циклом по возможным слагаемым
    if flag & number:  # проверяем, является ли число слагаемым с помощью побитового И
        summands.append(number)  # если является, добавляем его в массив

Выводить на экран информацию об исходящих сообщениях, как мне кажется, бессмысленно, поэтому мы добавим строку

if 2 not in summands:

Сейчас часть программы, работающая с сообщениями, выглядит так:

elif action_code == 4:
    summands = []  # массив, где мы будем хранить слагаемые
    flag = element[2]  # флаг сообщения
    for number in [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 65536]:  # проходим циклом по возможным слагаемым
        if flag & number:  # проверяем, является ли число слагаемым с помощью побитового И
            summands.append(number)  # если является, добавляем его в массив
    if 2 not in summands:
        if element[3] - 2000000000 > 0:  # проверяем, было ли отправлено сообщение в беседе
            user_id = element[6]['from']  # id отправителя
            chat_id = element[3] - 2000000000  # id беседы
            chat = requests.get('https://api.vk.com/method/messages.getChat', params={'chat_id': chat_id, 'access_token': token}).json()['response']['title']  # получение названия беседы
            user = requests.get('https://api.vk.com/method/users.get', params={'user_ids': user_id, 'name_case': 'gen'}).json()['response'][0]  # получение имени и фамилии пользователя, отправившего сообщение
            time_ = element[4]  # время отправления сообщения
            text = element[5]  # текст сообщения
            if text:  # проверяем, что сообщение содержит текст
                print(time.ctime(time_).split()[3] + ':', 'Сообщение от', user['first_name'], user['last_name'], 'в беседе "{}"'.format(chat) + ':', text)
        else:
            user_id = element[3]  # id собеседника
            user = requests.get('https://api.vk.com/method/users.get', params={'user_ids': user_id, 'name_case': 'gen'}).json()['response'][0]  # получение имени и фамилии пользователя, отправившего сообщение
            time_ = element[4]  # время отправления сообщения
            text = element[5]  # текст сообщения
            if text:  # проверяем, что сообщение содержит текст
                print(time.ctime(time_).split()[3] + ':', 'Сообщение от', user['first_name'], user['last_name'] + ':', text)

Теперь научимся работать с медиа-вложениями. Вот пример обновления, говорящего о том, что пришло сообщение с прикрепленными фотографией, песней, видео, документом и гео-позицией: [[4, $ts, $flag, $id, $unixtime, $text, {'attach1_type': 'photo', 'attach1': '$photoId', 'attach2_type': 'video', 'attach2': '$videoId', 'attach3_type': 'audio', 'attach3': '$audioId', 'attach4_type': 'doc', 'attach4': '$docId', 'geo': '2_SVK-le', 'geo_provider': '4', 'title': ' ... '}]. Чтобы получить доступ к этим вложениям, нужно будет обращаться к API для получения ссылки на файл. Методы для выполнения этого действия (photos.getById и docs.getById) нашлись лишь для фотографий и документов (аудиосообщения приходят как документы, поэтому ссылку для их прослушивания получить удастся). Для музыки с недавних пор недоступен ни один метод из-за авторских прав, а для видео нужный метод попросту отсутствует. Для дальнейшей работы с вложениями напишем код, который создаст два массива (для фото и для документов) со ссылками на просмотр вложений.

if 512 in summands:  # проверка, есть ли медиа-вложения
    index = 1
    photos = []  # массив для хранения id фотографий
    docs = []  # массив для хранения id документов
    media_type = 'attach1_type'
    while media_type in element[6].keys():  # проверка, существует ли медиа-вложение с таким индексом
        media_type = element[6]['attach{}_type'.format(index)]  # если существует, сохраняем его тип
        if media_type == 'photo':  # является ли вложение фотографией
            photos.append(element[6]['attach{}'.format(index)])  # добавляем id фотографии в массив
        elif media_type == 'doc':  # является ли вложение документом
            docs.append(element[6]['attach{}'.format(index)])  # добавляем id документа в массив
        index += 1  # увеличиваем индекс
        media_type = 'attach{}_type'.format(index)
    change = lambda ids, type_: requests.get('https://api.vk.com/method/{}.getById'.format(type_), params={type_: ids, 'access_token': token}).json()  # функция, возвращающаяся ссылки на объекты
    if photos:  # проверка, были ли во вложениях фотографии
        photos = change(', '.join(photos), 'photos')  # если были, то перезаписываем переменную photos на словарь
        if 'response' in photos.keys():
            photos = [attachment['src_xbig'] for attachment in photos['response']]  # перезаписываем на ссылки
            print('сообщение содержит следующие фотографии:', ', '.join(photos))
        else:
            pass  # скорее всего, возникла ошибка доступа
    if docs:  # проверка, были ли во вложениях документы
        docs = change(', '.join(docs), 'docs')  # если были, то перезаписываем переменную docs на словарь
        if 'response' in docs.keys():
            docs = [attachment['url'] for attachment in docs['response']]  # перезаписываем на ссылки
            print('сообщение содержит следующие документы:', ', '.join(docs))
        else:
            pass  # скорее всего, возникла ошибка доступа

Уберем строки

if text:

, чтобы при выводе данных было понятно, какие медиа-вложения к каким сообщениям относятся.

Теперь научим нашу программу реагировать на изменения в беседе: создание новой беседы, обновления фотографии, изменения названий, добавления и исключения людей. Информация о подобных событиях хранится в словаре, находящемся под индексом 6 в обновлениях с кодом 4. Ниже приведены примеры событий:

  • Изменение названия беседы: [4, $ts, $flag, $chat_id, $unixtime, '', {'source_act': 'chat_title_update', 'source_text': 'Новое название', 'source_old_text': 'Старое название', 'from': '$id'}]
  • Обновление фотографии беседы: [4, $ts, $flag, $chat_id, $unixtime, '', {'attach1_type': 'photo', 'attach1': '247178624_456242629', 'source_act': 'chat_photo_update', 'from': '247178624'}]
  • Добавление пользователя в беседу: [4, $ts, $flag, $chat_id, $unixtime, '', {'source_act': 'chat_invite_user', 'source_mid': '$added_user_id', 'from': '$adder_id'}]
  • Исключение пользователя из беседы (выход пользователя из беседы): [4, $ts, $flag, $chat_id, $unixtime, '', {'source_act': 'chat_kick_user', 'source_mid': '&removed_user_id', 'from': '&remover_id'}]
  • Создание новой беседы: [4, $ts, $flag, $chat_id, $unixtime, '', {'source_act': 'chat_create', 'source_text': 'Название', 'from': '$creator_id'}]

Вот программа, которая обрабатывает изменения такого вида:

elif action_code == 4:
    if 'source_act' not in element[6].keys():
        # <код, обрабатывающий сообщения>
    else:
        source_act = element[6]
        if source_act['source_act'] == 'chat_title_update':  # было ли обновление вызвано изменением названия беседы
            changer_id = source_act['from']  # id человека, изменившего названия
            source_text = source_act['source_text']  # новое название беседы
            source_old_text = source_act['source_old_text']  # старое название беседы
            changer = requests.get('https://api.vk.com/method/users.get', params={'user_ids': changer_id, 'fields': 'sex'}).json()['response'][0]  # получение имени и фамилии пользователя, изменившего название
            if changer['sex']:
                verb = 'изменила'
            else:
                verb = 'изменил'
            print(changer['first_name'], changer['last_name'], verb, 'название беседы с "{}" на "{}"'.format(source_old_text, source_text))
        elif source_act['source_act'] == 'chat_photo_update':
            chat_id = element[3] - 2000000000  # id беседы
            chat = requests.get('https://api.vk.com/method/messages.getChat', params={'chat_id': chat_id, 'access_token': token}).json()['response']['title']  # получение названия беседы
            user_id = source_act['from']  # id пользователя, обновившего фото
            photo_id = source_act['attach1']  # id фотографии
            photo = requests.get('https://api.vk.com/method/photos.getById', params={'photos': photo_id, 'access_token': token}).json()  # ссылка на фотографию
            user = requests.get('https://api.vk.com/method/users.get', params={'user_ids': user_id, 'fields': 'sex'}).json()['response'][0]  # имя и фамилия пользователя, обновившего фото
            if 'error' not in photo.keys():  # не возникло ли ошибок при получении ссылки
                if user['sex']:
                    verb = 'обновил'
                else:
                    verb = 'обновила'
                print(user['first_name'], user['last_name'], verb, 'фотографию беседы "{}" на'.format(chat), photo['response'][0]['src_xbig'])
            else:
                pass  # вероятнее всего, отсутствуют права для выполнения запроса
        elif source_act['source_act'] == 'chat_invite_user':
            chat_id = element[3] - 2000000000  # id беседы
            chat = requests.get('https://api.vk.com/method/messages.getChat', params={'chat_id': chat_id, 'access_token': token}).json()['response']['title']  # получение названия беседы
            invited_id = source_act['source_mid']  # id приглашенного
            inviter_id = source_act['from']  # id пригласившего
            if invited_id == inviter_id:  # вернулся ли пользователь в беседу или был добавлен кем-то из участников
                user = requests.get('https://api.vk.com/method/users.get', params={'user_ids': inviter_id, 'fields': 'sex'}).json()['response'][0]  # имя и фамилия вернувшегося
                if user['sex']:
                    verb = 'вернулась'
                else:
                    verb = 'вернулся'
                print(user['first_name'], user['last_name'], verb, 'в беседу "{}"'.format(chat))
            else:
                inviter_user = requests.get('https://api.vk.com/method/users.get', params={'user_ids': inviter_id, 'fields': 'sex'}).json()['response'][0]  # имя и фамилия добавившего
                invited_user = requests.get('https://api.vk.com/method/users.get', params={'user_ids': invited_id, 'name_case': 'acc'}).json()['response'][0]  # имя и фамилия добавленного
                if inviter_user['sex']:
                    verb = 'добавила'
                else:
                    verb = 'добавил'
                print(inviter_user['first_name'], inviter_user['last_name'], verb, 'в беседу "{}"'.format(chat), invited_user['first_name'], invited_user['last_name'])
        elif source_act['source_act'] == 'chat_kick_user':
            chat_id = element[3] - 2000000000  # id беседы
            chat = requests.get('https://api.vk.com/method/messages.getChat', params={'chat_id': chat_id, 'access_token': token}).json()['response']['title']  # получение названия беседы
            removed_id = source_act['source_mid']  # id исключенного
            remover_id = source_act['from']  # id исключившего
            if removed_id == remover_id:  # вышел ли пользователь сам или был исключен
                user = requests.get('https://api.vk.com/method/users.get', params={'user_ids': remover_id, 'fields': 'sex'}).json()['response'][0]  # имя и фамилия вышедшего
                if user['sex']:
                    verb = 'вышла'
                else:
                    verb = 'вышел'
                print(user['first_name'], user['last_name'], verb, 'из беседы "{}"'.format(chat))
            else:
                remover_user = requests.get('https://api.vk.com/method/users.get', params={'user_ids': remover_id, 'fields': 'sex'}).json()['response'][0]  # имя и фамилия исключившего
                removed_user = requests.get('https://api.vk.com/method/users.get', params={'user_ids': removed_id, 'name_case': 'acc'}).json()['response'][0]  # имя и фамилия исключенного
                if remover_user['sex']:
                    verb = 'исключила'
                else:
                     verb = 'исключил'
                print(remover_user['first_name'], remover_user['last_name'], verb, 'из беседы "{}"'.format(chat), removed_user['first_name'], removed_user['last_name'])
        elif source_act['source_act'] == 'chat_create':
            chat = source_act['source_text']  # название беседы
            creator_id = source_act['from']  # id создателя
            creator = requests.get('https://api.vk.com/method/users.get', params={'user_ids': creator_id, 'fields': 'sex'}).json()['response'][0]  # имя, фамилия и пол создателя
            if creator['sex']:
                verb = 'создала'
            else:
                verb = 'создал'
            print(creator['first_name'], creator['last_name'], verb, 'беседу "{}"'.format(chat))

Наводим красивости. Заключительная часть

В этой части мы рассмотрим лишь две детали: ошибку при запросе к Long Poll серверу и отображение спец. символов в сообщениях.

Об ошибке: если запустить программу в том виде, в котором она есть сейчас, через некоторое время возникнет ошибка KeyError на строке 8: response = requests.get('https://{server}?act=a_check&key={key}&ts={ts}&wait=20&mode=2&version=2'.format(server=data['server'], key=data['key'], ts=data['ts'])).json()['response'] # отправление запроса на Long Poll сервер со временем ожидания 20 и опциями ответа 2. Дело в том, что на наш запрос сервер вернул ошибку «error 2», что означает, что используемый параметр &key устарел и нужно получить новый. Для этого мы несколько изменим существующий код на:

response = requests.get('https://{server}?act=a_check&key={key}&ts={ts}&wait=20&mode=2&version=2'.format(server=data['server'], key=data['key'], ts=data['ts'])).json()  # отправление запроса на Long Poll сервер со временем ожидания 20 и опциями ответа 2
try:
    updates = response['updates']
except KeyError:  # если в этом месте возбуждается исключение KeyError, значит параметр key устарел, и нужно получить новый
    data = requests.get('https://api.vk.com/method/messages.getLongPollServer', params={'access_token': token}).json()['response']  # получение ответа от сервера
     continue  # переходим на следующую итерацию цикла, чтобы сделать повторный запрос

Теперь проблема решена! Остается последнее: спец. символы в сообщениях. Дело в том, что некоторые символы ВК возвращает не в привычном для нас виде. Так, например, если в сообщении есть амперсант, он будет заменен на &_amp (нижнее подчеркивание нужно, чтобы Хабр не заменил эту надпись на амерсант). Подобных символов много и всех их нужно вывести правильно. Для этого сохраним подобные символы и их коды в словарь, а затем заменим коды в сообщении на символы с помощью функции sub библиотеки re (не забудьте ее импортировать!).

import re
# <...>
symbols = {'<br>': 'n', '&_amp;': '&', '&_quot;': '"', '&_lt;': '<', '&_gt;': '>', '&_tilde;': '~', '&_circ;': '?', '&_ndash;': '–', '&_mdash;': '—', '&_euro;': '€', '&_permil;': '‰'}  # из каждого ключа уберите нижнее подчеркивание
for code, value in symbols.items():
    text = re.sub(code, value, text)

Разумеется, таких символов куда больше, чем я добавил в словарь, но переписать их всех очень сложно, тем более, что некоторые из них невыводимые. На этом создание нашей программы кончается, финальная ее версия выглядит так:

import re import time import requests token = '' # здесь вы должны написать свой access_token data = requests.get('https://api.vk.com/method/messages.getLongPollServer', params={'access_token': token}).json()['response'] # получение ответа от сервера while True: response = requests.get('https://{server}?act=a_check&key={key}&ts={ts}&wait=20&mode=2&version=2'.format(server=data['server'], key=data['key'], ts=data['ts'])).json() # отправление запроса на Long Poll сервер со временем ожидания 20 и опциями ответа 2 try: updates = response['updates'] except KeyError: # если в этом месте возбуждается исключение KeyError, значит параметр key устарел, и нужно получить новый data = requests.get('https://api.vk.com/method/messages.getLongPollServer', params={'access_token': token}).json()['response'] # получение ответа от сервера continue # переходим на следующую итерацию цикла, чтобы сделать повторный запрос if updates: # проверка, были ли обновления for element in updates: # проход по всем обновлениям в ответе action_code = element[0] if action_code == 80: # проверка кода события print('количество непрочитанных сообщений стало равно', element[1]) # вывод elif action_code == 9: user_id = element[1] * -1 # id пользователя, ставшего оффлайн user = requests.get('https://api.vk.com/method/users.get', params={'user_ids': user_id, 'fields': 'sex'}).json()['response'][0] # имя и фамилия пользователя с id = user_id timeout = bool(element[2]) # был ли поставлен статус оффлайн по истечении тайм-аута last_visit = element[3] # дата последнего действия пользователя на сайте if user['sex'] == 1: verb = ['стала', 'вышла'] else: verb = ['стал', 'вышел'] if timeout: print(user['first_name'], user['last_name'], verb[0], 'оффлайн по истечении тайм-аута. Время последнего действия на сайте:', time.ctime(last_visit).split()[3]) else: print(user['first_name'], user['last_name'], verb[1], 'из ВКонтакте. Время последнего действия на сайте:', time.ctime(last_visit).split()[3]) elif action_code == 8: user_id = element[1] * -1 # id пользователя, ставшего онлайн user = requests.get('https://api.vk.com/method/users.get', params={'user_ids': user_id, 'fields': 'sex'}).json()['response'][0] # имя и фамилия пользователя с id = user_id platform = element[2] # код платформы пользователя last_visit = element[3] # время последнего визита в Unix time if user['sex'] == 1: verb = 'стала' else: verb = 'стал' # определение платформы по ее коду if platform == 1: platform = 'официальную мобильную версию web-сайта VK' elif platform == 2: platform = 'официальное приложение VK для iPhone' elif platform == 3: platform = 'официальное приложение VK для iPad' elif platform == 4: platform = 'официальное приложение VK для Android' elif platform == 5: platform = 'официальное приложение VK для Windows Phone' elif platform == 6: platform = 'официальное приложение VK для Windows' elif platform == 7: platform = 'официальную web-версию VK' print(user['first_name'], user['last_name'], verb, 'онлайн через', platform, 'в', time.ctime(last_visit).split()[3]) elif action_code == 61: user = requests.get('https://api.vk.com/method/users.get', params={'user_ids': element[1]}).json()['response'][0] # получение имени и фамилии пользователя print(user['first_name'], user['last_name'], 'набирает сообщение') elif action_code == 62: user = requests.get('https://api.vk.com/method/users.get', params={'user_ids': element[1]}).json()['response'][0] # получение имени и фамилии пользователя, набирающего сообщение chat = requests.get('https://api.vk.com/method/messages.getChat', params={'chat_id': element[2], 'access_token': token}).json()['response']['title'] # получение названия беседы print(user['first_name'], user['last_name'], 'набирает сообщение в беседе "{}"'.format(chat)) elif action_code == 4: if 'source_act' not in element[6].keys(): summands = [] # массив, где мы будем хранить слагаемые flag = element[2] # флаг сообщения for number in [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 65536]: # проходим циклом по возможным слагаемым if flag & number: # проверяем, является ли число слагаемым с помощью побитового И summands.append(number) # если является, добавляем его в массив if 2 not in summands: if element[3] - 2000000000 > 0: # проверяем, было ли отправлено сообщение в беседе user_id = element[6]['from'] # id отправителя chat_id = element[3] - 2000000000 # id беседы chat = requests.get('https://api.vk.com/method/messages.getChat', params={'chat_id': chat_id, 'access_token': token}).json()['response']['title'] # получение названия беседы user = requests.get('https://api.vk.com/method/users.get', params={'user_ids': user_id, 'name_case': 'gen'}).json()['response'][0] # получение имени и фамилии пользователя, отправившего сообщение time_ = element[4] # время отправления сообщения text = element[5] # текст сообщения symbols = {'<br>': 'n', '&_amp;': '&', '&_quot;': '"', '&_lt;': '<', '&_gt;': '>', '&_tilde;': '~', '&_circ;': '?', '&_ndash;': '–', '&_mdash;': '—', '&_euro;': '€', '&_permil;': '‰'} # из каждого ключа уберите нижнее подчеркивание for code, value in symbols.items(): text = re.sub(code, value, text) print(time.ctime(time_).split()[3] + ':', 'Сообщение от', user['first_name'], user['last_name'], 'в беседе "{}"'.format(chat) + ':', text) else: user_id = element[3] # id собеседника user = requests.get('https://api.vk.com/method/users.get', params={'user_ids': user_id, 'name_case': 'gen'}).json()['response'][0] # получение имени и фамилии пользователя, отправившего сообщение time_ = element[4] # время отправления сообщения text = element[5] # текст сообщения symbols = {'<br>': 'n', '&_amp;': '&', '&_quot;': '"', '&_lt;': '<', '&_gt;': '>', '&_tilde;': '~', '&_circ;': '?', '&_ndash;': '–', '&_mdash;': '—', '&_euro;': '€', '&_permil;': '‰'} # из каждого ключа уберите нижнее подчеркивание for code, value in symbols.items(): text = re.sub(code, value, text) print(time.ctime(time_).split()[3] + ':', 'Сообщение от', user['first_name'], user['last_name'] + ':', text) if 512 in summands: # проверка, были ли медиа-вложения index = 1 photos = [] # массив для хранения id фотографий docs = [] # массив для хранения id документов media_type = 'attach1_type' while media_type in element[6].keys(): # проверка, существует ли медиа-вложение с таким индексом media_type = element[6]['attach{}_type'.format(index)] # если существует, сохраняем его тип if media_type == 'photo': # является ли вложение фотографией photos.append(element[6]['attach{}'.format(index)]) # добавляем id фотографии в массив elif media_type == 'doc': # является ли вложение документом docs.append(element[6]['attach{}'.format(index)]) # добавляем id документа в массив index += 1 # увеличиваем индекс media_type = 'attach{}_type'.format(index) change = lambda ids, type_: requests.get('https://api.vk.com/method/{}.getById'.format(type_), params={type_: ids, 'access_token': token}).json() # функция, возвращающаяся ссылки на объекты if photos: # проверка, были ли во вложениях фотографии photos = change(', '.join(photos), 'photos') # если были, то перезаписываем переменную photos на словарь if 'response' in photos.keys(): photos = [attachment['src_xbig'] for attachment in photos['response']] # перезаписываем на ссылки print('сообщение содержит следующие фотографии:', ', '.join(photos)) else: pass # скорее всего, возникла ошибка доступа if docs: # проверка, были ли во вложениях документы docs = change(', '.join(docs), 'docs') # если были, то перезаписываем переменную docs на словарь if 'response' in docs.keys(): docs = [attachment['url'] for attachment in docs['response']] # перезаписываем на ссылки print('сообщение содержит следующие документы:', ', '.join(docs)) else: pass # скорее всего, возникла ошибка доступа else: source_act = element[6] if source_act['source_act'] == 'chat_title_update': # было ли обновление вызвано изменением названия беседы changer_id = source_act['from'] # id человека, изменившего названия source_text = source_act['source_text'] # новое название беседы source_old_text = source_act['source_old_text'] # старое название беседы changer = requests.get('https://api.vk.com/method/users.get', params={'user_ids': changer_id, 'fields': 'sex'}).json()['response'][0] # получение имени и фамилии пользователя, изменившего название if changer['sex']: verb = 'изменила' else: verb = 'изменил' print(changer['first_name'], changer['last_name'], verb, 'название беседы с "{}" на "{}"'.format(source_old_text, source_text)) elif source_act['source_act'] == 'chat_photo_update': chat_id = element[3] - 2000000000 # id беседы chat = requests.get('https://api.vk.com/method/messages.getChat', params={'chat_id': chat_id, 'access_token': token}).json()['response']['title'] # получение названия беседы user_id = source_act['from'] # id пользователя, обновившего фото photo_id = source_act['attach1'] # id фотографии photo = requests.get('https://api.vk.com/method/photos.getById', params={'photos': photo_id, 'access_token': token}).json() # ссылка на фотографию user = requests.get('https://api.vk.com/method/users.get', params={'user_ids': user_id, 'fields': 'sex'}).json()['response'][0] # имя и фамилия пользователя, обновившего фото if 'error' not in photo.keys(): # не возникло ли ошибок при получении ссылки if user['sex']: verb = 'обновил' else: verb = 'обновила' print(user['first_name'], user['last_name'], verb, 'фотографию беседы "{}" на'.format(chat), photo['response'][0]['src_xbig']) else: pass # вероятнее всего, отсутствуют права для выполнения запроса elif source_act['source_act'] == 'chat_invite_user': chat_id = element[3] - 2000000000 # id беседы chat = requests.get('https://api.vk.com/method/messages.getChat', params={'chat_id': chat_id, 'access_token': token}).json()['response']['title'] # получение названия беседы invited_id = source_act['source_mid'] # id приглашенного inviter_id = source_act['from'] # id пригласившего if invited_id == inviter_id: # вернулся ли пользователь в беседу или был добавлен кем-то из участников user = requests.get('https://api.vk.com/method/users.get', params={'user_ids': inviter_id, 'fields': 'sex'}).json()['response'][0] # имя и фамилия вернувшегося if user['sex']: verb = 'вернулась' else: verb = 'вернулся' print(user['first_name'], user['last_name'], verb, 'в беседу "{}"'.format(chat)) else: inviter_user = requests.get('https://api.vk.com/method/users.get', params={'user_ids': inviter_id, 'fields': 'sex'}).json()['response'][0] # имя и фамилия добавившего invited_user = requests.get('https://api.vk.com/method/users.get', params={'user_ids': invited_id, 'name_case': 'acc'}).json()['response'][0] # имя и фамилия добавленного if inviter_user['sex']: verb = 'добавила' else: verb = 'добавил' print(inviter_user['first_name'], inviter_user['last_name'], verb, 'в беседу "{}"'.format(chat), invited_user['first_name'], invited_user['last_name']) elif source_act['source_act'] == 'chat_kick_user': chat_id = element[3] - 2000000000 # id беседы chat = requests.get('https://api.vk.com/method/messages.getChat', params={'chat_id': chat_id, 'access_token': token}).json()['response']['title'] # получение названия беседы removed_id = source_act['source_mid'] # id исключенного remover_id = source_act['from'] # id исключившего if removed_id == remover_id: # вышел ли пользователь сам или был исключен user = requests.get('https://api.vk.com/method/users.get', params={'user_ids': remover_id, 'fields': 'sex'}).json()['response'][0] # имя и фамилия вышедшего if user['sex']: verb = 'вышла' else: verb = 'вышел' print(user['first_name'], user['last_name'], verb, 'из беседы "{}"'.format(chat)) else: remover_user = requests.get('https://api.vk.com/method/users.get', params={'user_ids': remover_id, 'fields': 'sex'}).json()['response'][0] # имя и фамилия исключившего removed_user = requests.get('https://api.vk.com/method/users.get', params={'user_ids': removed_id, 'name_case': 'acc'}).json()['response'][0] # имя и фамилия исключенного if remover_user['sex']: verb = 'исключила' else: verb = 'исключил' print(remover_user['first_name'], remover_user['last_name'], verb, 'из беседы "{}"'.format(chat), removed_user['first_name'], removed_user['last_name']) elif source_act['source_act'] == 'chat_create': chat = source_act['source_text'] # название беседы creator_id = source_act['from'] # id создателя creator = requests.get('https://api.vk.com/method/users.get', params={'user_ids': creator_id, 'fields': 'sex'}).json()['response'][0] # имя, фамилия и пол создателя if creator['sex']: verb = 'создала' else: verb = 'создал' print(creator['first_name'], creator['last_name'], verb, 'беседу "{}"'.format(chat)) data['ts'] = response['ts'] # обновление номера последнего обновления 

В этом руководстве я рассмотрел не все возможности Long Poll-a, такие интересные вещи, как замена флагов сообщений или отметки «прочитано» и «непрочитанно» остались незатронутыми, но вы всегда можете дописать программу.
На этом у меня все, надеюсь, вы узнали для себя что-то новое. До скорых встреч!

Источники:

  • vk.com/dev
  • stackoverflow.com
  • www.htmlhelp.com

Recommend Projects

  • React photo
    React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo
    Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo
    Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo
    TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo
    Django

    The Web framework for perfectionists with deadlines.

  • Laravel photo
    Laravel

    A PHP framework for web artisans

  • D3 photo
    D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Visualization

    Some thing interesting about visualization, use data art

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo
    Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo
    Microsoft

    Open source projects and samples from Microsoft.

  • Google photo
    Google

    Google ❤️ Open Source for everyone.

  • Alibaba photo
    Alibaba

    Alibaba Open Source for everyone

  • D3 photo
    D3

    Data-Driven Documents codes.

  • Tencent photo
    Tencent

    China tencent open source team.

Jobs

Jooble

Recommend Projects

  • React photo
    React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo
    Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo
    Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo
    TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo
    Django

    The Web framework for perfectionists with deadlines.

  • Laravel photo
    Laravel

    A PHP framework for web artisans

  • D3 photo
    D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Visualization

    Some thing interesting about visualization, use data art

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo
    Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo
    Microsoft

    Open source projects and samples from Microsoft.

  • Google photo
    Google

    Google ❤️ Open Source for everyone.

  • Alibaba photo
    Alibaba

    Alibaba Open Source for everyone

  • D3 photo
    D3

    Data-Driven Documents codes.

  • Tencent photo
    Tencent

    China tencent open source team.

User LongPoll API

План документации:

  1. Отказ от ответственности
  2. Подключение
  3. Возвращаемые ошибки
  4. Получение истории событий
  5. Структура сообщения
  6. Описание событий
    • Событие 2. Установка флагов сообщения
    • Событие 3. Сброс флагов сообщения
    • Событие 4. Новое сообщение
    • Событие 5. Редактирование сообщения
    • Событие 6. Прочтение входящих сообщений
    • Событие 7. Прочтение исходящих сообщений
    • Событие 8. Друг появился в сети
    • Событие 9. Друг вышел из сети
    • Событие 10. Сброс флагов беседы
    • Событие 12. Установка флагов беседы
    • Событие 13. Удаление всех сообщений в диалоге
    • Событие 18. Обновление сообщения
    • Событие 19. Сброс кеша сообщения
    • Событие 20. Закрепление и открепление беседы (изменился majorId)
    • Событие 21. Изменился minorId
    • Событие 51. Изменение данных чата (устарело)
    • Событие 52. Изменение данных чата
    • Событие 63. Статус набора сообщения
    • Событие 64. Статус записи голосового сообщения
    • Событие 65. Статус загрузки фотографии
    • Событие 66. Статус загрузки видеозаписи
    • Событие 67. Статус загрузки файла
    • Событие 80. Изменение количества непрочитанных диалогов
    • Событие 81. Изменение состояния невидимки друга
    • Событие 90. Добавление или удаление из друзей
    • Событие 114. Изменение настроек пуш-уведомлений в беседе
    • Событие 115. Звонок
    • Событие 119. Ответ callback-кнопки
    • Событие 501. Создание папки
    • Событие 502. Удаление папки
    • Событие 503. Переименование папки
    • Событие 504. Добавление беседы в папку
    • Событие 505. Удаление беседы из папки
    • Событие 506. Изменился порядок папок
    • Событие 507. Изменение количества непрочитанных диалогов в папках
  7. Дополнительная информация
    • Флаги сообщений
    • Сервисные сообщения
    • Клавиатура для ботов
    • Вложения
    • Права доступа в беседах
    • Зачем нужен random_id

Документация написана для 10 версии LongPoll.

Отказ от ответственности

Автор не несет ответственности за точность, полноту или качество предоставленной информации. Используйте последующую информацию на свой страх и риск.

Правила пользования сайтом, пункт 6.7:

6.7. Создаваемые Пользователями приложения API должны использовать только опубликованные на Сайте методы API, а также ID, защищенный ключ и сервисный ключ доступа, указанные в настройках данных приложений. Использование других методов API, а также ID, защищенного ключа и сервисного ключа доступа приложений API третьих лиц, в т.ч. приложения API Администрации Сайта, строго запрещено. Пользователь обязуется регулярно проверять перечень разрешённых методов и незамедлительно вносить корректировки в функциональность своих приложений API в соответствии с изменениями перечня. За нарушение настоящего пункта Пользователь несет предусмотренную применимым законодательством, настоящими Правилами и иными документами Администрации Сайта ответственность. Администрация Сайта при этом оставляет за собой право на защиту собственных прав и законных интересов.

Подключение

Long Polling — это технология, используемая для получения событий в реальном времени, которая работает так:

  1. Запрос отправляется на сервер
  2. Сервер ждет нового события или таймаута
  3. Сервер возвращает список только что пришедших событий (или пустой список, если вернул ответ по таймауту)

Подробнее про данную технологию можно прочитать здесь.

Ссылка для запроса составляется следующим образом:

https://server?act=a_check&key=key&ts=ts&wait=wait&mode=mode&version=version

  • server, key и ts получаются методом messages.getLongPollServer
  • version — Версия LongPoll
  • wait — Время ожидания нового события в секундах, максимум 90
  • mode — Дополнительные опции ответа:
    • 2 — Возвращать вложения и дополнительные данные сообщения (additional и attachments в структуре сообщения)
    • 8 — Возвращать данные в 114, 115 и 119 событиях
    • 32 — Возвращать pts
    • 64 — Возвращать данные о платформе в событии онлайна друга
    • 128 — Возвращать random_id
    • 512 — Возвращать бизнес-уведомления

В JS ссылку можно составить следующим образом:

function toUrlParams(object) { return new URLSearchParams(object).toString(); } // server, key и ts нужно получить заранее с помощью метода messages.getLongPollServer. const link = `https://${server}?` + toUrlParams({ act: 'a_check', key: key, ts: ts, wait: 10, mode: 2 | 8 | 32 | 64 | 128 | 512, version: 10 });

После выполнения запроса сервер вернет ответ следующего вида:

interface LongPollResult { // Приходит, если нет ошибок или при failed: 1 ts?: number // Приходит, если нет ошибок (необходим флаг 32) pts?: number // Приходит, если нет ошибок updates?: any[] failed?: 1 | 2 | 3 | 4 // Приходят только при failed: 4 min_version?: 0 max_version?: 15 }

После обработки ответа нужно повторить запрос, перед этим заменив ts и pts на новые из ответа.

Возвращаемые ошибки

Иногда вместо поля updates в ответе может прийти поле failed. Чаще всего приходит 2 ошибка, но могут прийти и другие:

failed Описание
1 Устарела история событий. Решается получением пропущенной истории событий и использованием ts из ответа LongPoll
2 Истекло время действия ключа. Решается получением нового key через метод messages.getLongPollServer
4 Передана неправильная версия. В ответе приходят поля min_version и max_version, в пределах которых следует указать версию

Получение истории событий

Для получения истории событий нам необходим pts, который можно получить указав need_pts: 1 при вызове метода messages.getLongPollServer и добавлением в mode флага 32 при выполнении запроса.

Для получения истории мы будем использовать метод messages.getLongPollHistory с указанием следующих параметров:

  • pts — Последний полученный pts из LongPoll
  • msgs_limit — Максимальное количество передаваемых сообщений. Минимум 200, рекомендую 500, максимум 1000
  • onlines1 если возвращать события 8 и 9 (онлайн и оффлайн друга), 0 если нет
  • lp_version — Версия LongPoll
  • fields — Поля пользователей и групп, которые могут прийти вместе с историей

Ответ выглядит следующим образом:

// Описание типов: // 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 }

Поле history идентично полю updates, которое приходит из LongPoll, за исключением удаления некоторых лишних событий (т.к. приходит просто измененный объект беседы или сообщения) и сокращения некоторых событий:

  • 3 — Сброс флагов сообщения
  • 4 — Новое сообщение
  • 5 — Редактирование сообщения
  • 18 — Обновление сообщения

Структура этих событий выглядит так:

type LongPollHistoryMessageEvent = [ type: 3 | 4 | 5 | 18, messageId: number, flags: number, peerId: number ];

Всю информацию о сообщениях и беседах можно взять из полей messages и conversations, где содержатся данные из API.

Если в ответе придет поле more, то после обработки всех событий нужно будет повторить запрос, указав в поле pts пришедший new_pts.

Структура сообщения

Подробнее про некоторые части структуры можно почитать здесь:

  • Флаги сообщений
  • Шаблоны
  • Клавиатура для ботов
  • Сервисные сообщения
  • Вложения
  • random_id

Структура описывает массив, ключи используются только для описания значений

type LongPollMessage = [ type: 3 | 4 | 5 | 18, messageId: number, flags: number, peerId: number, timestamp: number, text: string, // Объект приходит только при указании флага 2 при подключении к LongPoll additional: { // ' ... ' приходит только в лс // '' приходит при редактировании любого сообщения // string приходит при написании в лс через vk.com с указанием темы // Устаревшее поле, не следует использовать title?: ' ... ' | '' | string // Наличие emoji в сообщении emoji?: '1' // id автора сообщения. Приходит только в беседах from?: string // Наличие шаблона (для получения шаблона нужно получить сообщение из API) has_template?: '1' marked_users?: [ // Список упомянутых людей, @online, ответ на сообщение | [1, number[]] // @all | [1, 'all'] // Исчезающее сообщение в обычном чате | [2, 'all'] ] // Клавиатура для ботов (для беседы или сообщения) keyboard?: MessageKeyboard // Количество секунд до исчезновения сообщения в обычном чате expire_ttl?: string // Количество секунд до исчезновения сообщения в фантомном чате ttl?: number // Сообщение исчезло, приходит в 18 событии is_expired?: '1' // Приходит, если при отправке сообщения указать параметр payload. // Возвращает всегда JSON, который может содержать любую информацию. // Там может содержаться отладочная информация, которая может пригодиться разработчику. payload?: string // Сервисное сообщение // см. ссылку выше }, // Объект приходит только при указании флага 2 при подключении к LongPoll attachments: { // Есть пересланное сообщение или ответ на сообщение fwd?: '0_0' // Ответ на сообщение: '{"conversation_message_id":number}' // Приходит только в 4 и 18 событиях reply?: string // Количество вложений в поле attachments // Число в строке attachments_count?: string // JSON с массивом вложений // Приходит только для некоторых типов вложений attachments?: string // Описание вложений вида { attach1_type, attach1, ... } // Все значения здесь имеют тип строки (и могут быть объектом в строке по типу '{"id":1}') // см. ссылку выше }, // Возвращается, если в mode есть флаг 128 randomId: number, // id сообщения относительно беседы conversationMsgId: number, // 0 (не редактировалось) или timestamp (время редактирования) updateTimestamp: number ];

Стоит отметить, что в поле text переносы строк обозначаются как <br>, а символы ", &, < и > экранируются.

В очень редких случаях может прийти урезанный вариант сообщения

Он приходит, если сообщение на момент возвращения событий уже было удалено для всех.
Его следует игнорировать, потому что после него приходит событие удаления сообщения,
которое удалит сообщение с вашей стороны, если оно уже было сохранено

type LongPollMessageShort = [ type: 3 | 4 | 5 | 18, messageId: number, flags: number ];

Описание событий

Событие 2. Установка флагов сообщения

Возможные значения флагов сообщения:

  1. Пометка важным (8 important)
  2. Пометка как спам (64 spam)
  3. Удаление сообщения (128 deleted)
  4. Удаление для всех (128 deleted и 131072 deleted_all)
  5. Прослушивание голосового сообщения (4096 audio_listened)

Событие приходит как при прослушивании собеседником вашего голосового сообщения,
так и при прослушивании вами голосового сообщения собеседника.

Вручную прослушать голосовое сообщение собеседника можно с помощью метода messages.markAsListened с параметром message_id.
Метод вернет 1 при первом прослушивании голосового сообщения, а 0 при последующих или при попытке прослушать свое сообщение.

type Event2 = [ type: 2, messageId: number, flags: number, peerId: number ];

Событие 3. Сброс флагов сообщения

Возможные значения флагов сообщения:

  1. Прочитано сообщение (1 unread). Устаревший флаг
  2. Отмена пометки важным (8 important)
  3. Отмена пометки сообщения как спам (64 spam и 32768 cancel_spam)
  4. Восстановление удаленного сообщения (128 deleted)

В 3 и 4 случаях возвращается сообщение.

type Event3 = [ type: 3, messageId: number, flags: number, peerId: number ] | LongPollMessage;

Событие 4. Новое сообщение

Данное событие возвращает новое сообщение.

type Event4 = LongPollMessage;

Событие 5. Редактирование сообщения

Данное событие возвращает отредактированное сообщение.

type Event5 = LongPollMessage;

Событие 6. Прочтение входящих сообщений

Вы прочитали в диалоге peer_id сообщения до msg_id включительно.
count — количество непрочитанных сообщений в диалоге.

type Event6 = [ type: 6, peerId: number, messageId: number, count: number ];

Событие 7. Прочтение исходящих сообщений

Собеседник прочитал в диалоге peer_id сообщения до msg_id включительно.
count — количество ваших непрочитанных сообщений в диалоге.

type Event7 = [ type: 7, peerId: number, messageId: number, count: number ];

Событие 8. Друг появился в сети

Это событие больше не приходит в 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 ];

Событие 9. Друг вышел из сети

Это событие больше не приходит в 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 ];

Событие 10. Сброс флагов беседы

Все возможные флаги описаны в 12 событии

Вместе со сбросом 14 флага сбрасывается и 10, который мог вообще не быть в списке флагов.
Сделано это для обратной совместимости

type Event10 = [ type: 10, peerId: number, flags: number ];

Событие 12. Установка флагов беседы

Возможные флаги беседы:

1 << 4 (16) — Беседа замьючена

1 << 5 (32) — Звук в беседе выключен (сомнительный флаг)

1 << 8 (256) — Входящий запрос на переписку / вступление в беседу

1 << 9 (512) — Отклоненный запрос на переписку / вступление в беседу

1 << 10 (1024) — Наличие упоминания

1 << 11 (2048) — Не отображать беседу при поиске

1 << 12 (4096) — Внутренний флаг

1 << 13 (8192) — Беседа в статусе бизнес-уведомления

1 << 14 (16384) — Наличие маркированного сообщения: упоминание или исчезающее сообщение

1 << 16 (65536) — Фантомный чат

1 << 18 (262144) — Не присылать уведомлений о @all и @online

1 << 19 (524288) — Не присылать уведомлений о всех упоминаниях

1 << 20 (1048576) — Беседа помечена как непрочитанная

1 << 22 (4194304) — Беседа в статусе входящего запроса на переписку (наличие флага 8 или 9)

1 << 23 (8388608) — Беседа помещена в архив

1 << 24 (16777216) — Беседа, в которой идет звонок

1 << 26 (67108864) — Признак того, что это чат

  • при создании фантомного чата добавляется этот флаг и 1 << 16
  • при создании канала с чата снимается этот флаг
type Event12 = [ type: 12, peerId: number, flags: number ];

Событие 13. Удаление всех сообщений в диалоге

В беседе peerId были удалены все сообщения до messageId включительно.

type Event13 = [ type: 13, peerId: number, messageId: number ];

Событие 18. Обновление сообщения

Приходит при следующих событиях:

  1. Добавился сниппет (ссылка) — к вложениям добавляется link.
  2. Сообщение исчезло — удаляется текст и все вложения, добавляется ключ is_expired: true.
  3. Пришел перевод голосового сообщения.

Данное событие возвращает сообщение.

type Event18 = LongPollMessage;

Событие 19. Сброс кеша сообщения

Приходит в двух случаях:

  1. По какой-либо причине изменилось сообщение (без явного редактирования). Необходимо переполучить сообщение через API.
  2. Сообщение исчезло. В данном случае можно игнорировать это событие, если вы обработали исчезновение сообщения в 18 событии.

ВАЖНО: если вы собираетесь обрабатывать исчезновение сообщений, то я рекомендую игнорировать 19 событие и обрабатывать
только 18 событие. Однако сначала приходит 19 событие, а потом уже 18, поэтому нужно сначала дождаться, пока обработается
18 событие и там к сообщению добавится поле is_expired, и только затем здесь проверять это сообщение на наличие этого поля.

type Event19 = [ type: 19, messageId: number ];

Событие 20. Закрепление и открепление беседы (изменился majorId)

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 ];

Событие 21. Изменился minorId

Для 10 версии LongPoll событие приходит только в методе
messages.getLongPollHistory

В версиях LongPoll выше 10 событие приходит еще в некоторых сценариях, но это будет подробно расписано только
при обновлении документации на новую версию LongPoll

minorId означает id последнего сообщения в беседе, но с одним отличием. В фантомном чате последнее сообщение
может исчезнуть, и чтобы диалог после этого полностью не пропал из списка, minorId сохраняет в себе id
последнего даже исчезнувшего сообщения.

type Event21 = [ type: 21, peerId: number, minorId: number ];

Событие 51. Изменение данных чата (устарело)

Событие означает, что в беседе chatId изменились какие-то данные.
Более подробно все расписано в 52 событии.

type Event51 = [ type: 51, chatId: number ];

Событие 52. Изменение данных чата

Тип Описание Дополнительная информация
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 Изменено оформление чата

При изменении названия (1) и обновлении аватарки беседы (2) нужные данные можно взять из
сервисного сообщения в 4 событии.

type Event52 = [ type: 52, // 0-19, 22 updateType: number, peerId: number, extra: number ];

Событие 63. Статус набора сообщения

Означает, что в беседе peerId начали писать текст totalCount людей. Их id записаны в userIds.
В массиве userIds может появиться и ваш id, так что нужно фильтровать этот список.

type Event63 = [ type: 63, peerId: number, userIds: number[], totalCount: number, timestamp: number ];

Событие 64. Статус записи голосового сообщения

Вызывается при записи голосового сообщения.
Идентичен событию 63.

Событие 65. Статус загрузки фотографии

Вызывается при загрузке фотографии.
Идентичен событию 63.

Событие 66. Статус загрузки видеозаписи

Вызывается при загрузке видеозаписи.
Идентичен событию 63.

Событие 67. Статус загрузки файла

Вызывается при загрузке файла.
Идентичен событию 63.

Событие 80. Изменение количества непрочитанных диалогов

Все счетчики возвращаются без учета сообщений в архиве

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 ];

Событие 81. Изменение состояния невидимки друга

Если невидимка будет выключена не для всех, то метод так же вернет state: 1,
хотя для нас онлайн может быть так же виден

type Event81 = [ type: 81, // Отрицательный id друга userId: number, // 0 - невидимка выключена, 1 - включена state: 0 | 1, // время последнего онлайна друга timestamp: number, -1, // При настройке невидимки через VK Me приходит 0, в других случаях не проверял appId: 0 | number ];

Событие 90. Добавление или удаление из друзей

Событие возвращается только для действий с вашей стороны.

Значения actionType:

  • 2 — вы приняли заявку пользователя
  • 3 — вы удалили из друзей или отклонили заявку в друзья пользователя
type Event90 = [ type: 90, actionType: 2 | 3, userId: number ];

Событие 114. Изменение настроек пуш-уведомлений в беседе

Для работы необходимо использовать в mode флаг 8 при выполнении запроса.

  • peer_idid беседы, в которой включили или выключили уведомления
  • sound — нестабильный параметр, не рекомендую его обрабатывать
  • disabled_until может быть трех видов:
    • 0 — Уведомления включены
    • -1 — Уведомления выключены
    • number, > 0, — Уведомления выключены до указанного timestamp
type Event114 = [ type: 114, data: { peer_id: number sound: 0 | 1 disabled_until: 0 | -1 | number } ];

Событие 115. Звонок

Для работы необходимо использовать в mode флаг 8 при выполнении запроса.

¯_(ツ)_/¯

Событие 119. Ответ callback-кнопки

Для работы необходимо использовать в mode флаг 8 при выполнении запроса.

Callback-кнопки работают следующим образом:

  1. Бот отправляет клавиатуру (обычную или инлайн), где находится callback-кнопка;
  2. Пользователь нажимает на эту кнопку и клиент вызывает метод messages.sendMessageEvent (параметры см. ниже).
    Метод возвращает строку — event_id;
  3. Бот получает событие message_event и вызывает метод messages.sendMessageEventAnswer;
  4. Этот метод вызывает 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 } } ];

Событие 501. Создание папки

type Event501 = [ type: 501, // Идентификатор папки folder_id: number // Имя папки name: string // Возможно, random_id random_id: number ];

Пример: пользователь создал папку с названием Рабочие, id папки — 4.

// folder_id, name, random_id [501, 4, "Рабочие", 0] 

Событие 502. Удаление папки

type Event502 = [ type: 502, // Идентификатор папки folder_id: number ];

Пример: пользователь удалил папку с id = 4.

Событие 503. Переименование папки

type Event503 = [ type: 503, // Идентификатор папки folder_id: number // Новое имя папки name: string ];

Пример: пользователь сменил имя папки, с id = 4 на Семья.

// folder_id, name [503, 4, "Семья"] 

Событие 504. Добавление беседы в папку

Порядок такой: тип события, идентификатор папки, далее идут peer_id тех бесед, которые были добавлены в папку. Например — в папку с id 2 были добавлены две беседы с peer_id 168354935 и 2000000192:

[504, 2, 168354935, 2000000192] 

Событие 505. Удаление беседы из папки

Порядок такой же, как и в 504. Например — из папки с id 2 были удалены две беседы с peer_id 2000000020 и 377864107:

[505, 2, 2000000020, 377864107] 

Событие 506. Изменился порядок папок

Порядок такой: тип события, далее id папок.

Событие 507. Изменение количества непрочитанных диалогов в папках

После типа события идут массивы, содержащие id папки, количество всех непрочитанных бесед и количество непрочитанных незамьюченных бесед

// folder_id, unread, unread_unmuted [507, [1, 3, 2], [2, 1, 0]] 

Дополнительная информация

Флаги сообщений

Маской называют сумму некоторых флагов (степеней двойки), которую можно использовать как хорошую замену для объектов или массивов.

// Обычно маску записывают подобным образом: 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);
Название Описание Бит Значение
unread Непрочитанное сообщение 1 << 0 1
outbox Исходящее сообщение 1 << 1 2
important Важное сообщение 1 << 3 8
chat_vkcom Отправка сообщения в беседу через vk.com 1 << 4 16
friends Исходящее; входящее от друга в лс 1 << 5 32
spam Пометка сообщения как спам 1 << 6 64
deleted Удаление сообщения локально 1 << 7 128
audio_listened Прослушано голосовое сообщение 1 << 12 4096
chat Отправка сообщения в беседу 1 << 13 8192
cancel_spam Отмена пометки как спам 1 << 15 32768
old_minor_id Сообщение не поднимает диалог вверх 1 << 16 65536
deleted_all Удаление сообщения для всех 1 << 17 131072
not_delivered Внутренний флаг 1 << 18 262144
chat_in Входящее сообщение в беседе 1 << 19 524288
silent Сообщение без уведомления 1 << 20 1048576
reply_msg Ответ на сообщение 1 << 21 2097152
auto_read Сообщение пришло сразу прочитанным 1 << 23 8388608
has_ttl Внутренний флаг 1 << 26 67108864

Бесшумное сообщение можно отправить, добавив к параметрам метода messages.send ключ silent: true:

  • Такое сообщение отправится без уведомления пользователю, даже если у него включены уведомления
  • Упоминание или ответ на сообщение тоже не отправит уведомления
  • Сообщение о выходе из беседы автоматически приходит без уведомления

not_delivered и has_ttl приходят для бизнес-уведомлений

Пример определения наличия флага в маске:

const mask = 1 | 2 | 32; // = 35 8 & mask // вернет 0 (false) 2 & mask // вернет 2 (true)

Сервисные сообщения

Сервисное сообщение описывается ключом 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_midid закрепившего сообщение
source_message — обрезанное закрепленное сообщение
source_chat_local_id — локальный id сообщения
chat_unpin_message
Открепление сообщения
source_midid открепившего сообщение
source_chat_local_id — локальный id сообщения
chat_invite_user
Вступление в беседу
source_midid вступившего в беседу
chat_invite_user_by_link
Вступление в беседу по ссылке
chat_kick_user
Выход или исключение из беседы
source_midid вышедшего или исключенного
chat_kick_don
Исключение дона из беседы
Приходит, начиная с версии API 5.154
chat_screenshot
Создание скриншота с фантомным сообщением
source_midid создавшего скриншот
chat_group_call_started
Начало группового звонка в беседе
Больше не приходит в LongPoll
Вместо него создается сообщение с вложением group_call_in_progress
Приходит для старых сообщений через API при получении через токен VK для Android
chat_invite_user_by_call
Приглашение пользователя в звонок
source_midid приглашенного юзера
chat_invite_user_by_call_join_link
Присоединение пользователя к звонку по ссылке
chat_invite_user_by_message_request
Запрос на добавление в беседу
source_midid пригласившего в беседу
conversation_style_update
Обновление стиля беседы
source_style — название стиля
Поле не приходит в случае сброса стиля
Возможные варианты можно посмотреть
здесь

Пример описания сервисного сообщения:

const longpollServiceMessage = { source_act: 'chat_pin_message', source_mid: '88262293', source_message: 'Сообщение, которое будет в закрепе', source_chat_local_id: '5517' }

Клавиатура для ботов

Клавиатура для ботов представляет собой объект с описанием ее типа и кнопок.
Основная структура представлена ниже, остальную информацию можно узнать в документации.

// Типы у объекта клавиатуры: // 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.

Работа с вложениями в LongPoll

Пример вложений, состоящих из фотографии, документа и аудиозаписи:

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

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, было только что отправлено.
Для этого нужно:

  1. Создать random_id и сохранить его в списке с отправляемыми сообщениями
  2. Отправить сообщение
  3. Дождаться прихода из LongPoll сообщения с нашим random_id
  4. Удалить random_id из списка, тем самым пометив сообщение как отправленное.

Актуально. После 10-16 часов работы бот выключается с такой ошибкой:

←[35m04:32:43←[0m - ←[31mERROR←[0m - ←[39mПроизошла ошибка при вызове метода API messages.getLongPollServer с значениями
 {'use_ssl': 1}:
({'error_code': 10, 'error_msg': 'Internal server error', 'request_params': [{'key': 'oauth', 'value': '1'}, {'key': 'me
thod', 'value': 'messages.getLongPollServer'}, {'key': 'use_ssl', 'value': '1'}, {'key': 'v', 'value': '5.52'}]}, 'https
://api.vk.com/method/messages.getLongPollServer')←[0m
←[35m04:32:44←[0m - ←[31mERROR←[0m - ←[39mПроизошла ошибка при вызове метода API messages.getLongPollServer с значениями
 {'use_ssl': 1}:
({'error_code': 10, 'error_msg': 'Internal server error', 'request_params': [{'key': 'oauth', 'value': '1'}, {'key': 'me
thod', 'value': 'messages.getLongPollServer'}, {'key': 'use_ssl', 'value': '1'}, {'key': 'v', 'value': '5.52'}]}, 'https
://api.vk.com/method/messages.getLongPollServer')←[0m
←[35m04:32:46←[0m - ←[31mERROR←[0m - ←[39mПроизошла ошибка при вызове метода API messages.getLongPollServer с значениями
 {'use_ssl': 1}:
({'error_code': 10, 'error_msg': 'Internal server error', 'request_params': [{'key': 'oauth', 'value': '1'}, {'key': 'me
thod', 'value': 'messages.getLongPollServer'}, {'key': 'use_ssl', 'value': '1'}, {'key': 'v', 'value': '5.52'}]}, 'https
://api.vk.com/method/messages.getLongPollServer')←[0m
←[35m04:32:47←[0m - ←[31mERROR←[0m - ←[39mПроизошла ошибка при вызове метода API messages.getLongPollServer с значениями
 {'use_ssl': 1}:
({'error_code': 10, 'error_msg': 'Internal server error', 'request_params': [{'key': 'oauth', 'value': '1'}, {'key': 'me
thod', 'value': 'messages.getLongPollServer'}, {'key': 'use_ssl', 'value': '1'}, {'key': 'v', 'value': '5.52'}]}, 'https
://api.vk.com/method/messages.getLongPollServer')←[0m
←[35m04:32:49←[0m - ←[31mERROR←[0m - ←[39mПроизошла ошибка при вызове метода API messages.getLongPollServer с значениями
 {'use_ssl': 1}:
({'error_code': 10, 'error_msg': 'Internal server error', 'request_params': [{'key': 'oauth', 'value': '1'}, {'key': 'me
thod', 'value': 'messages.getLongPollServer'}, {'key': 'use_ssl', 'value': '1'}, {'key': 'v', 'value': '5.52'}]}, 'https
://api.vk.com/method/messages.getLongPollServer')←[0m
←[35m04:32:49←[0m - ←[31mERROR←[0m - ←[39mНе удалось получить значения Long Poll сервера!←[0m

При активации кода вк бота для бесед выводит большую ошибку и.. как её пофиксить?
код:

import vk_api
from vk_api.bot_longpoll import VkBotLongPoll, VkBotEventType

vk_session = vk_api.VkApi(token = "тут токен")
longpoll = VkBotLongPoll(vk_session, тут айди группы)

def sender(id, text):
     vk_session.method('messages.send', {'chat_id' : id, 'message' : text, 'random_id' : 0})

for event in longpoll.listen():
    if event.type == VkBotEventType.MESSAGE_NEW:
       if event.from_chat:

        id = event.chat_id
        msg = event.object.message['text'].lower()

        if msg == 'ку':
            sender(id, 'привет')

Ошибка:

Traceback (most recent call last):
  File "D:Gamesbottest2.py", line 5, in <module>
    longpoll = VkBotLongPoll(vk_session, 206146581)
  File "C:UsersДимаAppDataLocalProgramsPythonPython38libsite-packagesvk_apibot_longpoll.py", line 218, in __init__
    self.update_longpoll_server()
  File "C:UsersДимаAppDataLocalProgramsPythonPython38libsite-packagesvk_apibot_longpoll.py", line 231, in update_longpoll_server
    response = self.vk.method('groups.getLongPollServer', values)
  File "C:UsersДимаAppDataLocalProgramsPythonPython38libsite-packagesvk_apivk_api.py", line 646, in method
    raise error
vk_api.exceptions.ApiError: [15] Access denied: no access to call this method

При попытке получить обновления сообщений через Long Poll, в консоли выходит ошибка, которая гласит о синтаксической ошибке 5bba1d7f2d5bc081249968.png

Сам код:

//Вызываю метод
vk.method(«messages.getLongPollServer», {}, function(data){
let LongPoll = data.response //Сохраняю ключ, server и ts
console.log(data) //Вывожу их в консоль
$.ajax({
url: «https://»+LongPoll.server+»?act=a_check&key=»+LongPoll.key+»&ts=»+LongPoll.ts+»&wait=25″, //Получаю данные
method: «GET»,
dataType: «JSONP»,
success: function(resp){
console.log(resp); //Вывожу полученные данные в консоль
}
});
});

Miron Diamond


  • #1

Версия MoonLoader

.026-beta

Вырвал кусок кода из vk notifications для чтения сообщений из вк и вставил в свой, всё переделал под себя и выдает ошибку: Access denied: no access to call this method
В оригинальном тоже самое. Что не так с методом? Памагити ;)

sa-mp-298.png

randazzo


  • randazzo

    randazzo

  • 18 Фев 2021

groups.getLongPollServer требует ещё manage, добавь «управление сообществом»

Miron Diamond


Rei

русская весна


  • #3

если оригинал тоже не работает, то значит где-то накосячил
лонг полл в группе самой врубил?

Miron Diamond


  • #4

если оригинал тоже не работает, то значит где-то накосячил
лонг полл в группе самой врубил?

да, + даже версию 5.80 поставил в группе

randazzo


  • #5

да, + версию 5.80 поставил в группе

1613666624143.png

необходимые типы включи и токен должен быть с правами «сообщения сообщества»

Miron Diamond


  • #6

1613666950923.png

Уже было.

randazzo


Miron Diamond


randazzo


  • #9

groups.getLongPollServer требует ещё manage, добавь «управление сообществом»

  • Ошибка меткого стрелка психология
  • Ошибка меткого стрелка примеры
  • Ошибка мет на ивеко еврокарго
  • Ошибка мертвый угол форд эксплорер 5
  • Ошибка мертвого жокея содержание