Categorias
Tecnologia

Como criar um chatbot para o Facebook Messenger

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.

Categorias
Crônicas

Retrospectiva 2016

Um resumo pessoal do ano que passou

Olhar para trás é sempre positivo. Nos ajuda a entender o que deu certo e o que deu errado em nossas vidas. É por isso que eu gosto de parar para fazer uma retrospectiva pessoal no final de cada ano.

Ah, 2016! Um ano que muitos falam que foi horrível, que só aconteceu coisa ruim e que só querem que acabe logo.

Não dá pra negar que muita coisa ruim aconteceu de um modo geral nesse ano que está acabando. Até me sinto mal dizendo que para mim foi um ano positivo.

Um ano de reconstrução pessoal. Um ano em que as coisas começaram a encaixar em seus devidos lugares. Um ano de autoconhecimento, mais uma vez, e com algumas dificuldades, mas nada comparado a tudo que aconteceu comigo em 2015.

Como tudo é uma questão de contexto, posso dizer que depois de 2015 vai ser difícil falar que um ano realmente me ensinou tanto através de acontecimentos não tão bons assim. Mas também aprendi muito com 2016.

Portanto, 2016 foi um bom ano sim… pelo menos para mim.

No trabalho consegui me estabilizar após o lançamento de dois produtos nos quais eu tive uma grande participação no desenvolvimento. O Diligeiro, lançado ainda na primeira metade do ano, e o SEUPROCESSO, lançado neste final de ano.

Com o primeiro eu aprendi muito no desenvolvimento de API Rest com Python, aprendi como trabalhar com GIS (Geolocalização) e também acabei me aventurando no desenvolvimento de webapp utilizando o AngularJS. Todos os três eram novidades para mim naquele momento, mas hoje, graças à essa experiência, eu posso dizer que consigo ter domínio em cada um deles.

Já com o SEUPROCESSO a coisa foi diferente. Trabalhamos em um ritmo muito intenso para transformar um sistema que já funcionava em uma API Rest e um webapp em tempo recorde. Fortaleci ainda mais os meus conhecimentos com esses dois, mas não foi um processo onde tive tempo para “aprender” mais.

Falando sobre o que aprendi, escrevi publicações sobre o desenvolvimento de APIs e sobre GIS com Django.

Aos poucos fui deixando de lado os trabalhos extras, os famosos freelas, para achar um tempo para a vida pessoal voltar à cena. Para isso, foi preciso organizar as contas de vez. Tarefa que ainda está em andamento…

Tenho certeza de que não sou o único que ainda está tentando organizar a vida financeira, infelizmente nosso país vem sofrendo com esse momento complicado da economia e da política. Então, não vou ficar reclamando sobre essa parte…

Como sobrou um pouco mais de tempo sem os freelas, comecei a estudar um pouco sobre outras coisas legais para fazer na minha área. E, mais ou menos na metade do ano, resolvi começar um desafio pessoal: fazer um chatbot.

Durante o ano li muito a respeito dos chatbots e quis criar um completo como aprendizado. Essa tarefa está quase concluída e já tem uma publicação quase pronta com o diário de desenvolvimento desse desafio… Pode ser que ela acabe publicada ainda esse ano por aqui.

Para finalizar, que tal lembrar do que não deu certo também?

Em uma meta eu falhei miseravelmente em 2016. Encontrar algo relacionado à atividade física que me prendesse de vez, como já aconteceu no passado com a arte marcial. Experimentei diversos tipos de atividades, mas infelizmente ainda não encontrei uma que tenha me conquistado pra valer.

Tenho a necessidade de melhorar minha saúde para que eu ainda possa escrever muitas retrospectivas no futuro. E sei que exercício físico é vital para isso. Portanto não vou desistir ainda.

Resumindo, 2016 foi ano de trabalhar muito e de ajustar a vida para que 2017 venha trazer os frutos desse trabalho e desse esforço para aparar as arestas.

Que venha o ano novo, estou pronto para você!

Publicado originalmente no Medium.