Criando e integrando o VK bot em um grupo por meio do VkBotLongPoll [Python]

Neste artigo, criaremos um bot e o integraremos ao grupo VK no Python 3.x

Para quem é este artigo?


Para quem deseja escrever um bot simples para sua comunidade, capaz de identificar equipes e exibir a resposta apropriada

Estágios principais



Criando um grupo de bot


Começaremos criando um bot, ou seja, um grupo no VK.

Para fazer isso, vá para "grupos" → "criar uma comunidade".

Selecione qualquer tipo de comunidade e insira um nome, tema de grupo.

Na página de configurações que é aberta, selecione "Trabalhar com a API".

Em seguida, você precisa criar uma chave de API.

Em seguida, selecione os parâmetros necessários para acessar sua chave de API.

Provavelmente, você precisará confirmar a ação no VK usando seu telefone celular. Em seguida, copie a chave da API resultante em algum lugar do arquivo. Nós ainda precisamos disso.

Então você precisa permitir mensagens. Para fazer isso, vá para a "mensagem" e ative-os. Ative também "Recursos de bot" em "Mensagens" -> "Configurações para o bot".

Lá, permitiremos adicionar uma comunidade a grupos se quisermos que o bot possa receber mensagens de um grupo.

Configurando Pesquisa Longa


Para trabalhar com a API Long Poll, usamos a biblioteca vk_api . Você pode instalá-lo através do pip.

Antes do trabalho, salvaremos nosso token da API no arquivo config.py e, a partir daí, carregaremos nossa chave.

Vamos criar nosso primeiro script. Vamos nomear server.py, que será o principal script do servidor.

Importamos os módulos que precisamos:

import vk_api.vk_api from vk_api.bot_longpoll import VkBotLongPoll from vk_api.bot_longpoll import VkBotEventType 

Crie uma classe de servidor:

 class Server: def __init__(self, api_token, group_id, server_name: str="Empty"): #    self.server_name = server_name #  Long Poll self.vk = vk_api.VkApi(token=api_token) #   Long Poll API self.long_poll = VkBotLongPoll(self.vk, group_id) #    vk_api self.vk_api = self.vk.get_api() def send_msg(self, send_id, message): """     messages.send :param send_id: vk id ,    :param message:    :return: None """ self.vk_api.messages.send(peer_id=send_id, message=message) def test(self): #      ID self.send_msg(255396611, "-!") 

Agora crie o arquivo server_manager.py, no qual ele gerenciará servidores diferentes. Por enquanto, para testes, escrevemos apenas uma chamada para a classe Server:

 #     Server from server import Server #   config.py  api-token from config import vk_api_token server1 = Server(vk_api_token, 172998024, "server1") # vk_api_token - API ,     # 172998024 - id - # "server1" -   server1.test() 

Importante!

O bot só pode escrever mensagens para usuários que permitiram que o bot enviasse mensagens. Você pode fazer isso na página da comunidade ou escrever o bot primeiro

Se tudo for feito corretamente, o bot nos enviará uma mensagem pessoal.

Agora adicione o bot ao grupo e ensine-o a processar mensagens.
Para adicionar um bot a um grupo, clique em "Convidar para uma conversa" no menu à direita da comunidade.

Adicione a função start ao bot, após o que começará a "ouvir" as mensagens via Long Poll (não esqueça de adicionar permissões aos tipos de eventos na seção "Trabalhando com API" -> "Long Poll API" e coloque a versão mais recente):

 def start(self): for event in self.long_poll.listen(): print(event) 

Execute-o através do server_manager.py:

 server1.start() 

Agora, se escrevermos uma mensagem para o grupo, podemos ver o objeto de evento:
<< classe 'vk_api.bot_longpoll.VkBotMessageEvent'> ({'type': 'message_new', 'object': {'date': 1541273151, 'from_id': 25599999999, 'id': 0, 'out': 0, 'peer_id': 2000000001, 'text': '[club172998024 | bot in Vk] este é um texto!', 'conversation_message_id': 187, 'fwd_messages': [], 'important': False, 'random_id': 0 , 'anexos': [], 'is_hidden': False}, 'group_id': 172998024})>

Além disso, se escrevermos em mensagens privadas:
<< classe 'vk_api.bot_longpoll.VkBotMessageEvent'> ({'type': 'message_new', 'object': {'date': 1541273238, 'from_id': 25599999999, 'id': 47, 'out': 0, 'peer_id': 255396611, 'text': 'esta é uma mensagem privada', 'conversation_message_id': 47, 'fwd_messages': [], 'important': False, 'random_id': 0, 'attachments': [], 'is_hidden ': Falso},' id_grupo ': 172998024})>


A partir desses dados, devemos prestar atenção ao tipo, object.from_id, object.id, object.peer_id, object.text . Os dados recebidos das mensagens e do grupo não diferem muito, exceto object.peer_id e object.id .

