em Tecnologia

Passo a passo para começar um chatbot em Python

Este é um tutorial no estilo passo-a-passo para a criação de um chatbot simples para o Facebook Messenger. Ele foi escrito como conteúdo para uma pequena apresentação que farei em um evento interno de compartilhamento de conhecimento da TIKAL TECH, empresa em que trabalho.

Existem diversas maneiras de se criar um bot para o Messenger, mas neste tutorial eu utilizarei apenas o microframework para web escrito em Python chamado Flask e nada mais.

Esse tutorial resume algumas das coisas que aprendi durante o desenvolvimento de um outro projeto.

Passo 1

Criando um aplicativo e vinculando com uma fanpage

Acesse o dashboard do Facebook Developers e crie um aplicativo. No tipo de aplicativo escolha “Aplicativos para o Facebook Messenger”. Se você não tiver uma página para vincular o aplicativo, crie uma também.

Com a página criada, entre novamente no dashboard e gere um token para o aplicativo ser vinculado à página.

Passo 2

Começando a programar o backend

Vamos utilizar o microframework Flask para facilitar esse desenvolvimento, mas qualquer outro framework web pode ser utilizado.

Se você está acostumado a trabalhar com Python você pode pular alguns dos passos abaixo.

Vamos começar criando um ambiente virtual na linha de comando e instalando o Flask, o requests e o gunicorn. Precisaremos dos dois primeiros para nosso chatbot e o último será para o deploy.

Escolha o diretório que você vai utilizar e digite os seguintes comandos.

$ cd ~/projetos/chatbot/
$ mkvirtualenv chatbot
...

(chatbot)$ pip install Flask
(chatbot)$ pip install requests
(chatbot)$ pip install gunicorn
(chatbot)$ touch index.py

Agora temos um arquivo index.py no nosso diretório chatbot. É nele que faremos os próximos passos do nosso tutorial.

Começamos com o básico do Flask.

import os
from flask import Flask, request

token = os.environ.get('FB_ACCESS_TOKEN')
app = Flask(__name__)

@app.route('/', methods=['GET', 'POST'])
def webhook():
    return 'Nothing'

if __name__ == '__main__':
    app.run(debug=True)

Esse arquivo tem o básico do que precisamos para rodar um app no Flask.

O token que estamos pegando de uma variável de ambiente é o token que geramos no dashboard do Facebook.

Para adicioná-lo como variável de ambiente execute na linha de comando:

$ export FB_ACCESS_TOKEN=<cole o token gerado>

Passo 3

Configurando os Webhooks

Temos que configurar um webhook para que o Facebook saiba para onde enviar as requisições do chat.

Para isso utilizaremos o ngrok para “tunelar” nosso localhost. Para saber como funciona, veja esta publicação.

Vamos rodar nosso servidor local com o Flask. Na linha de comando com o virtualenv ativado, digite:

(chatbot)$ python index.py

Abra outra janela de terminal para iniciar o ngrok.

./ngrok http 5000

Agora temos um endereço web com HTTPS para cadastrar no dashboard do Facebook. Basta ir até o painel Webhooks e clicar em Configurar Webhooks.

Antes de cadastrar o endereço, é preciso adicionar uma parte a mais no nosso index.py. O bloco em negrito tem o que é preciso para que o Facebook aceite esse link como elegível para webhook.

def webhook():
    if request.method == 'POST':
        pass
    elif request.method == 'GET': # Para a verificação inicial
        if request.args.get('hub.verify_token') == os.environ.get('FB_VERIFY_TOKEN'):
            return request.args.get('hub.challenge')
        return "Wrong Verify Token"
    return "Nothing"

Coloque o endereço gerado pelo ngrok, por exemplo https://7f3o6bd7.ngrok.io, e crie uma frase de segurança. Essa frase colocamos na variável de ambiente FB_VERIFY_TOKEN que você pode ver no código acima.

(chatbot)$ export FB_VERIFY_TOKEN=<digite qualquer frase aqui>

Com isso já somos capazes de registrar nosso endereço do chatbot no Facebook.

Para finalizar, no mesmo painel de webhooks você deve “inscrever” sua página criada ao webhook adicionado. Essa parte é bem autoexplicativa.

Passo 4

Lendo a mensagem e respondendo

