Erstellen und Integrieren eines VK-Bots in eine Gruppe über VkBotLongPoll [Python]

In diesem Artikel erstellen wir einen Bot und integrieren ihn in die VK-Gruppe in Python 3.x.

Für wen ist dieser Artikel?


Für diejenigen, die einen einfachen Bot für ihre Community schreiben möchten, in der Lage sind, Teams zu identifizieren und die entsprechende Antwort anzuzeigen

Hauptbühnen



Erstellen einer Bot-Gruppe


Wir werden zunächst einen Bot erstellen, nämlich eine Gruppe in VK.

Gehen Sie dazu zu "Gruppen" → "Community erstellen".

Wählen Sie einen beliebigen Community-Typ aus und geben Sie einen Namen und ein Gruppenthema ein.

Wählen Sie auf der sich öffnenden Einstellungsseite "Mit der API arbeiten" aus.

Als Nächstes müssen Sie einen API-Schlüssel erstellen.

Wählen Sie dann die Parameter aus, die Sie für den Zugriff auf Ihren API-Schlüssel benötigen.

Höchstwahrscheinlich müssen Sie die Aktion in VK mit Ihrem Mobiltelefon bestätigen. Kopieren Sie dann den resultierenden API-Schlüssel irgendwo in eine Datei. Wir brauchen es noch.

Dann müssen Sie Nachrichten zulassen. Gehen Sie dazu zur "Nachricht" und schalten Sie sie ein. Aktivieren Sie auch "Bot-Funktionen" unter "Nachrichten" -> "Einstellungen für den Bot".

Dort können wir Gruppen eine Community hinzufügen, wenn der Bot Nachrichten von einer Gruppe empfangen soll.

Lange Umfrage einrichten


Um mit der Long Poll API zu arbeiten, verwenden wir die Bibliothek vk_api . Sie können es über pip installieren.

Vor der Arbeit werden wir unser API-Token in der Datei config.py speichern und von dort aus unseren Schlüssel laden.

Lassen Sie uns unser erstes Skript erstellen. Nennen wir server.py, das das Hauptserver-Skript sein wird.

Wir importieren die Module, die wir brauchen:

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

Erstellen Sie eine Serverklasse:

 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, "-!") 

Erstellen Sie nun die Datei server_manager.py, in der verschiedene Server verwaltet werden. Derzeit schreiben wir für Tests nur einen Aufruf an die Server-Klasse:

 #     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() 

Wichtig!

Der Bot kann nur Nachrichten an Benutzer schreiben, die dem Bot das Senden von Nachrichten erlaubt haben. Sie können dies auf der Community-Seite tun oder zuerst den Bot schreiben

Wenn alles richtig gemacht ist, sendet uns der Bot eine persönliche Nachricht.

Fügen Sie nun den Bot der Gruppe hinzu und bringen Sie ihm bei, wie Nachrichten verarbeitet werden.
Um einen Bot zu einer Gruppe hinzuzufügen, klicken Sie im rechten Community-Menü auf "Zur Konversation einladen".

Fügen Sie dem Bot die Startfunktion hinzu, wonach er Nachrichten über Long Poll "abhört" (vergessen Sie nicht, den Ereignistypen im Abschnitt "Arbeiten mit API" -> "Long Poll API" Berechtigungen hinzuzufügen und die neueste Version einzufügen):

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

Führen Sie es über server_manager.py aus:

 server1.start() 

Wenn wir nun eine Nachricht an die Gruppe schreiben, können wir das Ereignisobjekt sehen:
<< class '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 da Vk] dies ist ein text!', 'gesprächsnachrichten_id': 187, 'fwd_messages': [], 'wichtig': Falsch, 'zufällige_id': 0 , 'Anhänge': [], 'is_hidden': False}, 'group_id': 172998024})>

Auch wenn wir in privaten Nachrichten schreiben:
<< class 'vk_api.bot_longpoll.VkBotMessageEvent'> ({'type': 'message_new', 'object': {'date': 1541273238, 'from_id': 25599999999, 'id': 47, 'out': 0, 'peer_id': 255396611, 'text': 'Dies ist eine private Nachricht', 'Konversationsmessage_id': 47, 'fwd_messages': [], 'wichtig': Falsch, 'Zufalls_id': 0, 'Anhänge': [], 'is_hidden ': False},' group_id ': 172998024})>


Aus diesen Daten sollten wir den Typ object.from_id, object.id, object.peer_id, object.text beachten . Die von Nachrichten und von der Gruppe empfangenen Daten unterscheiden sich nur zwischen object.peer_id und object.id .

Wenn Sie genau hinschauen, ist object.id für alle Nachrichten aus der Gruppe 0, es gibt jedoch keine Nachrichten aus persönlichen Nachrichten. Somit ist es möglich, von der Gruppe empfangene und von persönlichen Nachrichten zu trennen.

Wir verarbeiten die empfangenen Daten innerhalb der Serverklasse:

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


Wir werden zwei Nachrichten an den Bot schreiben: eine aus der Gruppe, eine in PM. Dann bekommen wir:
Benutzername: Arthur
Aus: Sankt Petersburg
Text: [club172998024 | @ club172998024] Dies ist eine Nachricht von der Gruppe
Typ: Gruppennachricht
- -
Benutzername: Arthur
Aus: Sankt Petersburg
Text: Dies ist eine private Nachricht
Typ: private Nachricht
- -

Hinweis


