Membuat dan mengintegrasikan VK bot ke grup melalui VkBotLongPoll [Python]

Pada artikel ini, kita akan membuat bot dan mengintegrasikannya ke dalam grup VK di Python 3.x

Untuk siapa artikel ini?


Bagi mereka yang ingin menulis bot sederhana untuk komunitas mereka, dapat mengidentifikasi tim dan menampilkan jawaban yang sesuai

Tahap utama



Membuat grup bot


Kami akan mulai dengan membuat bot, yaitu grup di VK.

Untuk melakukan ini, buka "grup" โ†’ "buat komunitas."

Pilih semua jenis komunitas dan masukkan nama, tema grup.

Pada halaman pengaturan yang terbuka, pilih "Bekerja dengan API."

Selanjutnya, Anda perlu membuat kunci API.

Kemudian pilih parameter yang Anda butuhkan dengan akses untuk kunci API Anda.

Kemungkinan besar, Anda harus mengkonfirmasi tindakan dalam VK menggunakan ponsel Anda. Kemudian salin kunci API yang dihasilkan di suatu tempat ke file. Kami masih membutuhkannya.

Maka Anda perlu mengizinkan pesan. Untuk melakukan ini, buka "pesan" dan nyalakan. Aktifkan juga "Fitur Bot" di "Pesan" -> "Pengaturan untuk bot."

Di sana kami akan mengizinkan menambahkan komunitas ke grup jika kami ingin bot dapat menerima pesan dari grup.

Menyiapkan Poll Panjang


Untuk bekerja dengan Long Poll API, kami menggunakan pustaka vk_api . Anda dapat menginstalnya melalui pip.

Sebelum bekerja, kami akan menyimpan token API kami di file config.py, dari sana kami akan memuat kunci kami.

Mari kita buat skrip pertama kami. Beri nama server.py, yang akan menjadi skrip server utama.

Kami mengimpor modul yang kami butuhkan:

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

Buat kelas server:

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

Sekarang buat file server_manager.py, di mana ia akan mengelola server yang berbeda. Untuk saat ini, untuk pengujian, kami hanya menulis panggilan ke kelas 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() 

Penting!

Bot hanya dapat menulis pesan kepada pengguna yang telah mengizinkan bot mengirim pesan. Anda dapat melakukan ini di halaman komunitas atau menulis bot terlebih dahulu

Jika semuanya dilakukan dengan benar, bot akan mengirimkan kami pesan pribadi.

Sekarang tambahkan bot ke grup dan ajari dia cara memproses pesan.
Untuk menambahkan bot ke grup, klik "Undang ke percakapan" di menu kanan komunitas.

Tambahkan fungsi mulai ke bot, setelah itu akan mulai "mendengarkan" pesan melalui Long Poll (jangan lupa untuk menambahkan izin ke jenis acara di bagian "Bekerja dengan API" -> "Long Poll API" dan meletakkan versi terbaru):

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

Jalankan melalui server_manager.py:

 server1.start() 

Sekarang, jika kita menulis pesan ke grup, kita dapat melihat objek acara:
<< 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 di da Vk] ini adalah teks!', 'conversation_message_id': 187, 'fwd_messages': [], 'penting': Salah, 'random_id': 0 , 'lampiran': [], 'is_hidden': False}, 'group_id': 172998024})>

Juga, jika kita menulis dalam pesan pribadi:
<< class 'vk_api.bot_longpoll.VkBotMessageEvent'> ({'type': 'message_new', 'object': {'date': 1541273238, 'from_id': 25599999999, 'id': 47, 'out': 0, 'peer_id': 255396611, 'text': 'ini adalah pesan pribadi', 'conversation_message_id': 47, 'fwd_messages': [], 'penting': Salah, 'random_id': 0, 'lampiran': [], 'is_hidden ': False},' group_id ': 172998024})>


Dari data ini, kita harus memperhatikan jenis, object.from_id, object.id, object.peer_id, object.text . Data yang diterima dari pesan dan dari grup tidak berbeda jauh kecuali object.peer_id dan object.id .

Jika Anda perhatikan dengan seksama, object.id untuk semua pesan dari grup adalah 0, tetapi tidak ada pesan dari pesan pribadi. Dengan demikian, dimungkinkan untuk memisahkan pesan yang diterima dari grup dan dari pesan pribadi.

Kami memproses data yang diterima di dalam kelas 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'] 


Kami akan menulis dua pesan ke bot: satu dari grup, satu di PM. Lalu kita dapatkan:
Nama pengguna: Arthur
Dari: Saint Petersburg
Teks: [club172998024 | @ club172998024] ini adalah pesan dari grup
Ketik: pesan grup
-
Nama pengguna: Arthur
Dari: Saint Petersburg
Teks: ini adalah pesan pribadi
Ketik: pesan pribadi
-

Catatan