Agora vamos adicionar uma maneira de lermos o que o usuário nos enviou pelo Messenger. Isso é feito verificando os dados enviados na requisição que o chat nos envia via POST.

Então vamos adicionar ao nosso código o seguinte trecho:

import os
import requests
import traceback
import json

(...)

def webhook():
    if request.method == 'POST':
        try:
            data = json.loads(request.data.decode())
            text = data['entry'][0]['messaging'][0]['message']['text']
            sender = data['entry'][0]['messaging'][0]['sender']['id']
            payload = {'recipient': {'id': sender}, 'message': {'text': "Hello World"}}
            r = requests.post('https://graph.facebook.com/v2.6/me/messages/?access_token=' + token, json=payload)
        except Exception as e:
            print(traceback.format_exc())
    elif request.method == 'GET': # Para a verificação inicial
(...)

Explicando o que estamos fazendo. Adicionamos os imports necessários no topo do arquivo index.py e dentro do nosso método webhook verificamos se a requisição chegou como POST.

Com o data = json.loads(request.data.decode()) nós capturamos o corpo da mensagem que nos foi enviado pelo Messenger.

Com o trecho text = data['entry'][0]['messaging'][0]['message']['text']conseguimos pegar o texto que o usuário enviou via chat.

Pra saber quem enviou, usamos o trecho sender = data['entry'][0]['messaging'][0]['sender']['id']. Essa informação é necessária para que possamos enviar uma resposta para o usuário correto.