Wie Sie vor der Nachricht in der Gruppe sehen können, gibt es [club172998024 | @ club172998024], damit das Team alle Inhalte in eckigen Klammern entfernen oder dem Bot Zugriff auf die gesamte Korrespondenz gewähren kann

Wie wir sehen können, können wir mit vk_api die VK-API-Methoden problemlos verwenden. Zum Beispiel haben wir jetzt die Methode users.get verwendet

Eine Liste aller Methoden finden Sie unter: vk.com/dev/methods

Ich rate Ihnen, die Methoden zu studieren und zu experimentieren, die Sie interessieren. Glücklicherweise hat uns VK eine sehr gute Dokumentation zur Verfügung gestellt, auch in russischer Sprache.

Fügen Sie zum Konsolidieren des Materials eine Funktion zum Senden einer Nachricht über die Methode messages.send hinzu :

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

<peer_id> ist die Zielkennung. Um auf die Nachricht einer anderen Person zu antworten, geben wir event.object.peer_id als Parameter peer_id an . Das heißt, wir senden eine Nachricht an die Herkunft der Anfrage.

Ändern Sie die Startmethode:

 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},    !") 

Wenn der Bot die Nachricht empfängt, antwortet er uns in diesem Stil:
Arthur, ich habe deine Nachricht bekommen!

Ganzer Code

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() 


Die Aufgabe zum Fixieren des Materials:


Erstellen Sie eine Funktion, die den Parameter peer_id verwendet und dem Benutzer ein Foto sendet, das an die Community hochgeladen wurde. Nützliches Dock: vk.com/dev/messages.send

Lösung
Laden Sie zuerst das Foto in die Gruppe hoch und öffnen Sie es in VK. Beachten Sie den Link:
vkcom / club172998024? z = photo-172998024_456239017 % 2Falbum-172998024_256250731

Wir interessieren uns nur für den hervorgehobenen Teil: photo-172998024_456239017 . Übergeben Sie es als Argument an die Methode messages.send:

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

Fügen Sie es der Startmethode hinzu und erhalten Sie:



Das sind alle Grundlagen. Die Hauptsache ist, zu lernen, wie man vk_api mit verschiedenen Methoden benutzt. Die gesamte Liste lautet: vk.com/dev/methods . Wenn Sie lernen, wie Sie mit der VK-API-Dokumentation arbeiten, können Sie Bots mit unterschiedlicher Komplexität und unterschiedlichen Zwecken erstellen. Ein Beispiel für meinen Bot für eine Lerngruppe : github.com/AppLoidx/GroupAssistant/tree/master

Beginnen wir nun mit der Erstellung der Bot-Logik.


Erstellen Sie die Datei command.py, die Befehle akzeptiert und die an den Vk-Benutzer übergebene Antwort zurückgibt:

 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 

Lassen Sie uns die Architektur unseres Programms erstellen:


Wir "hören" auf den Long Poll Server und erhalten eine Benutzermeldung ->
Wir übergeben die Nachricht an Commander.input () -> Definieren Sie den Modus -> Definieren Sie den Befehl ->
Wir geben die Antwort zurück -> Wir übertragen an den Benutzer

Um den Modus und den Befehl zu definieren, erstellen wir zwei Dateien command_enum.py und mode_enum.py. Mit ihnen definieren wir Modi und Befehle mit den Methoden der Enum-Klasse:

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 


Verwenden Sie zum Ändern des Modus [Schrägstrich ("/") + <Modusname>], und wir akzeptieren alle anderen Befehle als Befehle.

Wir implementieren dies in 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) 


Der gesamte Code ist auf github verfügbar: github.com/AppLoidx/VkLongPollBot

Fügen Sie eine Tastatur hinzu:


Dies ist ein sehr einfacher Vorgang. Komplikationen können auftreten, wenn wir die Tastatur auf eine bestimmte Signatur von Befehlen ändern, die für jeden Modus unterschiedlich ist.

Um dem Dialogfeld eine Tastatur hinzuzufügen, müssen Sie in der Methode messages.send den Tastaturparameter angeben, der json akzeptiert. Es sieht so aus:

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

Oder Sie können die Tastatur direkt aus der .json-Datei übergeben:

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

Dokumentation: vk.com/dev/bots_docs_3?f=4.%2BKeyboard%2Bfor%2Bworks

Betrachten Sie das Beispiel unseres Programms, indem Sie eine Tastatur hinzufügen.

Erstellen Sie zunächst eine Datei 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" }] ] } 

Um die Tastatur zu entfernen, müssen Sie json mit leeren Tasten übergeben:

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

Überschreiben Sie send_message in 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()) 

Und auch in der Startmethode:

 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)) 

Als Ergebnis erhalten wir:



Letztes Wort


Sie sollten nicht die bloße Auflistung der hier dargestellten Quellcodes verwenden, sie werden nur verwendet, damit Sie besser verstehen, was unter der Haube passiert. Natürlich sind sie alle verwendbar und können in Teilen verwendet werden.

Persönlich habe ich einen solchen Bot für einen Gruppenassistenten verwendet, der wusste wie:

  • Erstellen Sie Warteschlangen von Gruppenmitgliedern, einschließlich vieler Teams, die die Warteschlange bearbeitet haben, z. B. Hinzufügen, Löschen, Erstellen usw.
  • Senden Sie Nachrichten an alle Teilnehmer
  • gestellte Fragen (zum Beispiel in Java)
  • ermöglichte es, Anwendungen für den Austausch von Orten usw. zu erstellen.

Github-Projekt
Hier vorgestellte Quellen

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


All Articles