Assistente pessoal para Whatsapp
Quem nunca quis ter um assistente pessoal? eu desenvolvi um chatbot que roda no Whatsapp com algumas funções bem legais e aqui vou ensinar como você pode fazer o seu próprio
Nesse artigo irei mostrar como desenvolvi o código explicando as partes que considero mais importantes para o entendimento por completo. É em geral uma aplicação simples, principalmente por ser escrita em Python❤ e não demorou mais de 2 dias para ser construída por completo. Claro que tem muito espaço para melhorias e sinta-se a vontade para enviar pull requests.
Quero começar dizendo que eu sempre quis ter um assistente pessoal, igualzinho o Jarvis do Iron man ou a TARS do Interstellar, mas por enquanto posso-me contentar com um robozinho inteligente ligado ao meu WhatsApp (Mas quem sabe daqui um tempo não aparece um texto aqui meu explicando como criei um robô que controla minha armadura de batalha).
Usei como base esse projeto <ChatBot no WhatsApp com Python> caso tenha alguma dúvida referente ao Web Scrapping acesse o link que lá ele explica muito bem como foi feita a parte de extração dos dados do WhatsApp, aqui irei mostrar como adicionei diversas funções e criei um assistente que além de aprender consegue trazer conteúdos da internet.
O que ele pode fazer
O assistente tem como objetivo auxiliar em algumas atividades básicas do cotidiano, como por exemplo:
- Mostrar o clima
- Fazer pesquisas na Wikipédia
- Trazer as notícias de tecnologia
- Trazer vídeos do YouTube
- Analisar fotos enviadas
Vamos para o código
No seu terminal:
# Instalando as bibliotecas chatterbot e selenium
pip install selenium
pip install chatterbot# Instalando as bibliotecas para as APIs
pip install wikipedia
pip install cognitive_face
Classe Bot
A classe Bot serve para nos comunicarmos com o usuário, ela não é acessada diretamente por ser uma classe abstrata. A maior parte dos métodos dessa classe estão fazendo o Web Scrapping para pegar os dados do Whatsapp, irei comentar de alguns que acho bom frisar:
Aqui por exemplo iniciamos o webdriver para poder acessar o Chrome
def __init__(self, config):
self.config = config
self.nome = self.config['BOT']['nome']
self.bot = ChatBot(self.nome)
self.bot.set_trainer(ListTrainer)
# Caminho para onde esta o arquivo do driver para o selenium
self.chrome = self.config['BOT']['path_driver'] + '\chromedriver.exe'
self.options = webdriver.ChromeOptions()
self.options.add_argument(r"user-data-dir=" + self.config['BOT']['path_driver'] + "\profileGoogle\wpp")
self.driver = webdriver.Chrome(self.chrome, chrome_options=self.options)
# Instanciando o Bot Microsoft
self.microsoft = BotMicrosoft()
Para saber o que digitamos o assistente possui o método escutar(), e nele que dizemos qual a class deve ser utilizada no css na hora da extração
# Pega o texto que o usuário enviou
def escuta(self):
post = self.driver.find_elements_by_class_name('_3_7SH')
ultimo = len(post) - 1
texto = post[ultimo].find_element_by_css_selector('span.selectable-text').text
return texto
Bem parecido com o método acima é o método CapturarImagem(), a única diferença é que caso o item enviado seja uma imagem iremos salvar ela na pasta imagens e depois convertar para o formato .jpg para facilitar a análise. Falando em análise podemos ver ali a chamada do indentificarFaceImagem() e como retorno teremos as informações que explicaremos mais a frente na classe BotMicrosoft, por fim enviamos a resposta ao usuário com o escreveNaTela().
# Salva a imagem na pasta imagens para ela ser analizada
def CapturarImagem(self):
post = self.driver.find_elements_by_class_name('_3v3PK')
ultimo = len(post) - 1
self.escreveNaTela("Analisando imagem...")
idImagem = randrange(9999)
for element in post[ultimo].find_elements_by_tag_name('img'):
print("Tetando pegar o SRC")
try:
nomeImagem = "screenshot{}".format(idImagem)
element.screenshot("imagens/{}.png".format(nomeImagem))
im = Image.open("imagens/{}.png".format(nomeImagem))
rgb_im = im.convert('RGB')
rgb_im.save('imagens/convertidas/{}.jpg'.format(nomeImagem))
self.escreveNaTela("Análise concluida")
#Chama a função para indentificar as caracteristicas no rosto
for response in self.microsoft.indentificarFaceImagem('imagens\\convertidas\\{}.jpg'.format(nomeImagem), self.config['MICROSOFT']['key'], self.config['MICROSOFT']['location']):
self.escreveNaTela(response)
except:
self.escreveNaTela("Não foi possível análisar a imagem, tente novamente mais tarde")
Para ensinar algo novo para o seu assistente, final sempre é bom conhecer algo novo(ainda mais se você é uma máquina que acabou de “nascer”), o método aprenderConversa() tem essa função ele vai receber a seguint entrada:
/aprenda pergunta ? resposta
/aprenda Posso comer pizza com ketchup ? Claro, pizza com ketchup é uma delícia
Ele vai aprender a responder a pergunta “Posso comer pizza com ketchup?” com “Claro, pizza com ketchup é uma delícia”
# Para ensinar o bot uma nova frase
def aprenderConversa(self, texto):
if texto.find('?') != -1:
ultimo_texto = texto
texto = texto.replace(':', '')
texto = texto.lower()
texto = texto.replace('?', '?*')
texto = texto.split('*')
novo = []
for elemento in texto:
elemento = elemento.strip()
novo.append(elemento.replace('?', ''))
with codecs.open('treino/treinar.txt', 'a', 'utf-8') as arq:
arq.write('\n')
arq.write(novo[0])
arq.write('\n')
arq.write(novo[1])
arq.close()
self.bot.train(novo)
self.caixa_de_mensagem.send_keys('*{}*: Aprendi = {}'.format(self.nome,texto))
time.sleep(1)
self.botao_enviar = self.driver.find_element_by_class_name('_35EW6')
self.botao_enviar.click()
else:
self.caixa_de_mensagem.send_keys("*{}*: '{}' não está no formato certo pergunta?resposta".format(self.nome,texto))
time.sleep(1)
self.botao_enviar = self.driver.find_element_by_class_name('_35EW6')
self.botao_enviar.click()
return ultimo_texto
Classe Bot Search
É nela que são feitas as consultas as APIs, essa classe herda todas as informações de Bot para poder se comunicar com o usuário, a seguir mostro os métodos dessa classe:
Buscar por notícias:
O que passamos como parâmetro aqui é o termo de pesquisa, todos os termos devem ter relação com tecnologia para funcionar, caso queira artigos referentes a outros assuntos basta alterar o category na URL de pesquisa.
def PesquisarNoticias(self, keyword):
try:
if(keyword == None):
req = requests.get('https://newsapi.org/v2/top-headlines?country=br&category=technology&pageSize=5&apiKey=f6fdb7cb0f2a497d92dbe719a29b197f')
noticias = json.loads(req.text)
else:
req = requests.get('https://newsapi.org/v2/top-headlines?q={}&country=br&category=technology&pageSize=5&apiKey=f6fdb7cb0f2a497d92dbe719a29b197f'.format(keyword))
noticias = json.loads(req.text)
if(noticias["totalResults"] != 0):
for news in noticias['articles']:
titulo = news['title']
link = news['url']
new = '*'+ self.nome +'*: ' + titulo + ' ' + link + '\n'
self.caixa_de_mensagem.send_keys(new)
time.sleep(1)
else:
self.escreveNaTela("*{}*: Não encontrei nada, tem certeza que o termo tem relação com tecnologia?".format(self.nome))
except:
pesquisa = "Termo {} não encontrado, tente outro".format(keyword)
self.caixa_de_mensagem.send_keys('*' + self.nome + '*:' + pesquisa)
self.botao_enviar = self.driver.find_element_by_class_name('_35EW6')
self.botao_enviar.click()
Buscar na Wikipédia :
O que passamos como parâmetro aqui é o termo de pesquisa, caso tenha um resultado correspondente ele retornará
def PesquisarWikipedia(self, keyword):
try:
wikipedia.set_lang("pt") # Defina antes
pesquisa = wikipedia.summary(keyword, sentences=5)
wikiaprenda = [keyword, str(pesquisa)]
self.bot.train(wikiaprenda)
except:
pesquisa = 'Termo não encontrado, tente outro'
self.caixa_de_mensagem.send_keys('*{}*:{}'.format(self.nome,pesquisa))
self.botao_enviar = self.driver.find_element_by_class_name('_35EW6')
self.botao_enviar.click()
Buscar no Youtube:
O que passamos como parâmetro aqui é o termo de pesquisa do vídeo
def PesquisaYoutube(self, keyword):
try:
if (keyword == None):
videos = "Insira um termo para pesquisa"
else:
videos = requests.get(
'https://www.googleapis.com/youtube/v3/search?part=id%2Csnippet&q={}&type=video&order=relevance&chart=mostPopular&locale=br&maxResults=5®ionCode=br&key={}'.format(
keyword,self.config['KEY']['youtube']))
json_videos = videos.json()
videos = "*Vídeos*\n"
for i in range(len(json_videos["items"])):
try:
nome = json_videos["items"][i]["snippet"]["title"]
link = json_videos["items"][i]["id"]["videoId"]
videos += "{} - https://www.youtube.com/watch?v={}\n".format(nome, link)
except:
videos += "{}\n".format(nome)
self.caixa_de_mensagem.send_keys(videos)
time.sleep(1)
except:
videos = 'Houve um problema, tente mais tarde'
self.escreveNaTela(videos)
Buscar pelo clima em São Paulo:
def PesquisaClima(self):
try:
weather = requests.get('http://apiadvisor.climatempo.com.br/api/v1/weather/locale/3477/current?token=42e95b3445df96d67215946ab6ce301c')
json_weather1 = weather.json()
weatherText = "\n======Clima em {}/{} ======\n".format(json_weather1["name"], json_weather1["state"])
weatherText += "*Condição* = {}\n".format(json_weather1["data"]["condition"])
weatherText += "*Temperature* = {}ºC\n".format(json_weather1["data"]["temperature"])
weatherText += "*Umidade* = {}%\n".format(json_weather1["data"]["humidity"])
weatherText += "*Espero ter ajudado*"
except:
weatherText = 'Houve um problema, tente mais tarde'
self.escreveNaTela(weatherText)
Classe Bot Microsoft
Aqui eu faço o uso dos serviços cognitivos do Microsoft Azure, em específico estou utilizando o cognitive face para poder trazer as informações de
- Chances da pessoa estar sorrindo(%)
- Se a pessoa está usando óculos
- Sexo
- Idade
Temos que passar como parâmetro aqui a imagem e o location (para mais informações acessar a documentação)
import cognitive_face as CF
class BotMicrosoft():
def indentificarFaceImagem(self,imgURL, key, location):
CF.Key.set(key)
BASE_URL = 'https://{}.api.cognitive.microsoft.com/face/v1.0/detect?returnFaceId=true&returnFaceLandmarks=false&returnFaceAttributes=age,gender,smile,glasses,emotion,hair&recognitionModel=recognition_01&returnRecognitionModel=true'.format(location) # Replace with your regional Base URL
CF.BaseUrl.set(BASE_URL)
img_url = '{}'.format(imgURL)
result = CF.face.detect(img_url)
result = result[0]['faceAttributes']
response = ["{}% de chances de você estar sorrindo".format((result['smile'] * 100))]
if (result['gender'] == 'male'):
response.append("Você é do sexo Masculino")
elif (result['gender'] == 'female'):
response.append("Você é do sexo Feminino")
if (result['glasses'] == 'NoGlasses'):
response.append("Não usa óculos")
else:
response.append("Usa óculos")
response.append("Tem por volta de {} anos".format(result['age']))
return response
O Main
Chegamos aonde a mágica acontece, é nesse arquivo que vamos executar o assistente.
Nessa primeira parte ele busca as informações no arquivo config.json e passa como parâmetro na hora de instanciar o objeto bot. Vemos o método iniciar recebendo o usuário o qual o assistente vai conversar, que pode ser você mesmo ou um grupo (fico até com dó dele quando está em um grupo com muitas pessoas), após iniciar ele envia a primeira mensagem explicando como deve ser feito para ser entendido pelo assistente.
# Buscando arquivos de configuração
with open('config.json', 'r') as f:
config = json.load(f)
# Instanciando o bot
bot = BotSearch(config)
# Passando o arquivo para treino
bot.treina(config['BOT']['treino'])
# Selecionando a pessoa que ele vai pesquisar no WhatsApp
bot.inicia(config['BOT']['usuario'])
# Dizendo o primeiro 'Oi'
bot.saudacao(
['*{}*: Oi, sou o {}! Estou disponível para ajuda-los'.format(config['BOT']['nome'], config['BOT']['nome']),
'Para falar com o {} */texto*'.format(config['BOT']['nome']),
'Para pesquisar notícias de tecnologia */news* ou *:news termo de tecnologia*',
'Para pesquisa na wikipédia */wiki texto*',
'Para o {} aprender algo */aprenda pergunta?resposta*'.format(config['BOT']['nome']),
'Para pesquisar o clima escreva */clima*',
'Para pesquisar vídeos no youtube */youtube* ou */youtube termo pesquisa*'])
# bot.saudacao(['*{}*: Oi, sou o SIBot! Estou disponível para ajuda-los'.format(nomebot)])
ultimo_texto = ''
Em seguida vou entrar em um While para que ele fique o tempo inteiro “lendo” o que o usuário enviar. Vários if’s podem ser vistos a frente(alguns até brincam que I.A não passe de muitos if’s haha), caso ele uma das palavras chave: aprenda, news, youtube, wiki ou clima será direcionado ao método expecífico.
while True:
#Iniciando o processo de captar mensagens do whatsaap
try:
# Capturando textos digitados
texto = bot.escuta()
except:
# Capturando imagens para análise
bot.CapturarImagem()
if texto != ultimo_texto and re.match(r'^/', texto):
ultimo_texto = texto
texto = texto.replace('/', '')
texto = texto.lower()
if (re.match(r'^ aprenda', texto) or re.match(r'^aprenda', texto)):
texto = texto.replace('aprenda', '')
ultimo_texto = bot.aprenderConversa(texto)
elif (texto == 'news' or texto == ' news' or re.match(r'^news', texto) or re.match(r'^ news', texto)):
if(texto == 'news'):
bot.PesquisarNoticias(None)
else:
texto = texto.replace('news', '')
bot.PesquisarNoticias(texto)
elif (re.match(r'^ wiki', texto) or re.match(r'^wiki', texto)):
keyword = texto.replace('wiki', '')
bot.PesquisarWikipedia(keyword)
elif(texto == "clima" or re.match(r'^ clima', texto) or re.match(r'^clima', texto)):
bot.PesquisaClima()
elif (texto == "youtube" or re.match(r'^ youtube', texto) or re.match(r'^youtube', texto)):
keyword = texto.replace('youtube', '')
bot.PesquisaYoutube(keyword)
else:
bot.responde(texto)
Arquivo config.json
É nele que encontramos as variáveis para o nosso assistente, aqui temos a explicação e um exemplo de como configurar ele
{
"BOT": {
"nome": "Nome do robô",
"treino": "Arquivo para treino",
"path_driver": "driver",
"usuario": "Nome do usuário com quem quer conversar"
},
"KEY":{
"youtube": "Chave para usar a API do youtube"
},
"MICROSOFT": {
"key": "Chave para usar a API da Microsoft",
"location": "location da API da Microsoft, consulte a documentação deles"
}
}
Exemplo:
{
"BOT": {
"nome": "Bot",
"treino": "treinar.txt",
"path_driver": "driver",
"usuario": "Jean"
},
"KEY":{
"youtube": "EGeaSyBWTr6gXzZxJUEyva2Rh7IdSmv-jX8IWvw"
},
"MICROSOFT": {
"key": "992ac345681dcb9c818720358e8d6b80",
"location": "westus"
}
}
Para acessar o projeto completo acesse o meu github: https://github.com/jjeanjacques10/chatbot-whatsapp
Concluindo
Primeiramente queria dizer que ainda estou aprendendo e por isso posso ter cometido alguns erros no código ou nas explicações, não sei tudo (acho que ninguém no mundo sabe), mas dei o meu melhor para trazer um bom conteúdo para vocês.
Obrigado por ler o meu primeiro artigo aqui no medium, fiquei muito animado desenvolvendo esse assistente e testando ele no grupo da sala da faculdade (e desculpa para aqueles que chegaram sem saber o que estava acontecendo e mais de 1000 novas mensagens no grupo)
Espero que tenha gostado.
Caso tenha alguma crítica, sugestão ou dúvida fique a vontade para me enviar uma mensagem:
Linkedin: https://www.linkedin.com/in/jjean-jacques10/
Até a próxima!