コヌルバックAPI䞊のPythonでのVK甹Chatbot

チャットボットはすでに非垞に䞀般的になっおおり、毎日すべおのむンスタントメッセンゞャヌに衚瀺されたす。



この蚘事では、䞀連の単玔なコマンドを䜿甚しおボットを䜜成する方法を段階的に芋お、将来的にその機胜を拡匵する方法を芋぀けたす。 この蚘事は、チャットボットを䜜成したこずのないほずんどの初心者に圹立ちたす。



ボットを䜜成したいずきは、VKontakteで䜿甚可胜なボットの䟋を怜蚎し、その構造を最倧限に単玔化するこずを詊みたした。



ボットを䜜成するために、Python 3.53番目のpythonの他のバヌゞョンがおそらく適切ですず远加のFlaskおよびVKラむブラリを䜿甚したした。 それらをむンストヌルする必芁がありたす。 Flaskのむンストヌルに関するロシア語の蚘事が倚数ありたす。 Pycharmをお持ちの堎合は、おそらくPycharmず共にむンストヌルされおいたす。



API自䜓から始めたしょう。 ボットには、グルヌプメッセヌゞに䜿甚できるコヌルバックAPIを䜿甚したす。 たず、接続されたメッセヌゞを含むVKontakteグルヌプを䜜成するか、すでに持っおいる必芁がありたす。



[ コミュニティ管理] →[ APIの操䜜]セクションで、コミュニティメッセヌゞにアクセスできるキヌを䜜成する必芁がありたす。



画像






コヌルバックを䜿甚するには、APIからむベントのリク゚ストを受信し、それらを凊理し、レスポンスリク゚ストを送信するWebサヌバヌが必芁です。 ぀たり、送信されたリク゚ストにのみ応答し、送信する「りェブサむト」を䜜成したす。



pythonで蚘述しおいるため、䜿甚できる最も簡単なものはpythonのホスティングです。 Pythonの無料ホスティングを䜿甚したした。 そこで登録し、FlaskでPython 3.5甚のアプリケヌションを䜜成する必芁がありたすWebセクションで䜜成できたす。 初期ファむルが䜜成されたす



# A very simple Flask Hello World app for you to get started with... from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello from Flask!'
      
      





珟圚ファむルにある唯䞀の機胜は、登録時に指定されたアドレスでペヌゞを埋めるこずに責任がありたす。 username.pythonanywhere.comのブラりザニックネヌムを䜿甚にアクセスするず、「Hello from Flask」ずいうテキストのみが衚瀺されたす。



将来的には、コヌドはブロックで衚瀺され、ブロック党䜓の完了埌にチェックするこずが可胜になり、そのプロセスでコヌドが環境によっお゚ラヌずしおマヌクされる可胜性がありたす。 怖がらないで、ブロックを最埌たで終わらせたほうがいいです。



したがっお、 ブロック1です。

サむトに送信されたリク゚ストを凊理するには、ドキュメントの最埌に次のコヌドを远加したす。



 @app.route('/', methods=['POST']) def processing(): return 'xxxxxxxx'
      
      





Xの代わりに、「サヌバヌが返す必芁のある文字列」を代入したす。 これは、コヌルバックAPIセクションのグルヌプ管理にリストされおいたす。



この機胜により、グルヌプに通知するためにサむトを接続できたす。



これで䜜業を確認できたす。 アプリケヌションを再起動するだけです。 サむトが新しいデヌタで䜜業を開始できるようにファむルを倉曎しお保存した埌のホスティングでは、[Web]タブでそれをリロヌドする必芁がありたす。 このコヌドを远加した埌、VKontakteグルヌプのサヌバヌアドレスバヌに察応するアドレスusername.pythonanywhere.comを入力し、[確認]をクリックしたす。



サヌバヌアドレスが正垞に接続されたこずを瀺す緑色の通知が衚瀺されたす。



[確認]をクリックするず、VKontakteはサヌバヌに接続し、それが実際にグルヌプ所有者に属しおいるこずを確認し、サヌバヌが芁求に応じお確認コヌドを返すのを「埅機」したす。



ブロック2

次のステップに進むこずができたす。 コミュニティに代わっおメッセヌゞを曞き蟌む機胜を远加したす。 ホスティングにVKラむブラリをむンストヌルしたす。 「コン゜ヌル」セクションで、bashコン゜ヌルを起動し、コマンドたたは遞択したバヌゞョンのpythonに適したコマンドを実行したす。



 pip3.5 install --user vk
      
      





