python

Будь в курсе

  • понедельник, 24 июля 2017 г. в 03:10:54
https://habrahabr.ru/post/333942/
  • Программирование
  • Вконтакте API
  • Python



Вконтакте запустил Streaming API, инструмент для получения публичных данных из ВКонтакте по заданным ключевым словам.

ВК сам присылает новый подходящий контент по мере его появления. Таким образом можно получать интересующие записи без вступление в десятки групп, сразу после публикации.

Давайте напишем бота для телеграмма с оповещением о записях в ВК.

Получаем новости


Для начала нужно создать приложение в ВК и взять из настроек сервисный ключ



Для передачи данных используется протокол WebSocket. Испольхуем библиотеку «websocket».

import websocket

Перед соединением используйте метод streaming.getServerUrl. В качестве результата метод возвращает URL для дальнейших запросов в поле endpoint (string) и ключ доступа в поле key (string).

Получаем урл и ключ

def get_streaming_server_key(token):
    request_url = "https://api.vk.com/method/streaming.getServerUrl?access_token={}&v=5.64".format(token)
    r = requests.get(request_url)
    data = r.json()
    return {"server":data["response"]["endpoint"],"key":data["response"]["key"]}

Слушаем сервер

def listen_stream():
    websocket.enableTrace(True)
    ws = websocket.WebSocketApp("wss://{}/stream?key={} ".format(stream["server"], stream["key"]),
                                on_message=on_message,
                                on_error=on_error,
                                on_close=on_close)
    ws.on_open = on_open
    ws.run_forever()

def on_message(ws, message):
    print(">>>> receive message:", message)

def on_error(ws, error):
    print(">>>> error thead:",error)

def on_close(ws):
    print(">>>> close thead")

def on_open(ws):
    print(">>>> open thead")


stream = get_streaming_server_key(my_servise_token)
listen_stream()

После запуска в консоли должно появиться примерно следующее
— request header — GET /stream?key=e6290ba04916a314c398c331f60224d012fabeb1 HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: streaming.vk.com
Origin: streaming.vk.com
Sec-WebSocket-Key: vG40A5bwbPaVBS+DLBOyog==
Sec-WebSocket-Version: 13
— — response header — HTTP/1.1 101 Switching Protocols
Server: Apache
Date: Thu, 20 Jul 2017 22:06:55 GMT
Connection: upgrade
Upgrade: websocket
Sec-WebSocket-Accept: QRJNqD8K7vWNMcQesYKo64q8MfA=
— >>>> open thead
send: b"\x8a\x80d')\x90"
send: b'\x8a\x80\xfcmp\xe6'
send: b'\x8a\x80s\x9f6{'
send: b'\x8a\x80\xc9\xfa.\xd4'
send: b'\x8a\x80fU<\xed'
send: b'\x8a\x80\xc6Ji\x19'
send: b'\x8a\x80\xd2D\x08!'

Работа с правилами


Добавление

Метод HTTP POST
Content Type application/json
URL запроса https://{endpoint}/rules?key={key}


def set_my_rules(value):
    rule_params = {"rule":{"value":value,"tag":'tag_'+str(random.randint(11111, 99999))}}
    headers = {'content-type': 'application/json'}
    r = requests.post("https://{}/rules?key={}".format(stream["server"], stream["key"]), data=json.dumps(rule_params), headers=headers)
    data = r.json()

    return data['code'] == 200

Получение

Метод HTTP GET
URL запроса https://{endpoint}/rules?key={key}



def get_my_rules():
    r = requests.get("https://{}/rules?key={}".format(stream["server"], stream["key"]))
    data = r.json()
    if data['code'] != 200:
        return False

    return data['rules']

Удаление

Метод HTTP DELETE
Content Type application/json
URL запроса https://{endpoint}/rules?key={key}


def del_my_rules(tag):
    headers = {'content-type': 'application/json'}
    rule_params = {"tag":tag}
    r = requests.delete("https://{}/rules?key={}".format(stream["server"], stream["key"]), data=json.dumps(rule_params), headers=headers)
    data = r.json()

    return data['code'] == 200

Пробуем получить новости

stream = get_streaming_server_key(my_servise_token)
set_my_rules('кот')
listen_stream()