Seperti yang dapat Anda lihat sebelum pesan dalam grup ada [club172998024 | @ club172998024], agar tim dapat memproses dengan benar, Anda harus menyingkirkan semua konten dalam tanda kurung siku, atau mengizinkan bot mengakses semua korespondensi

Seperti yang bisa kita lihat, vk_api memungkinkan kita untuk dengan mudah menggunakan metode VK API. Misalnya, sekarang kami menggunakan metode users.get

Daftar semua metode tersedia di: vk.com/dev/methods

Saya menyarankan Anda untuk belajar dan bereksperimen dengan metode yang menarik minat Anda. Untungnya VK memberi kami dokumentasi yang sangat bagus, juga dalam bahasa Rusia.

Untuk mengkonsolidasikan materi, mari tambahkan fungsi untuk mengirim pesan melalui metode messages.send :

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

<peer_id> adalah pengidentifikasi tujuan. Untuk menanggapi pesan seseorang, kami akan menentukan event.object.peer_id sebagai parameter peer_id . Artinya, kami akan mengirim pesan ke tempat asal permintaan itu.

Ubah metode mulai:

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

Sekarang, jika bot menerima pesan, maka bot akan menanggapi kami dengan gaya ini:
Arthur, saya mendapat pesan Anda!

Seluruh kode

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


Tugas untuk memperbaiki materi:


Buat fungsi yang mengambil parameter peer_id dan mengirim pengguna foto yang diunggah ke komunitas. Dock yang Berguna: vk.com/dev/messages.send

Solusi
Pertama, unggah foto ke grup dan buka di VK, pertimbangkan tautannya:
vkcom / club172998024? z = foto-172998024_456239017 % 2Falbum-172998024_256250731

Kami hanya tertarik pada bagian yang disorot: foto-172998024_456239017 . Lulus sebagai argumen ke metode messages.send:

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

Tambahkan ke metode mulai dan dapatkan:



Itu semua dasar-dasarnya. Hal utama adalah mempelajari cara menggunakan vk_api menggunakan berbagai metode, seluruh daftar mereka adalah: vk.com/dev/methods . Jika Anda belajar cara bekerja dengan dokumentasi VK API, Anda dapat membuat bot dengan berbagai kompleksitas dan tujuan. Contoh bot saya untuk grup studi: github.com/AppLoidx/GroupAssistant/tree/master

Sekarang mari kita mulai membuat logika bot.


Buat commander.py, yang akan menerima perintah dan mengembalikan respons yang diteruskan ke pengguna 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 

Mari kita bangun arsitektur program kami:


Kami "mendengarkan" ke server Polling Panjang dan menerima pesan pengguna ->
Kami meneruskan pesan ke Commander.input () -> Tentukan mode -> Tentukan perintah ->
Kami mengembalikan jawabannya -> Kami mentransfer ke pengguna

Untuk mendefinisikan mode dan perintah, kita membuat dua file command_enum.py dan mode_enum.py. Dengan menggunakannya, kita akan mendefinisikan mode dan perintah melalui metode kelas 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 


Untuk mengubah mode, gunakan [slash ("/") + <mode_name>], dan kami akan menerima semua perintah lain sebagai perintah.

Kami menerapkan ini di 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) 


Semua kode tersedia di github: github.com/AppLoidx/VkLongPollBot

Tambahkan keyboard:


Ini adalah proses yang sangat mudah, komplikasi dapat disebabkan ketika kita mengubah keyboard ke tanda tangan perintah tertentu, yang berbeda untuk setiap mode.

Untuk menambahkan keyboard ke kotak dialog, Anda harus menentukan parameter keyboard yang menerima json di metode messages.send. Ini terlihat seperti ini:

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

Atau Anda dapat melewatkan keyboard langsung dari file .json:

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

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

Pertimbangkan contoh program kami dengan menambahkan keyboard.

Pertama, buat file 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" }] ] } 

Untuk menghapus keyboard, Anda harus memberikan json dengan tombol kosong:

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

Abaikan send_message di 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()) 

Dan juga dalam metode awal:

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

Sebagai hasilnya, kita mendapatkan:



Kata terakhir


Anda tidak boleh menggunakan daftar telanjang kode sumber yang disajikan di sini, mereka hanya digunakan sehingga Anda lebih memahami apa yang terjadi di bawah tenda. Tentu saja, semuanya dapat digunakan dan Anda dapat menggunakannya di bagian-bagian.

Secara pribadi, saya menggunakan bot seperti itu untuk asisten grup yang tahu caranya:

  • buat antrian dari anggota grup, termasuk banyak tim yang mengedit antrian, seperti menambah, menghapus, membuat, dll.
  • mengirim pesan ke semua peserta
  • pertanyaan yang diajukan (misalnya, di Jawa)
  • memungkinkan untuk membuat aplikasi untuk pertukaran tempat, dll.

Proyek Github
Sumber disajikan di sini

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


All Articles