モゞュヌルのむンストヌル方法に぀いおは、 ここで説明したす 。



受信リク゚ストを凊理するための関数のコヌドを倉曎したす。



 @app.route('/', methods=['POST']) def processing(): # json   POST- data = json.loads(request.data) #        if 'type' not in data.keys(): return 'not vk' if data['type'] == 'confirmation': return confirmation_token elif data['type'] == 'message_new': session = vk.Session() api = vk.API(session, v=5.0) user_id = data['object']['user_id'] api.messages.send(access_token=token, user_id=str(user_id), message=',   !') #   ,     return 'ok'
      
      





VKontakteサヌバヌには、凊理が成功したずいうメッセヌゞが必芁です。 ゚ラヌが発生した堎合、たたは他の回答が到着した堎合、サヌバヌは䞀定の間隔で凊理するたで着信メッセヌゞに関する通知を送信し続けたす。



新しいメッセヌゞに぀いお通知する着信芁求の構造は次のずおりです。



 {"type":"message_new","object":{"id":43, "date":1492522323, "out":0, "user_id":xxxxxxxx, "read_state":0, "title":" ... ", "body":""}, "group_id":xxxxxxxxxxx}
      
      





Vkontakteは、サむトに3぀のオブゞェクト「type」、「object」、「group_id」を枡し、メッセヌゞに関する情報は「object」内に保存されたす。



すべおのリク゚ストはVKontakteのドキュメントに蚘茉されおいたす



たた、新しい「むンポヌト」をファむルの先頭に远加したす。



 from flask import Flask, request, json from settings import * import vk
      
      





同じsettings.pyフォルダヌに新しいファむルを䜜成し、そこに必芁なログむンデヌタを保存したす。



 token = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' confirmation_token = 'xxxxxxxx'
      
      





それらはトヌクンに眮き換える必芁がありたす。 最初は蚘事の冒頭で䜜成し、2番目はグルヌプをサヌバヌに接続するための確認コヌドです。



これでボットは着信メッセヌゞに挚拶し、コヌドを䞎えたグルヌプに属しおいるこずを確認できたす。



これを確認しおメッ​​セヌゞを曞き蟌むこずができたす。CallbackAPIセクションのグルヌプ蚭定で着信メッセヌゞに関する通知を有効にするだけです。



ボットがメッセヌゞを送信するには、アプリケヌションを再起動する必芁がありたす。 その埌、ボットに再床曞き蟌み、すべおが正垞に行われた堎合、次のステップに進みたす。



ブロック3

すべおがうたくいき、メッセヌゞに応じおボットがあなたに挚拶した堎合、次のステップに進みたす。 vkラむブラリずのすべおの察話を別のファむルに入れたした。vkapiず呌びたす。



 import vk session = vk.Session() api = vk.API(session, v=5.0) def send_message(user_id, token, message, attachment=""): api.messages.send(access_token=token, user_id=str(user_id), message=message, attachment=attachment)
      
      





VKontakteセッションの関数ず初期化は1぀だけですが、他のセッションを远加したす。 朜圚的に、関数は添付ファむルを送信するこずもできたす。 埌でこの機䌚を利甚したす。



次に、メッセヌゞハンドラファむルを開始したす。 着信メッセヌゞを凊理し、衚瀺されたずきに適切なコマンドを決定し、必芁な回答を発行したす。



ファむル「messageHandler.py」



 import vkapi def get_answer(body): message = ",   !" return message def create_answer(data, token): user_id = data['user_id'] message = get_answer(data['body'].lower()) vkapi.send_message(user_id, token, message)
      
      





新しいファむルをメむンのファむルに接続したす。 メむンファむルのリク゚スト凊理関数を倉曎したす。



 @app.route('/', methods=['POST']) def processing(): data = json.loads(request.data) if 'type' not in data.keys(): return 'not vk' if data['type'] == 'confirmation': return confirmation_token elif data['type'] == 'message_new': messageHandler.create_answer(data['object'], token) return 'ok'
      
      





そしお、適切なむンポヌトをファむルの先頭に远加したす。



 import messageHandler
      
      





アプリケヌションを再起動するこずで、実行したこずを確認できたす。



ブロック4

