Основы работы с LongPoll сервером ВКонтакте
- вторник, 8 августа 2017 г. в 03:12:22
Callback 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.
. https://api.vk.com/method/{METHOD_NAME}?{PARAMS}&v={API_VERSION}
, где {METHOD_NAME} — название метода, {PARAMS} — параметры вызываемого метода, а {API_VERSION} — версия API, которую должен использовать сервер при формировании ответа.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
Импортирование библиотеки requestsdata = 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'}]}}
. Long Polling — это технология, которая позволяет получать информацию о новых событиях с помощью «длинных запросов». Сервер получает запрос, но отправляет ответ на него не сразу, а лишь тогда, когда произойдет какое-либо событие (например, поступит новое входящее сообщение), либо истечет заданное время ожидания.Другими словами, получая от вас запрос, сервер ждет, когда произойдет событие, о котором он должен вас уведомить, и, когда оно происходит, Long Poll сервер отправляет ответ на ваш запрос, содержащий информацию о случившемся событии.
key — секретный ключ сессии;
server — адрес сервера;
ts — номер последнего события, начиная с которого нужно получать данные.
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}
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)
{'ts': 0000000000, 'updates': [[9, -999999999, 0, 1501588841]]}
. Что же значит пришедший ответ и как с ним дальше можно работать?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'] # обновление номера последнего обновления
[8, -999999999, 1, 1501592696]
[8, -999999999, 7, 1501592862]
[9, -999999999, 0, 1501592882]
[9, -999999999, 1, 1501592583]
[8, -999999999, 4, 1501592893]
[9, -999999999, 0, 1501592900]
[80, 0, 0]
[80, 1, 0]
# часть кода в этом примере опущена для сокращения места, без нее код работать не будет
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'] # обновление номера последнего обновления
elif action_code == 9:
[9, -000000000, 1, 1501744865]
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
# не забудьте написать вначале "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]
'Thu Jan 1 03:00:00 1970'
. Так как нам нужно лишь время последнего действия на сайте, мы разбиваем эту строку методом split по пробелам и получаем массив, где в индексе 0 хранится день недели, в индексе 1 — месяц, 2 — число, 3 — время, а 4 — год. Мы извлекаем элемент с индексом 3, то есть время.[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 = 'стал'
# определение платформы по ее коду
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])
[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, $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)
+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:
, чтобы при выводе данных было понятно, какие медиа-вложения к каким сообщениям относятся. [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))
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'] # обновление номера последнего обновления