Se você observar atentamente, object.id para todas as mensagens do grupo é 0, mas não há mensagens de mensagens pessoais. Assim, é possível separar as mensagens recebidas do grupo e das pessoais.

Processamos os dados recebidos dentro da classe Server:

 def start(self): for event in self.long_poll.listen(): #   #    if event.type == VkBotEventType.MESSAGE_NEW: print("Username: " + self.get_user_name(event.object.from_id)) print("From: " + self.get_user_city(event.object.from_id)) print("Text: " + event.object.text) print("Type: ", end="") if event.object.id > 0: print("private message") else: print("group message") print(" --- ") def get_user_name(self, user_id): """   """ return self.vk_api.users.get(user_id=user_id)[0]['first_name'] def get_user_city(self, user_id): """   """ return self.vk_api.users.get(user_id=user_id, fields="city")[0]["city"]['title'] 


Escreveremos duas mensagens para o bot: uma do grupo, uma da PM. Então temos:
Nome de usuário: Arthur
De: São Petersburgo
Texto: [club172998024 | @ club172998024] esta é uma mensagem do grupo
Tipo: mensagem de grupo
-
Nome de usuário: Arthur
De: São Petersburgo
Texto: esta é uma mensagem privada
Tipo: mensagem privada
-

Nota


Como você pode ver antes da mensagem no grupo, [club172998024 | @ club172998024], para que a equipe processe corretamente, você deve se livrar de todo o conteúdo entre colchetes ou permitir que o bot acesse toda a correspondência

Como podemos ver, vk_api nos permite usar facilmente os métodos da API VK. Por exemplo, agora usamos o método users.get

Uma lista de todos os métodos está disponível em: vk.com/dev/methods

Eu aconselho você a estudar e experimentar os métodos que lhe interessam. Felizmente, o VK nos forneceu uma documentação muito boa, também em russo.

Para consolidar o material, vamos adicionar uma função para enviar uma mensagem através do método messages.send :

 def send_message(self, peer_id, message): self.vk_api.messages.send(peer_id=peer_id, message=message) 

<peer_id> é o identificador de destino. Para responder à mensagem de alguém, especificaremos event.object.peer_id como o parâmetro peer_id . Ou seja, enviaremos uma mensagem para a origem da solicitação.

Mude o método de início:

 def start(self): for event in self.long_poll.listen(): #   #    if event.type == VkBotEventType.MESSAGE_NEW: username = self.get_user_name(event.object.from_id) print("Username: " + username) print("From: " + self.get_user_city(event.object.from_id)) print("Text: " + event.object.text) print("Type: ", end="") if event.object.id > 0: print("private message") else: print("group message") print(" --- ") self.send_message(event.object.peer_id, f"{username},    !") 

Agora, se o bot receber a mensagem, ele responderá a nós neste estilo:
Arthur, recebi sua mensagem!

Código inteiro

server.py


 import vk_api.vk_api from vk_api.bot_longpoll import VkBotLongPoll from vk_api.bot_longpoll import VkBotEventType class Server: def __init__(self, api_token, group_id, server_name: str="Empty"): #    self.server_name = server_name #  Long Poll self.vk = vk_api.VkApi(token=api_token) #   Long Poll API self.long_poll = VkBotLongPoll(self.vk, group_id, wait=20) #    vk_api self.vk_api = self.vk.get_api() def send_msg(self, send_id, message): """     messages.send :param send_id: vk id ,    :param message:    :return: None """ self.vk_api.messages.send(peer_id=send_id, message=message) def test(self): self.send_msg(255396611, "-!") def start(self): for event in self.long_poll.listen(): #   #    if event.type == VkBotEventType.MESSAGE_NEW: username = self.get_user_name(event.object.from_id) print("Username: " + username) print("From: " + self.get_user_city(event.object.from_id)) print("Text: " + event.object.text) print("Type: ", end="") if event.object.id > 0: print("private message") else: print("group message") print(" --- ") self.send_message(event.object.peer_id, f"{username},    !") def get_user_name(self, user_id): """   """ return self.vk_api.users.get(user_id=user_id)[0]['first_name'] def get_user_city(self, user_id): """   """ return self.vk_api.users.get(user_id=user_id, fields="city")[0]["city"]['title'] def send_message(self, peer_id, message): self.vk_api.messages.send(peer_id=peer_id, message=message) 

server_manager.py


 #     Server from server import Server #   config.py  api-token from config import vk_api_token server1 = Server(vk_api_token, 172998024, "server1") server1.start() 


A tarefa de consertar o material:


Crie uma função que use o parâmetro peer_id e envie ao usuário uma foto carregada para a comunidade. Dock útil: vk.com/dev/messages.send