チヌムの䜜成を始めたしょう。 コマンドのクラスを䜜成したす。



ファむル「command_system.py」



 command_list = [] class Command: def __init__(self): self.__keys = [] self.description = '' command_list.append(self) @property def keys(self): return self.__keys @keys.setter def keys(self, mas): for k in mas: self.__keys.append(k.lower()) def process(self): pass
      
      





クラスにはkeysプロパティがあり、このコマンドにアクセスできるキヌが保存されたす。 プロパティを蚭定するずき、すべおのキヌは小文字で保存されたす。たた、コマンドコヌルの成功にレゞスタが圱響しないように、小文字に倉換されたナヌザヌメッセヌゞず比范する必芁がありたす。



説明フィヌルドは、ボットコマンドに関する情報を発行するために䜿甚されたす。 プロセス関数が実行され、応答メッセヌゞが生成されたす。



初期化時にすべおのコマンドが保存される䞀般的なリストがありたす。 圌は教宀の倖にいたす。 このリストを䜿甚しお、ナヌザヌがメッセヌゞで芁求したコマンドを芋぀けたす。



それでは、ボット甚のチヌムをいく぀か䜜成したしょう。 ロヌドの䟿宜䞊、コマンドを初期化するファむルを「commands」フォルダヌに配眮したす。



耇数のファむルを䜜成したすが、コマンドを1぀のファむルに配眮するこずもできたす



「Hello.py」

 import command_system def hello(): message = ', !\n  -.' return message, '' hello_command = command_system.Command() hello_command.keys = ['', 'hello', '', '', ''] hello_command.description = ' ' hello_command.process = hello
      
      





「Cat.py」



 import command_system import vkapi import settings def cat(): #      attachment = vkapi.get_random_wall_picture(-32015300, settings.access_token) message = '   :)\n      .' return message, attachment cat_command = command_system.Command() cat_command.keys = ['', '', '', '', '', 'cat'] cat_command.description = '   ' cat_command.process = cat
      
      





猫を送るチヌムには、グルヌプたたはナヌザヌの壁からランダムな画像を返す「vkapi」ファむルの新しいトヌクンず新しい関数が必芁です。 この堎合、私たちは猫ず䞀緒に公衆の壁からランダムな写真を受け取りたす。



トヌクンを取埗するこずから始めたしょう。 サヌビスアクセスキヌが必芁です。 これを行うには、新しいスタンドアロンアプリケヌションを䜜成したす。 参照によっお䜜成できたす。 次に、アプリケヌションを䜜成したら、その蚭定に移動しお、[サヌビスアクセスキヌ]フィヌルドの内容をコピヌする必芁がありたす。

これをトヌクンファむルに远加する必芁がありたす。

「Settings.py」

 token = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' confirmation_token = 'xxxxxxxx' access_token = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
      
      







それでは、新しいvkapiメ゜ッドの䜜成に移りたしょう。 ここでは、䜿甚するAPIメ゜ッドの範囲を少し拡倧したす。



このメ゜ッドは次のようになりたす。



 def get_random_wall_picture(group_id, token): max_num = api.photos.get(owner_id=group_id, album_id='wall', count=0, access_token=token)['count'] num = random.randint(1, max_num) photo = api.photos.get(owner_id=str(group_id), album_id='wall', count=1, offset=num, access_token=token)['items'][0]['id'] attachment = 'photo' + str(group_id) + '_' + str(photo) return attachment
      
      





「vkapi」ファむルに远加したす。 たた、必芁なむンポヌトを「vkapi」ファむルの先頭に远加したす。



 import random
      
      





そしお最埌のチヌム



「Info.py」

 import command_system def info(): message = '' for c in command_system.command_list: message += c.keys[0] + ' - ' + c.description + '\n' return message, '' info_command = command_system.Command() info_command.keys = ['', '', 'help'] info_command.desciption = '  ' info_command.process = info
      
      





最終的なファむル階局



画像

botFlaskは、着信芁求を受け入れるメむンファむルです。



コマンドの説明が終わったので、コマンドシヌトがいっぱいになっおいるこずを確認する必芁がありたす。たた、command_listは特定のコマンドでファむルを起動したずきにのみ入力されるため、ナヌザヌがアクセスしたコマンドを理解できたす。



ボットの起動時に、「commands」フォルダヌからすべおのファむルを自動的に起動したす。



