Чат-боты стали уже очень распространенным явлением, и появляются во всех мессенджерах ежедневно.
В этой статье по шагам разберем создание бота с набором простых команд и узнаем, как в дальнейшем можно расширить его функционал. Статья будет полезна для самых новичков, которые никогда не пробовали создавать чат-ботов.
Когда мне захотелось создать бота, я изучил доступные примеры ботов для ВКонтакте и постарался достигнуть максимального упрощения их структуры.
Для создания бота я использовал Python 3.5 (вероятно, подойдут и другие версии 3-го питона) и дополнительные библиотеки Flask и VK. Их надо будет установить. По установке Flask есть много статей на русском. Если у вас стоит Pycharm, то он, скорее всего, установился вместе с ним.
Начнем с самого API. Для нашего бота будем использовать Callback API, доступный для сообщений групп. Прежде всего, нам нужно создать или уже иметь группу ВКонтакте с подключенными сообщениями.
В разделе управление сообществом > работа с API необходимо создать ключ с доступом к сообщениям сообщества.
# 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!'
@app.route('/', methods=['POST'])
def processing():
return 'xxxxxxxx'
pip3.5 install --user vk
@app.route('/', methods=['POST'])
def processing():
#Распаковываем json из пришедшего GET-запроса
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'
{"type":"message_new","object":{"id":43, "date":1492522323, "out":0, "user_id":xxxxxxxx, "read_state":0, "title":" ... ", "body":"помощь"}, "group_id":xxxxxxxxxxx}
from flask import Flask, request, json
from settings import *
import vk
token = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
confirmation_token = 'xxxxxxxx'
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)
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
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
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
import command_system
import vkapi
def cat():
# Получаем случайную картинку из паблика
attachment = vkapi.get_random_wall_picture(-32015300)
message = 'Вот тебе котик :)\nВ следующий раз я пришлю другого котика.'
return message, attachment
cat_command = command_system.Command()
cat_command.keys = ['котик', 'кошка', 'кот', 'котенок', 'котяра', 'cat']
cat_command.description = 'Пришлю картинку с котиком'
cat_command.process = cat
def get_random_wall_picture(group_id):
max_num = api.photos.get(owner_id=group_id, album_id='wall', count=0)['count']
num = random.randint(1, max_num)
photo = api.photos.get(owner_id=str(group_id), album_id='wall', count=1, offset=num)['items'][0]['id']
attachment = 'photo' + str(group_id) + '_' + str(photo)
return attachment
import random
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
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])
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)
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]
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
К сожалению, не доступен сервер mySQL