Solução
Primeiro, faça o upload da foto para o grupo e abra-a no VK, considere o link:
Vkcom / club172998024? z = photo-172998024_456239017 % 2Falbum-172998024_256250731

Estamos interessados ​​apenas na parte destacada: photo-172998024_456239017 . Passe-o como um argumento para o método messages.send:

 def send_img(self, peer_id): self.vk_api.messages.send(peer_id=peer_id, attachment="photo-172998024_456239017") 

Adicione-o ao método start e obtenha:



Isso é tudo o básico. O principal é aprender a usar o vk_api usando vários métodos, a lista inteira é: vk.com/dev/methods . Se você aprender a trabalhar com a documentação da API VK, poderá criar bots de várias complexidades e propósitos. Um exemplo do meu bot para um grupo de estudo: github.com/AppLoidx/GroupAssistant/tree/master

Agora vamos começar a criar a lógica do bot.


Crie commander.py, que aceitará comandos e retornará a resposta passada ao usuário Vk:

 class Commander: def __init__(self, vk_api, user_id): self.vk_api = vk_api self.user_id = user_id def input(self, msg): """     :param msg:  :return:  ,   """ pass 

Vamos construir a arquitetura do nosso programa:


Nós "ouvimos" o servidor Long Poll e recebemos uma mensagem do usuário ->
Passamos a mensagem para Commander.input () -> Definir o modo -> Definir o comando ->
Retornamos a resposta -> Transferimos para o usuário

Para definir o modo e o comando, criamos dois arquivos command_enum.py e mode_enum.py. Utilizando-os, definiremos modos e comandos através dos métodos da classe Enum:

command_enum.py:


 from enum import Enum class Command(Enum): """ weather """ weather = ["weather", ""] """ myanimelist """ anime_top = ["top anime", " "] 


mode_enum.py:


 from enum import Enum class Mode(Enum): default = [" ", "default"] translate = [" ", "translate"] get_ans = 0 


Para alterar os modos, use [slash ("/") + <mode_name>] e aceitaremos todos os outros comandos como comandos.

Implementamos isso no Commander.py:

 #  ,  from command_enum import Command from mode_enum import Mode #   from translate.yandex_translate import Translator from weather import Weather from myanimelist import Myanimelist # Config from config import yandex_translate_api class Commander: def __init__(self): # ,   self.now_mode = Mode.default self.last_mode = Mode.default self.last_command = None #     self.last_ans = None #    self.translator = Translator(yandex_translate_api) def change_mode(self, to_mode): """     :param to_mode:   """ self.last_mode = self.now_mode self.now_mode = to_mode self.last_ans = None def input(self, msg): """     :param msg:  :return:  ,   """ #      if msg.startswith("/"): for mode in Mode: if msg[1::] in mode.value: self.change_mode(mode) return "   " + self.now_mode.value[0] return "  " + msg[1::] #    if self.now_mode == Mode.get_ans: self.last_ans = msg self.now_mode = self.last_mode return "Ok!" if self.now_mode == Mode.default: #  if msg in Command.weather.value: return Weather.get_weather_today() #   if msg in Command.anime_top.value: res = "" top = Myanimelist.get_top() for anime in top: res += anime + " : " + top[anime] + "\n" return res if self.now_mode == Mode.translate: if self.last_ans is None: #    ,    self.change_mode(Mode.get_ans) self.last_command = msg return "     " elif self.last_ans == "change": #    self.last_ans = None self.change_mode(Mode.default) else: #  return self.translator.translate_to(msg, self.last_ans) return "  !" 

weather.py
 import requests from bs4 import BeautifulSoup class Weather: @staticmethod def get_weather_today(city: str = "-") -> list: http = "https://sinoptik.com.ru/-" + city b = BeautifulSoup(requests.get(http).text, "html.parser") p3 = b.select('.temperature .p3') weather1 = p3[0].getText() p4 = b.select('.temperature .p4') weather2 = p4[0].getText() p5 = b.select('.temperature .p5') weather3 = p5[0].getText() p6 = b.select('.temperature .p6') weather4 = p6[0].getText() result = '' result = result + (' :' + weather1 + ' ' + weather2) + '\n' result = result + (' :' + weather3 + ' ' + weather4) + '\n' temp = b.select('.rSide .description') weather = temp[0].getText() result = result + weather.strip() return result 


myanimelist.py
 import requests import bs4 class Myanimelist: @staticmethod def get_top(count: int=5, by: str="") -> dict: types = ["", "airing", "upcoming", "tv", "movie", "ova", "special", "bypopularity", "favorite"] if by not in types: return {"error: ": " !"} html = requests.get("https://myanimelist.net/topanime.php?type="+by) soup = bs4.BeautifulSoup(html.text, "html.parser") res = {} for anime in soup.select(".ranking-list", limit=count): url = anime.select(".hoverinfo_trigger")[0]['href'] anime = anime.select(".hoverinfo_trigger")[0].findAll("img")[0] name = anime['alt'].split(":")[1].strip(" ") res[name] = url return res 