これを行うには、messageHandler.pyファむルに関数を远加したす。



 def load_modules(): #    ,       files = os.listdir("mysite/commands") modules = filter(lambda x: x.endswith('.py'), files) for m in modules: importlib.import_module("commands." + m[0:-3])
      
      





この関数では、コマンドを䜿甚しおディレクトリからファむルのリストをロヌドし、Pythonファむルのみをフィルタヌ凊理しおプログラムにむンポヌトしたす。これにより、リストにコマンドが入力されたす。



この関数の呌び出しがcreate_answerに远加されたす。 次に、察応する応答を呌び出すようにget_answer関数を倉曎したしょう。



ファむルの最終圢匏



 import vkapi import os import importlib from command_system import command_list def load_modules(): #    ,       files = os.listdir("mysite/commands") modules = filter(lambda x: x.endswith('.py'), files) for m in modules: importlib.import_module("commands." + m[0:-3]) def get_answer(body): #        message = ",   .  '',    " attachment = '' for c in command_list: if body in c.keys: message, attachment = c.process() return message, attachment def create_answer(data, token): load_modules() user_id = data['user_id'] message, attachment = get_answer(data['body'].lower()) vkapi.send_message(user_id, token, message, attachment)
      
      





ボットの準備ができたした これで、ボットのベヌスを䜜成し、そのための新しいチヌムを远加する方法がわかりたした。



ブロック5

蚘事の残りの郚分では、必芁だず思う1぀の改善に぀いお説明したす。 ただし、ボットはそれなしで動䜜したす。



おおよそのコマンド認識



ナヌザヌが1぀の文字を間違えた堎合、最も類䌌したコマンドを念頭に眮いおいる可胜性が高いです。 したがっお、ボットがただ答えを出し、「私はあなたを理解しおいたせん」ず蚀わなかったらいいでしょう。



おおよその認識のために、Damerau-Levenshtein距離を䜿甚したす。 文字を削陀、挿入、眮換、移動する操䜜が、ある行から別の行に移動できる回数を瀺しおいたす。



この距離を芋぀けるためのアルゎリズムは、䟋えばりィキペディアで説明されおいたす。



関数をmessageHandler.pyファむルに远加したす。



 def damerau_levenshtein_distance(s1, s2): d = {} lenstr1 = len(s1) lenstr2 = len(s2) for i in range(-1, lenstr1 + 1): d[(i, -1)] = i + 1 for j in range(-1, lenstr2 + 1): d[(-1, j)] = j + 1 for i in range(lenstr1): for j in range(lenstr2): if s1[i] == s2[j]: cost = 0 else: cost = 1 d[(i, j)] = min( d[(i - 1, j)] + 1, # deletion d[(i, j - 1)] + 1, # insertion d[(i - 1, j - 1)] + cost, # substitution ) if i and j and s1[i] == s2[j - 1] and s1[i - 1] == s2[j]: d[(i, j)] = min(d[(i, j)], d[i - 2, j - 2] + cost) # transposition return d[lenstr1 - 1, lenstr2 - 1]
      
      





この距離を芋぀けるためのアルゎリズムが実装されおおり、必芁に応じお倉曎たたは改善できたす。



これらの行によるず、1぀を別の倀に倉換するための操䜜の数が䞎えられたす。 get_answerメ゜ッドを倉曎したす。



 def get_answer(body): message = ",   .  '',    " attachment = '' distance = len(body) command = None key = '' for c in command_list: for k in c.keys: d = damerau_levenshtein_distance(body, k) if d < distance: distance = d command = c key = k if distance == 0: message, attachment = c.process() return message, attachment if distance < len(body)*0.4: message, attachment = command.process() message = '     "%s"\n\n' % key + message return message, attachment
      
      





この関数では、メッセヌゞず各キヌの距離を蚈算したす。 䞀臎が䞍正確な堎合、送信された各コマンドをボットがどのように認識したかを蚘述したす。 距離が送信されたメッセヌゞの長さの40を超えた堎合、ナヌザヌが誀解しすぎおいるず芋なし、デフォルトメッセヌゞを返したす。



それだけです。この蚘事を曞いおいる時点で動䜜するコヌドはgithubに投皿されおいたす。



VK甚の独自のボットを䜜成するこずに決めた堎合、この蚘事があなたの人生を少し楜にするこずを願っおいたす。



All Articles