Пишем чат бота для ВКонтакте на python с помощью longpoll +17


Сейчас боты стали обыденностью и находятся на каждом шагу, но если тебе нужен свой бот в социальной сети вконтакте, то это легко реализовать.

Нам понадобятся:

  1. Python
  2. VK Api
  3. Желание

Ну прям совсем для новичков


Как установить Python?
Скачиваем, запускаем установщик.

Куда писать этот код?
В текстовый документ с расширением .py

А чем писать?
Да хоть блокнотом. Лично я рекомендую Notepad++

А как запускать?
Через командную строку.
python путь до папки с файлом\файл.py

Как оно работает?


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

Техническая реализация


Для начала нам нужно доказать вконтакту что мы — это мы, а не кто-либо ещё. Делается это очень просто.

import vk_api
import requests

session = requests.Session()
login, password = 'Ваш логин, email или телефон', 'Ваш пароль'
vk_session = vk_api.VkApi(login, password)
try:
    vk_session.auth(token_only=True)
except vk_api.AuthError as error_msg:
    print(error_msg)
    return

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

Если бот будет сидеть в группе то авторизация выглядит по другому.

import requests
import vk_api

vk_session = vk_api.VkApi(token='токен с доступом к сообщениям и фото')


— Что такое токен?
— Такая штука из циферок и буковок которую нужно получить в настройках группы. Для этого достаточно открыть раздел «Управление сообществом» («Управление страницей», если у Вас публичная страница), выбрать вкладку «Работа с API» и нажать «Создать ключ доступа».

Теперь вызовем longpool.

from vk_api.longpoll import VkLongPoll, VkEventType
for event in longpoll.listen():
    if event.type == VkEventType.MESSAGE_NEW and event.to_me and event.text:
   #Слушаем longpoll, если пришло сообщение то:			
        if event.text == 'Первый вариант фразы' or event.text == 'Второй вариант фразы': #Если написали заданную фразу
            if event.from_user: #Если написали в ЛС
                vk.messages.send( #Отправляем сообщение
                    user_id=event.user_id,
                    message='Ваш текст'
		)
            elif event.from_chat: #Если написали в Беседе
                vk.messages.send( #Отправляем собщение
                    chat_id=event.chat_id,
                    message='Ваш текст'
		)

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

import datetime
vk.messages.send(
    user_id=event.user_id,
    message='Московское время: ' + str(now.strftime("%H:%M"))
)

А ещё можно прикреплять картинки.

attachments = []
from vk_api import VkUpload 
upload = VkUpload(vk_session)
image_url = 'Ссылка на картинку'
image = session.get(image_url, stream=True)
photo = upload.photo_messages(photos=image.raw)[0]
attachments.append(
    'photo{}_{}'.format(photo['owner_id'], photo['id'])
)
vk.messages.send(
    user_id=event.user_id,
    attachment=','.join(attachments),
    message='Ваш текст'
)

Можно придумать ещё много всего интересного, но тут подумайте сами, а я лишь скажу что: ссылки можно делить на части. Например:

image_url = 'http://сайт.com/uploads/' + event.text + '.png'

и никто не запретил нам получать ответ от пользователя на примере Википедии:

import wikipedia #Модуль Википедии
wikipedia.set_lang("RU")
if event.text == 'Википедия' or event.text == 'Вики' or event.text == 'википедия' or event.text == 'вики' or event.text == 'Wikipedia' or event.text == 'wikipedia' or event.text == 'Wiki' or event.text == 'wiki': #если нам пришло сообщение с текстом Википедия или Вики или ... или wiki
    if event.from_user: #Если написали в KC
        vk.messages.send(
            user_id=event.user_id,
            message='Введите запрос' #Пишем "Введите запрос"
	)
    elif event.from_chat: #Если написали в беседе
        vk.messages.send(
            chat_id=event.chat_id,
            message='Введите запрос' #Пишем "Введите запрос"
	)
    for event in longpoll.listen():
        if event.type == VkEventType.MESSAGE_NEW and event.to_me and event.text: #Пинаем longpoll
            if event.from_user:
                vk.messages.send( #Если написали в ЛС
                    user_id=event.user_id,
                    message='Вот что я нашёл: \n' + str(wikipedia.summary(event.text)) #Пишем "Вот что я нашёл" И то что вернёт нам api Wikipedia по запросу текста сообщения
		)
                break #выходим из цикла
        elif event.from_chat: #Если написали в беседе
            vk.messages.send(
                chat_id=event.chat_id,
                message='Вот что я нашёл: \n' + str(wikipedia.summary(event.text)) #Пишем "Вот что я нашёл" И то что вернёт нам api Wikipedia по запросу текста сообщения
	    )
            break #выходим из цикла
    continue

Ссылки на примеры и документацию


Пример бота работающего на DuckDuckGo api
Примеры использования VK api (общие)
Документация по VK api Раз, Два

На этом я с вами попрощаюсь. Хорошего кодинга.

Вы можете помочь и перевести немного средств на развитие сайта

Теги:



Комментарии (19):

  1. soon
    /#19312851

    В свое время был написан бот который пересылал в группу письма из почты. Крутился на AWS, но спустя некоторе время vk начал блокировать авторизацию, подсовывая капчу, в связи с чем бот был заброшен. А потом и телеграм подоспел с более адекватным API

    • Xokare228
      /#19312861

      Я тьфу-тьфу-тьфу с капчей не встречался т.к. бот запускается 1 раз и жужжит себе в фоне.

      • soon
        /#19312881

        Насколько я помню, проблема была в том, что аккаунт был зарегистрирован из России, а AWS крутилась где-то снаружи. В результате ВК периодически решал что неплохо бы проверить кто это пишет сообщения, и прямо посреди работы бота мог заблочить последующую отправку до тех пор пока я не заходил на инстанс AWS и руками не подтверждал что это действительно "я". Не помню точно, почему не перенес в Россию, вполне возможно что у амазона был жирный кредит, а тратиться на сервера не хотелось

      • tamapw
        /#19315288

        Каптча может вылезти не только при авторизации, но и, если начать заспамливать вашего бота.
        У вас в показанных кусках я не увидел ограничение на количество обращений к API. Насколько я помню, 2-3 в секунду — оптимальное. При превышении начнёт капчу просить.

        И так же при однотипных действиях начнёт просить, но на общий функционал не повлияет — longpoll как работал, так и будет работать, просто некоторое время не будет отправлять сообщения. У меня при 10 однотипных действиях действиях просто 2-3 последних не отрабатывает. И через пару секунд уже работает, как ни в чём не бывало.

  2. ThisMan
    /#19313387

    Забавно, что сам вчера начал писать бота, который выкладывает в паблик картинки, которые прислали в конфе. Правда столкнулся с проблемой, что во-первых atttachments, который приходит при событии longpoll имеет не очень удобный формат:


    attachment1_type, attachment1, attachment2_type, attachment2, etc

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

    • tamapw
      /#19315264

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

      Когда именно картинки не доступны по прямой ссылке?

      longpoll в принципе не даёт адекватной возможности разбирать вложения и пересылки, ибо данные там не дают совершенно никакой информации о них.

      Я решил проблему так: если в сообщении было вложение картинки\пересылки, то делаю запрос в messages.getById (что-то подобное) и получаю всю информацию по указанному сообщению. И в этой информации ВСЕГДА есть прямая ссылка на вложение(видео, аудио, аудиосообщение, изображение, документ).
      В вашем случае есть вариант уменьшить количество запросов к API — накапливать сообщения с изображениями и затем делать запрос с 10-20 сообщениями сразу. Но один ньюанс — если пользователь удалит картинку до запроса getById, то изображение будет утеряно.

      • ThisMan
        /#19315380

        Идея интересная, просто казалось, что longpoll будет достаточно, для того, что бы получить всю нужную инфу.

        • tamapw
          /#19315398

          Увы, но нет, недостаточно. ;)
          Видели же, как отображаются пересланные сообщения?… fwd: 0_0…
          Из этих удивлённых глаз никакую информацию не вытянуть — хоть пытай, хоть веди задушевную беседу.

  3. masb
    /#19314036

    Long Polling

    Какое максимальное время ожидания события/ответа?

    • Xokare228
      /#19314050

      wait — время ожидания (так как некоторые прокси-серверы обрывают соединение после 30 секунд, мы рекомендуем указывать wait=25). Максимальное значение — 90.

      Документация

      • Xokare228
        /#19314068

        А так насколько я понял longpoll.listen(): на бесконечном цикле

        def listen(self):
                """ Слушать сервер
        
                :yields: :class:`Event`
                """
        
                while True:
                    for event in self.check():
                        yield event

        • tamapw
          /#19315306

          Не видел реализацию на питоне, но он, скорее всего, просто делает периодические запросы в longpoll из разряда «а не появилось ли чего нового?». Если появилось — вернёт результат вам и перезапишет ts, чтобы в следующем запросе не продублировались данные, которые уже вам вернули только что.

          Это не связано с временем ожидания.

  4. ZXZs
    /#19315354

    Ох уж этот код:


    if event.text == 'Википедия' or event.text == 'Вики' or event.text == 'википедия' or event.text == 'вики' or event.text == 'Wikipedia' or event.text == 'wikipedia' or event.text == 'Wiki' or event.text == 'wiki':

    А разве вот так сделать не получится?:


    vars = ['википедия', 'вики', 'wikipedia', 'wiki']
    if ( str(event.text).lower() ) in vars:
        # ... some code should be here ... #

    Мне кажется, так будет лаконичнее :)

    • coderun
      /#19315520

      Не все умеют в лаконичный Питон

      • tamapw
        /#19315900

        Тут нет ничего питоно-специфичного же.)
        Такая оптимизация внешнего вида присутствует много где.

        • ZXZs
          /#19320598

          Много где, да (ИМХО) только в питоне всё так просто. Я постепенно начинаю понимать, почему его так любят за умение работать с данными. Потому что вызвал один метод и счастлив :)

  5. taranarmo
    /#19316424

    message='Московское время: ' + str(now.strftime("%H:%M"))


    Лучше string interpolation использовать. И читаемость повысится, и писать удобнее:

    message=f'Московское время: {now:%H:%M}'