No trecho payload = {'recipient': {'id': sender}, 'message': {'text: "Hello World"}} nós montamos o corpo da nossa resposta ao usuário.

Para finalizar, respondemos enviando uma requisição para o endereço da API do Facebook Messenger com o corpo criado anteriormente. O trecho referente ao envio segue abaixo.

r = requests.post('https://graph.facebook.com/v2.6/me/messages/?access_token=' + token, json=payload)

Colocamos tudo isso dentro de um try/except para capturar um possível erro que imprimiremos no console. Isso fazemos com o trecho final do código que acabamos de adicionar.

except Exception as e:
    print(traceback.format_exc())

Com isso, respondemos um Hello World para cada entrada que o usuário nos enviar.

Passo 5

Tornando o chatbot em algo útil

Como responder Hello World para cada mensagem é algo totalmente inútil para um bot fazer, vamos colocar uma funcionalidade aproveitando a documentação do Messenger, que é bem completa.

Vamos pegar a localização do usuário e enviar informações sobre o clima atual em sua localidade.

Primeiro criamos uma função que monta um payload com formatoQuick Reply do Messenger. O formato que precisamos enviar para ter esse resultado é facilmente encontrado na documentação do Messenger. Nela definiremos que queremos um tipo location.

def location_quick_reply(sender):
    return {
        "recipient": {
            "id": sender
        },
        "message": {
            "text": "Compartilhe sua localização:",
            "quick_replies": [
                {
                    "content_type": "location",
                }
            ]
        }
    }

Agora, quando formos enviar uma mensagem para nosso usuário, utilizamos essa função no lugar do antigo payload.

payload = location_quick_reply(sender)

Isso vai enviar uma opção para o usuário nos enviar sua localização. Como mostra a imagem abaixo.

Vamos receber essa localização de maneira diferente do que um texto comum, ela virá como um anexo. Dessa forma mudamos nossa verificação de texto para o bloco abaixo.

if request.method == 'POST':
        try:
            data = json.loads(request.data.decode())
            print(data)
            message = data['entry'][0]['messaging'][0]['message']
            sender = data['entry'][0]['messaging'][0]['sender']['id'] # Sender ID

            if 'attachments' in message:
                if 'payload' in message['attachments'][0]:
                    if 'coordinates' in message['attachments'][0]['payload']:
                        location = message['attachments'][0]['payload']['coordinates']
                        latitude = location['lat']
                        longitude = location['long']
            else:
                text = message['text']
                payload = location_quick_reply(sender)
                r = requests.post('https://graph.facebook.com/v2.6/me/messages/?access_token=' + token,
                                  json=payload)

Nesse bloco verificamos se o corpo da requisição que o Messenger nos enviou possui uma chave attachments e se possui uma chave payload com as coordenadas da localização.

Se não vier nesse formato é porque ele enviou uma mensagem normal de texto, então reenviamos o botão que pede sua localização.

Agora que pegamos a latitude e longitude, podemos utilizar uma API aberta de clima para enviar as informações para nosso usuário.

Eu escolhi a OpenWeatherMap para isso. Basta se inscrever e gerar uma chave de API no site deles e você já pode começar a fazer requisições variadas para receber informações de clima do mundo inteiro.

Adicionei a chave de API deles numa variável de ambiente para utilizar nas requisições que farei dentro do código e pronto.

(chatbot)$ export WEATHER_API_KEY=<digite a api key>

Agora podemos acessar o endpoint que eles fornecem para current weathere pegar a temperatura e outros dados climáticos do local do usuário.

Logo após o trecho do código onde pegamos a latitude e longitude, adicionamos uma requisição para a API do OpenWeatherMap para montar a resposta que daremos ao nosso usuário.

api_key = os.environ.get('WEATHER_API_KEY')

(...)

url = 'http://api.openweathermap.org/data/2.5/weather?' \
      'lat={}&lon={}&appid={}&units={}&lang={}'.format(latitude, longitude, api_key, 'metric', 'pt')

r = requests.get(url)

description = r.json()['weather'][0]['description'].title()
icon = r.json()['weather'][0]['icon']
weather = r.json()['main']

text_res = '{}\n' \
           'Temperatura: {}\n' \
           'Pressão: {}\n' \
           'Humidade: {}\n' \
           'Máxima: {}\n' \
           'Mínima: {}'.format(description, weather['temp'], weather['pressure'], weather['humidity'], weather['temp_max'], weather['temp_min'])

payload = {'recipient': {'id': sender}, 'message': {'text': text_res}}

r = requests.post('https://graph.facebook.com/v2.6/me/messages/?access_token=' + token, json=payload)

Basicamente analisei a resposta da API do OpenWeatherMap e peguei as informações que gostaria de exibir ao usuário. Depois disso montei um bloco de texto simples exibindo algumas das informações.

Pronto! Agora temos uma funcionalidade para nosso bot experimental.

Passo 6

Fazendo o deploy

Vamos colocar nosso novo bot em um servidor para que ele fique disponível constantemente. Como eu falei anteriormente é preciso uma conexão segura (HTTPS) para que o Facebook aceite a URL do chatbot.

Com o Heroku é muito simples fazer o deploy. Basta que o seu código esteja no github, por exemplo, e com um clique ele fica online.

Para fazê-lo funcionar no Heroku é preciso adicionar dois novos arquivos ao nosso projeto. O Procfile e o runtime.txt. O primeiro configura o servidor web e o segundo define que estamos usando o Python 3.

Se você estiver utilizando a versão 2 do Python, o segundo arquivo é desnecessário.

O arquivo Procfile ficará assim:

web: gunicorn index:app

E o runtime.txt assim:

python-3.5.2

Criamos uma conta no Heroku e adicionamos um novo app. Em seguida vamos na aba Deploy e conectamos com o Github. Com isso basta escolher o seu repositório e clicar em Deploy Branch.

Abas do Dashboard do Heroku

Precisamos adicionar nossas variáveis de ambiente. Para isso devemos ir à aba Settings e adicionar manualmente por lá. É simples e autoexplicativo.

Agora testamos nossa URL, que será algo como https://<nome_do_app>.herokuapp.com.

Voltamos ao dashboard de developers do Facebook para mudar nosso webhook configurado. Lembra que fizemos isso com o endereço que o ngrok nos forneceu no passo três? Agora vamos mudar a URL para a nova que o Heroku nos forneceu.

Aprovação do Facebook

Com nosso bot funcional e online, agora temos que deixá-lo em aprovação no dashboard de developers do Facebook.

Basta preencher o que eles pedem por lá e aguardar até cinco dias para que seu bot possa ser usado por qualquer usuário do Facebook.

Conclusão

O Facebook aprovou rapidamente o chatbot que chamei de Climão. Isso porque utilizamos apenas a permissão de messages do Webhook, que é a mais simples.

Para utilizar esse bot acesse a Página do Facebook e envie uma mensagem para o Climão. =)

O código que criamos durante esse tutorial está disponível no Github.

Qualquer dúvida, entre em contato pelas respostas abaixo.

Publicado originalmente no Medium.