yandex_translate.py
 import requests from config import yandex_translate_api class Translator: """ -  API Yandex Translate : _key --   API Yandex.Translate _yandex_comment --       API Yandex.Translate """ def __init__(self, key, comment=None): """ :param key:   API Yandex.Translate :param comment:     """ self._key = key if comment is None: self._yandex_comment = "\n  «.» http://translate.yandex.ru/" else: self._yandex_comment = comment def translate(self, text, lang, to_lang=None): """         :param text: ,    :param lang:   :param to_lang:   :return:   """ if to_lang is not None: lang = f"{lang}-{to_lang}" main_url = "https://translate.yandex.net/api/v1.5/tr.json/translate" response = requests.get(f"{main_url}?" f"key={self._key}&" f"lang={lang}&" f"text={text}") return response.json()['text'][0] + self._yandex_comment def lang_identify(self, text, hint="ru,en"): """   :param text:  :param hint:     :return:   """ main_url = "https://translate.yandex.net/api/v1.5/tr.json/detect" response = requests.get(f"{main_url}?" f"key={self._key}&" f"hint={hint}&" f"text={text}") return response.json()['lang'] def translate_ru_en(self, text): """       :param text: ,    :return:      """ if self.lang_identify(text) == "ru": to_lang = "en" from_lang = "ru" else: to_lang = "ru" from_lang = "en" return self.translate(text, from_lang, to_lang) def translate_to_ru(self, text, hint=None): """     :param text: ,    :param hint:     :return:      """ if hint is None: hint = "ru,en" from_lang = self.lang_identify(text, hint) return self.translate(text, from_lang, "ru") def translate_to(self, text, to_lang, hint=None): """      :param text: ,    :param to_lang:    :param hint:     :return:   """ if hint is None: hint = "ru,en" from_lang = self.lang_identify(text, hint) return self.translate(text, from_lang, to_lang) 


Todo o código está disponível no github: github.com/AppLoidx/VkLongPollBot

Adicione um teclado:


Este é um processo muito fácil. Podem ocorrer complicações quando alteramos o teclado para uma assinatura específica de comandos, diferente para cada modo.

Para adicionar um teclado à caixa de diálogo, você deve especificar o parâmetro de teclado que aceita json no método messages.send. É assim:

 vk_api.messages.send(...,keyboard=keyboard_json,...) 

Ou você pode passar o teclado diretamente do arquivo .json:

 vk_api.messages.send(...,keyboard=open(filename,"r",encoding="UTF-8").read() 

Documentação: vk.com/dev/bots_docs_3?f=4.%2BKeyboard%2Bfor%2Bworks

Considere o exemplo do nosso programa adicionando um teclado.

Primeiro, crie um arquivo keyboard.json:

 { "one_time": false, "buttons": [ [{ "action": { "type": "text", "label": "top anime" }, "color": "positive" }, { "action": { "type": "text", "label": "weather" }, "color": "positive" }], [{ "action": { "type": "text", "label": "translate" }, "color": "default" }] ] } 

Para remover o teclado, você precisa passar o json com botões vazios:

 {"buttons":[],"one_time":true} 

Substitua send_message em server.py:

 def send_msg(self, send_id, message): """     messages.send :param send_id: vk id ,    :param message:    :return: None """ return self.vk_api.messages.send(peer_id=send_id, message=message, keyboard=open("keyboards/default.json", "r", encoding="UTF-8").read()) 

E também no método start:

 def start(self): for event in self.long_poll.listen(): #   if event.type == VkBotEventType.MESSAGE_NEW: if event.object.from_id not in self.users: self.users[event.object.from_id] = Commander() #    if event.type == VkBotEventType.MESSAGE_NEW: self.send_msg(event.object.peer_id, self.users[event.object.from_id].input(event.object.text)) 

Como resultado, obtemos:



Última palavra


Você não deve usar a lista simples dos códigos-fonte apresentados aqui, eles são usados ​​apenas para que você entenda melhor o que está acontecendo sob o capô. Obviamente, eles são todos utilizáveis ​​e você pode usá-los em partes.

Pessoalmente, usei esse bot para um assistente de grupo que sabia como:

  • crie filas de membros do grupo, incluindo muitas equipes que editaram a fila, como adicionar, excluir, criar etc.
  • envie mensagens para todos os participantes
  • perguntas mais frequentes (por exemplo, em Java)
  • tornou possível criar aplicativos para troca de lugares etc.

Projeto Github
Fontes aqui apresentadas

Source: https://habr.com/ru/post/pt428680/


All Articles