В консоли должно появиться примерно следующее
send: b'\x8a\x80\xc9\xfa.\xd4'
send: b'\x8a\x80fU<\xed'
send: b'\x8a\x80\xc6Ji\x19'
send: b'\x8a\x80\xd2D\x08!'
{«code»:100,«event»:{«event_type»:«post»,«event_id»:{«post_owner_id»:-35708825,«post_id»:4560},«event_url»:«vk.com/wall-35708825_4560»,«text»:«vk.com/small.dolly
Пропал кот. В г.Электросталь на улице Загонова в районе 16 школы. Молодой мальчик, 2 года кастрирован. Обычного серого окраса с полосками. На подбородке и грудке белое пятно. Кот крупный около 7 кг. Пропал 27 июня. Если кто-то его видел пожалуйста сообщите 89250506596 или 89251527466. Мы его очень любим и хотим чтобы он вернулся домой»,«creation_time»:1498942995,«tags»:[«test_cats»],«author»:{«id»:-35708825}}} — наша новсть

Телеграмм


Используем библиотеку «telebot».


import telebot

TELEGRAM_API_TOKEN = '3277332...' # токен выдаваемый при создании бота
bot = telebot.TeleBot(TELEGRAM_API_TOKEN)

Старт бота


def _send(msg):
    markup = types.ReplyKeyboardMarkup(row_width=1, resize_keyboard=True)
    markup.add('Мои интересы', 'Очистить список интересов', 'Добавить')

    msg = bot.send_message(chatID, msg, reply_markup=markup) # шлем текст с вариантами ответа
    bot.register_next_step_handler(msg, process_step) # устанавливаем обработчик для наших ответов

# обработчик для 'help', 'start'
@bot.message_handler(commands=['help', 'start'])
def send_welcome(message):
    global chatID
    chatID = message.chat.id
    hello_test = 'Привет, %s! Я бот использующий VK Streaming API!' % message.from_user.first_name
    _send(hello_test)

# обработчик для наших ответов
def process_step(message):
    if message.text == 'Мои интересы':
        _send(get_rules_list())

    if message.text == 'Очистить список интересов':
        _send(clear_rules_list())

    if message.text == 'Добавить':
        msg = bot.send_message(chatID, "Что добавить?")
        bot.register_next_step_handler(msg, add_rule_handler)

Добавить




if message.text == 'Добавить':
        msg = bot.send_message(chatID, "Что добавить?")
        bot.register_next_step_handler(msg, add_rule_handler)
.....
def add_rule_handler(message):
    new_rule = set_my_rules(message.text)
    if new_rule:
        _send("Successful")
    else:
        logging.debug("Error add rules")
        _send("Error")

Мои интересы

def get_rules_list():
    rules = get_my_rules()
    if rules:
        return "\n".join([str(rule['value']) for rule in rules])
    else:
        logging.debug("Error get rules list")
        return 'Error'

Очистить список интересов
def clear_rules_list():
    rules = get_my_rules()
    if rules:
        for rule in rules:
            del_my_rules(rule['tag'])

        return "Successful"

    else:
        logging.debug("Error clear rules list")
        return 'Error'

Отправка новостей

def on_message(ws, message):
    print(">>>> receive message:", message)
    message = json.loads(message)

    if not message['code']:
        return

    if not message['event']['event_type'] or message['event']['event_type'] != 'post':
        return # выходим, если запись не отдельный пост

    post = message['event']['event_type'] +"\n"+message['event']['text'].replace("<br>", "\n") +"\n\n"+ message['event']['event_url']

    _send_post(post)

Запускаем все вместе


Для корректной работы сокеты нужно запустить в отдельном треде.

def listen_stream():
    websocket.enableTrace(True)
    ws = websocket.WebSocketApp("wss://{}/stream?key={} ".format(stream["server"], stream["key"]),
                                on_message=on_message,
                                on_error=on_error,
                                on_close=on_close)
    ws.on_open = on_open
    #ws.run_forever()

    wst = threading.Thread(target=ws.run_forever)
    wst.daemon = True
    wst.start()

Запускаем

if __name__ == '__main__':
    try:
        chatID = 0
        stream = get_streaming_server_key(my_servise_token)
        listen_stream()

        bot.polling(none_stop=True)
    except Exception as e:
        logging.exception("error start")





На этом все, спасибо.

полный код скрипта
Streaming API docs
Telegram API docs
Python websocket-client
Python TelegramBotAPI