Categorias
Tecnologia

Redirecionando posts para subdomínio no WordPress

Quando postagens já estão indexadas pelos mecanismos de busca, como Google e Bing, há algum tempo fica complicado mudar um blog do WordPress de domínio.

No meu caso eu precisava mudar apenas alguns posts de categorias específicas para um outro subdomínio do meu site. O objetivo era redirecionar os posts relacionados com artes para o meu novo blog sobre arte.

Depois de pesquisar um pouco acabei chegando em uma solução razoável.

Criei o novo blog com WordPress, importei o conteúdo das categorias específicas para o mesmo e coloquei em um novo subdomínio.

No meu blog atual, este que você está lendo agora, no arquivo functions.php do template adicionei o código abaixo.

add_action('template_redirect', 'redirect_category_posts');
function redirect_category_posts() {
 global $post;
 if (is_single($post->ID) && (
  in_category('ilustracoes', $post) || in_category('ilustracao-em-acao', $post) || 
  in_category('hqs', $post) || in_category('ecomic-hqs', $post) || 
  in_category('tailer-hqs', $post) || in_category('video', $post) || 
  in_category('quick-sketch', $post)
 )) {
  $new_url = str_replace('http://mbeck.com.br', 
                         'http://arte.mbeck.com.br',
                         get_permalink($post->ID));
  wp_redirect($new_url, 301);
  exit;
 }
}

Linha por Linha

A primeira linha adiciona uma nova ação ao template.

add_action('template_redirect', 'redirect_category_posts');

Em seguida eu crio a função e utilizo a global $post do WordPress. Nela teremos os dados da publicação.

function redirect_category_posts() { 
  global $post;

Adiciono uma condição para verificar se estamos em um post único através do $post->ID. Para verificar se encaixa em uma das categorias especificadas uso a função in_category do WordPress.

if (is_single($post->ID) && (
 in_category('ilustracoes', $post) || in_category('ilustracao-em-acao', $post) || 
 in_category('hqs', $post) || in_category('ecomic-hqs', $post) || 
 in_category('tailer-hqs', $post) || in_category('video', $post) || 
 in_category('quick-sketch', $post) 
)) {

Em seguida monto a URL que será utilizada para o redirecionamento substituindo o domínio antigo pelo novo com a função str_replace do PHP. Para pegar o link permanente do post eu uso a função get_permalink do WordPress.

  $new_url = str_replace('http://mbeck.com.br', 
                         'http://arte.mbeck.com.br', 
                         get_permalink($post->ID));

Lembrando que quando criei outro blog com o WordPress e importei o conteúdo do antigo eu utilizei exatamente o mesmo formato de URL (permalink) para os posts do novo blog que eu utilizava no antigo.

Para fechar, utilizo a função wp_redirect do WordPress, com um código 301 (código HTTP que significa movido permanentemente), e termino com um exit.

  wp_redirect($new_url, 301); 
  exit; 
 } 
}

E voilá! Está funcionando como o esperado.

Se ficou alguma dúvida comente abaixo.

Nos vemos na próxima.

Categorias
Tecnologia

Criando um bot super simples para o Slack

Em uma das reuniões dessa última semana na empresa onde trabalho surgiu uma necessidade simples de comunicação: Precisamos que toda a equipe saiba quando um novo release de algum dos nossos repositórios for criado no Github.

Como usamos o Slack como ferramenta de comunicação interna, na hora já pensei:

“Esta é a minha chance de criar um bot para o Slack!”

Sempre tive vontade de fazer algo para o Slack, mas não tinha conseguido achar um tempo e nem um motivo específico para isso. Eu já imaginava que para esse objetivo, avisar sobre um novo release, não seria algo complicado.

Realmente não foi. Em apenas algumas linhas deu pra criar um bot que conecta os webhooks do Github e do Slack utilizando o Flask como meio-campo.

Abaixo o código final da primeira versão.

# coding: utf-8
import os
import requests
from flask import Flask
from flask import request

app = Flask(__name__)

slack_webhook_url = os.getenv('SLACK_WEBHOOK_URL')

@app.route("/", methods=['POST', ])
def webhook():
 action = request.json['action']
 release = request.json['release']
 repository = request.json['repository']

slack_data = {
 "text": "A new release from *{repo_name}* was {action}!\n"
         "Click <{release_url}|Release {tag_name}> for more details".format(action=action,
           repo_name=repository['name'],
           release_url=release['url'],
           tag_name=release['tag_name'])
}

response = requests.post(slack_webhook_url, json=slack_data)

if response.status_code != 200:
 raise ValueError(
   'Request to slack returned an error {}, the response is:\n{}'.format(response.status_code, 
      response.text)
 )

return ""

Pelo pouco tempo que passei lendo sobre como fazer um bot para o Slack eu entendi que você cadastra um Incoming Webhook e eles geram uma URL a qual você irá executar um request do tipo POST enviando a mensagem que você quer que um canal específico receba.

É realmente muito simples, porque a própria URL já carrega os tokens que são gerados para você.

Existem diversas outras funcionalidades que podem ser criadas em um bot para o Slack e a documentação deles cobre tudo. Mas vou falar apenas desse pequeno release-alert que criei.

Explicando linha por linha

Antes de começar a escrever o código vou precisar do Flask e de alguns outros pacotes. Então começo ainda na linha de comando do shell.

$ pip install Flask
$ pip install requests

Depois eu coloco a URL que gerei no Incoming Webhooks do Slack em uma variável de ambiente que chamo de SLACK_WEBHOOK_URL. Depois basta importá-la para dentro do código.

$ export SLACK_WEBHOOK_URL=<aqui vai a url do webhook do slack>

Agora crio meu arquivo Python chamado bot.py. Como estou usando o Flask, só vou precisar desse arquivo e praticamente nada mais.

Nas primeiras linhas começo importando o que vou utilizar e criando o app do Flask.

import os
import requests
from flask import Flask
from flask import request

app = Flask(__name__)

Trago para dentro do código a variável de ambiente que criei antes.

slack_webhook_url = os.getenv('SLACK_WEBHOOK_URL')

Agora vou começar a montar o meu endpoint. Ele receberá um evento POST enviado pelo webhook do Github. Para que isso aconteça você precisa cadastrar uma URL de webhook no settings do seu projeto no Github.

No meu caso, eu cadastrei a webhook no settings da organização, já que queria que todos os projetos da empresa disparassem o aviso. Mas funciona do mesmo jeito em qualquer um dos casos.

Quando cadastrei o meu webhook no Github especifiquei que gostaria de receber apenas os eventos relacionados ao release dos projetos.

Para testar o código localmente eu utilizei o ngrok. Explico como usar o ngrok em outra publicação.

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

As primeiras linhas do meu endpoint capturam as informações que eu preciso do corpo enviado pelo POST do webhook do Github.

 action = request.json['action']
 release = request.json['release']
 repository = request.json['repository']

O Flask facilita recuperar informações de um corpo de um request JSON com o seu objeto request nativo. Basta chamar request.json e ele retorna um dicionário.

Agora eu vou montar o corpo da mensagem que enviarei para o Slack. O formato pedido é muito simples: um JSON com um campo ‘text’.

slack_data = {
 "text": "A new release from *{repo_name}* was {action}!\n"
         "Click <{release_url}|Release {tag_name}> for more details".format(action=action,
            repo_name=repository['name'],
            release_url=release['url'],
            tag_name=release['tag_name'])
}

Esse dicionário será transformado em um JSON pela próxima linha. O pacote requests já resolve isso para mim através do argumento json.

response = requests.post(slack_webhook_url, json=slack_data)

if response.status_code != 200:
 raise ValueError(
   'Request to slack returned an error {}, the response is:\n{}'.format(response.status_code, 
       response.text)
 )

return ""

No final do arquivo estou apenas tratando algum possível erro. Caso o requests retorne um status_code diferente de 200 eu levanto uma exceção.

No final retorno uma string vazia, porque cada endpoint do Flask precisa retornar alguma coisa para não levantar erros.

Para que meu bot fique disponível o tempo todo, fiz um deploy na minha conta gratuita do Heroku. E voilá!

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
Tecnologia

API Rest e os verbos HTTP

Arquitetura, padrões e boas práticas

Quando desenvolvemos um serviço web, que alimentará com dados algum cliente (aplicativos, webapps ou qualquer outro sistema), precisamos prestar muita atenção no modelo de interface que utilizaremos.

Isso é importante para que os desenvolvedores que utilizarem seu serviço web tenham fácil compreensão do que cada endpoint faz, de acordo com o verbo HTTP utilizado para a requisição.

A maneira mais comum para modelar uma interface como essas nos dias de hoje é a arquitetura REST.

Antes de continuar, vamos para algumas definições importantes para seguirmos o assunto sem que tudo fique incompreensível para quem não é familiarizado com as expressões utilizadas nos primeiros parágrafos.

O que é API?

Interface de Programação de Aplicação, cujo acrônimo API provém do Inglês Application Programming Interface, é um conjunto de rotinas e padrões estabelecidos por um software para a utilização das suas funcionalidades por aplicativos que não pretendem envolver-se em detalhes da implementação do software, mas apenas usar seus serviços. (fonte Wikipedia)

No caso desse texto estamos falando especificamente sobre as APIs de serviço web, ou web services APIs, que são interfaces de programação para troca de dados via web, utilizando protocolo HTTP.

Isso é feito através de endpoints.

Os endpoints são os endereços web que executam as ações e são acessados diretamente pelos clientes.

O que é REST?

Representational state transfer (REST) or RESTful web services são uma forma de proporcionar interoperabilidade entre sistemas de computadores na Internet. Os serviços da Web compatíveis com REST permitem que os sistemas solicitantes acessem e manipulem representações textuais de recursos da Web usando um conjunto uniforme e predefinido de operações sem estado (stateless). (fonte Wikipedia)

Quando a arquitetura REST é aplicada à APIs de serviços web, chamamos de RESTful APIs.

E os verbos HTTP?

O protocolo HTTP define métodos (às vezes referidos como verbos) para indicar a ação desejada a ser realizada no recurso identificado. O que este recurso representa, se são dados pré-existentes ou dados gerados dinamicamente, depende da implementação do servidor. Muitas vezes, o recurso corresponde a um arquivo ou a saída de um executável residente no servidor. (fonte Wikipedia)

Em resumo, os verbos HTTP são os métodos de requisição que utilizamos para acessar os endpoints de uma RESTful API.

Uma das primeiras coisas que deve ser feita ao iniciar um projeto de API é planejar os endpoints que existirão para o acesso aos dados e para as ações específicas.

Segue um exemplo bem simples desse planejamento feito em uma planilha qualquer.

Vendo esse exemplo fica bem explícito o que cada verbo HTTP significa, certo?

Qualquer um que conheça essa arquitetura vai saber que o endpoint /users tem, no mínimo, essas opções de ação. Por isso é tão importante respeitar os verbos para facilitar o entendimento da sua API.

Vamos detalhar melhor cada um deles.

POST

O verbo POST é mais frequentemente utilizado para criar novos recursos. Na criação bem-sucedida, retornar o status HTTP 201.

Ele não é um método seguro, pois altera o estado do recurso no servidor. Ele também não é idempotente, o que quer dizer que se ele for executado duas vezes de forma idêntica serão criados dois itens diferentes com o mesmo conjunto de dados.

GET

O método HTTP GET é usado para ler ou recuperar uma representação de um recurso. Em caso de sucesso, retorna uma representação em JSON e um código de resposta HTTP de 200 (OK). Em caso de erro, ele geralmente retorna um 404 (NOT FOUND) ou 400 (BAD REQUEST).

De acordo com o design da especificação HTTP, requisições GET (juntamente com HEAD) são usadas apenas para ler dados e jamais alterá-los. Portanto, quando usados dessa forma, são considerados seguros.

Além disso, GET (e HEAD) é idempotente, o que significa que fazer várias solicitações idênticas acaba tendo o mesmo resultado de uma única solicitação.

PUT

PUT é mais utilizado para substituir (ou atualizar) recursos, executando a requisição para uma URI de recurso conhecido, com o corpo da requisição contendo a representação recém-atualizada do recurso original.

Na atualização bem-sucedida, retorna 200 (ou 204 se não retornar qualquer conteúdo no corpo). Retornar os dados do recurso no corpo é opcional, lembrando que fazer isso causa maior consumo de banda.

PUT não é uma operação segura, pois modifica estado no servidor, mas é idempotente. Em outras palavras, se você atualizar um recurso usando PUT e, em seguida, fazer essa mesma chamada novamente, o recurso ainda está lá e ainda tem o mesmo estado.

Obs: Se, por exemplo, executar uma requisição PUT em um recurso incrementar um contador (dentro do recurso), a chamada não é mais idempotente. É recomendado manter as solicitações PUT idempotentes. Use o POST para solicitações não idempotentes.

PATCH

PATCH é usado para modificar parcialmente os recursos. A requisição só precisa conter as alterações específicas para o recurso, não o recurso completo.

Se parece com PUT, mas o corpo contém um conjunto de instruções descrevendo como um recurso no servidor deve ser modificado para produzir uma nova versão.

PATCH não é nem seguro, nem idempotente.

DELETE

DELETE é bastante fácil de entender. Ele é usado para excluir um recurso identificado por um URI.

Na exclusão bem-sucedida, devolve o status HTTP 200 (OK) ou o status HTTP 204 (NO CONTENT) sem corpo de resposta.

Operações DELETE são idempotentes.

Há uma advertência sobre idempotência no DELETE. Chamar DELETE em um recurso uma segunda vez geralmente retornará um 404 (NOT FOUND) já que ele já foi removido e, portanto, não é mais encontrável. Isso, por algumas opiniões, faz operações DELETE não mais idempotente, no entanto, o estado final do recurso é o mesmo. Retornar um 404 é aceitável e comunica com precisão o status da chamada.

Obs: Se a requisição DELETE decrementa um contador (dentro do recurso), a chamada DELETE não é mais idempotente. É recomendável usar o POST para solicitações de recursos não idempotentes.

Os códigos de resposta (status codes) HTTP

Nas descrições dos verbos HTTP foram citados diversas vezes os status code do protocolo HTTP.

Esse é outro item importante para a arquitetura de uma API REST, porque, da mesma maneira que acontece como os verbos HTTP, elas formam um padrão facilmente reconhecido por quem for consumir o web service.

Os principais códigos utilizados para as respostas de um endpoint são o 200 (OK), o 201 (CREATED), o 204 (NO CONTENT), o 404 (NOT FOUND) e o 400 (BAD REQUEST).

Todos os códigos tem nomes autoexplicativos, portanto é muito simples lembrar o que utilizar em cada situação.

Existe uma lista enorme de códigos de resposta do protocolo HTTP que podem ser utilizados, como o 301 (MOVED PERMANETLY) e o 304 (NOT MODIFIED). Sendo o segundo para conteúdos em cache, por exemplo.

Os códigos de sucesso tem o padrão 20x, os de redirecionamento 30x, os de erro do cliente 40x e os de erro de servidor 50x.

Lembrando mais uma vez, os padrões facilitam qualquer desenvolvedor de entender facilmente o que aconteceu com o retorno da requisição que ele executou.

No caso de erros, mesmo tendo esses padrões, é importante devolver ao cliente uma mensagem clara do que aconteceu com a requisição e qual o motivo do erro.

Para ver mais detalhes sobre os códigos HTTP e também a lista completa deles, acesse o link https://httpstatuses.com/.

Concluindo

Este foi uma publicação mais teórica sobre a arquitetura, os padrões e algumas boas práticas no desenvolvimento de uma RESTful API.

Ainda existem muitos assuntos a serem abordados para quem quer se aprofundar mais nesse tipo de desenvolvimento, mas essa publicação ajuda a dar os primeiros passos.

Mas a melhor maneira de aprender de verdade é passando pela experiência de desenvolver uma API que seja consumida por alguns clientes, ou até criar um cliente que consuma uma API pública já existente.

Publicado originalmente no Medium.

Categorias
Tecnologia

Como fazer tunelamento de localhost de forma segura

Veja como expor seu servidor local para a internet e para que serve isso

O tunelamento do localhost é uma maneira de ter um “link externo” que aponte para alguma aplicação que você esteja rodando localmente. Dessa forma podemos “expor” nosso servidor local de forma segura.

Mas por que eu iria querer isso? Podemos usar essa solução para diversas situações, principalmente no desenvolvimento web. Vou mostrar alguns exemplos de uso nessa publicação.

Para os exemplos a seguir eu estou usando o ngrok, um software que facilita muito isso e ainda mantém essa exposição totalmente segura.

Mostrar um desenvolvimento em andamento para o cliente

Digamos que você esteja desenvolvendo um site ou um sistema web para um cliente e quer mostrar o que você desenvolveu até o momento. O mais comum seria fazer o deploy do trabalho em andamento em um servidor web e disponibilizar o link para seu cliente.

Mas existe uma outra opção, podemos deixar o site rodando localmente e tunelar com o ngrok.

Você pode iniciar um servidor local com o SimpleHTTPServer do Python, por exemplo, e rodar o ngrok na linha de comando, como no exemplo abaixo.

$ python -m SimpleHTTPServer 8000
Serving HTTP on 0.0.0.0 port 8000 ...

Em outra aba do terminal você inicia o tunelamento.

./ngrok http 8000

Agora ele irá exibir o endereço que ficará disponível para seu cliente pela internet. Será algo como http://36096fff.ngrok.io.

Ao rodar, o ngrok vai aparecer assim no seu terminal…

Estamos iniciando um servidor HTTP local na porta 8000. Perceba que o comando de tunelamento recebeu os parâmetros http e 8000, indicando que é um servidor http e que está usando a porta 8000.

Testar um chatbot durante o desenvolvimento

Durante o desenvolvimento de um chatbot para o Facebook Messenger, por exemplo, não é possível testar sem um “endereço web”. É preciso que seja registrada uma URL nas configurações do seu chatbot do Messenger para que ele receba os eventos de mensagem.

Para entender melhor sobre isso, veja este artigo.

Para que você não precise fazer deploy de cada pequena alteração em um servidor web para testar no seu chatbot, crie um tunelamento, como no exemplo anterior, e registre o endereço gerado na seção webhook nas configurações do chatbot no Messenger.

Obs: Nem todos os aplicativos de mensagem se utilizam de webhooks para receber os eventos. No caso do Telegram, como neste link, não é necessário utilizar o tunelamento.

Testar endpoints de webhooks

Eu falei em webhooks no item anterior. Pra quem não sabe o que é, segue uma tradução livre de uma parte do artigo da Wikipedia:

Webhooks são “retornos de chamada HTTP definidos pelo usuário”. Normalmente eles são ativados por algum evento, como o push de código para um repositório ou um novo comentário postado em um blog. Quando ele acontece, o site origem faz uma requisição HTTP para a URI configurada para o webhook, enviando os dados do evento.

Existem diversos serviços que utilizam esse formato para enviar dados de eventos. Um bom exemplo é o Sendgrid ou o Mandrill, que usam webhooks para enviar os eventos de email (lido, marcado como spam, link clicado, etc.) para um sistema de terceiros que esteja usando sua API.

Outro bom exemplo são integrações com gateways de pagamento, como Iugu, PagSeguro ou PayPal, onde, mesmo durante o desenvolvimento, você precisa de um “endereço web aberto” para receber os eventos relacionados aos pagamentos, alterando o status de uma compra, por exemplo.

Para isso, abra um tunelamento com o ngrok e registre o novo endereço gerado por ele com o seu endpoint para receber os eventos webhook do serviço que você está utilizando.

Painel do ngrok

Uma funcionalidade interessante do ngrok é o seu painel, ou interface web.

Screenshot da interface web

Com ela você vê todas as requisições que foram feitas ao seu endereço web aberto. Com isso você pode verificar os dados que estão vindo com as requisições e ainda dar um “replay” na mesma, para testar novamente.

Para acessar, veja o endereço listado no terminal do ngrok como Web Interface e abra no seu navegador. Vai ser algo como http://127.0.0.1:4040.

Concluindo

Esses são alguns casos de uso para o tunelamento de localhost. Alguns deles eu já utilizei, como o teste de webhooks e a demonstração de desenvolvimento em andamento para cliente. Mas eu descobri mesmo que isso era possível ao ler um tutorial de criação de chatbot para o Facebook Messenger, então resolvi dar esse exemplo também.

Publicado originalmente no Medium.
Categorias
Tecnologia

Como criar um chatbot para Telegram

Essa publicação é baseada na minha primeira experiência criando um chatbot para o Telegram usando Python. O código fonte dessa experiência pode ser encontrado no meu Github.

The Botfather

O Telegram facilita muito a criação de bots para seu sistema. Além de uma documentação muito boa e uma API muito simples de usar, ele possui o BotFather, um bot que ajuda qualquer um a criar seu próprio bot.

Segundo a documentação do Telegram:

BotFather is the one bot to rule them all. It will help you create new bots and change settings for existing ones.

Então vamos lá. Para começar é preciso acessar o @BotFather e fazer o registro do seu novo chatbot.

Para qualquer criação ou configuração de bot é preciso acessá-lo. E como vamos criar um novo bot, acessamos o link @BotFather.

Lá você terá acesso a diversos comandos, dentre eles o /start, que irá apresentar a lista desses comandos e o /newbot, que serve para a criação de um novo bot.

Quando executarmos o /newbot ele pede algumas configurações, como o nome e o username que serão utilizados pelo bot. Nada complicado.

Seu bot será como um usuário comum do Telegram, com um “nome para exibição” e um “nome de usuário” (username) para controle.

Assim que forem preenchidos os dados necessários ele irá gerar um token para ser utilizado no seu script. Esse token é único e serve como uma “senha” para seu bot, portanto deve ser mantido em sigilo.

Para saber mais sobre o @BotFather, acesse a documentação do Telegram que fala sobre bots para desenvolvedores.

Armazenando e acessando o token

Agora que já temos o token do nosso bot, devemos guardá-lo em uma variável de ambiente para que possamos acessá-lo posteriormente no nosso script Python.

Na linha de comando, execute o seguinte:

$ export BOT_API_TOKEN="<aqui vai o token gerado pelo botfather>"

Agora você poderá recuperá-lo facilmente em seu script Python com apenas duas linhas de código.

import os
token = os.environ['BOT_API_TOKEN']

Obs: Esta é apenas uma sugestão de como armazenar essa informação. Você pode guardar seu token da maneira que achar melhor.

Implementando os comandos

Um pacote que facilita muito a vida de quem está desenvolvendo um bot para o Telegram é o pyTelegramBotAPI. Com ele basta adicionar decorators nas suas funções e elas passarão a atender aos comandos recebidos.

Podemos usar o seguinte comando para instalar o pacote via pip:

$ pip install pyTelegramBotAPI

Agora chegou a hora de implementar os comandos que controlarão o bot.

Vou abordar somente um tipo de decorator e de resposta a caráter de exemplo. Através da documentação do pyTelegramBotAPI você consegue facilmente implementar diversas outras formas de input e output para o seu bot.

Começamos o script em Python fazendo nossos imports e iniciando o telebot com o token do Telegram gerado pelo @BotFather, como no exemplo de código abaixo.

import os
import telebot
  
bot = telebot.TeleBot(os.environ['BOT_API_TOKEN'])

Agora é só começar a implementar os comandos. No exemplo abaixo eu estou implementando uma mensagem de boas vindas para os comandos /start e /help.

@bot.message_handler(commands=['start', 'help'])
def send_welcome(message):
    bot.reply_to(message, u"Olá, bem-vindo ao bot!")

No final do script adicionamos o método bot.polling() para inicializar o nosso novíssimo bot.

bot.polling()

Basta executar o script em linha de comando e o seu bot está pronto para começar a ser testado.

$ python bot.py

Para executar o teste, basta acessar seu Telegram e seguir o usuário com o username que você deu para seu bot lá no começo do processo, com o @BotFather.

E voilá!

Simples, não é? Veja como nosso script em Python ficou bem pequeno.

import os
import telebot
  
bot = telebot.TeleBot(os.environ['BOT_API_TOKEN'])
@bot.message_handler(commands=['start', 'help'])
def send_welcome(message):
    bot.reply_to(message, u"Olá, bem-vindo ao bot!")
bot.polling()

Publicado originalmente no Medium.

Categorias
Tecnologia

Como usar múltiplos databases no Django

Utilizando o Database Router de uma forma genérica

Existem diversos motivos para utilizar mais de um banco de dados no seu projeto de software. Um deles pode ser as réplicas, por exemplo. Neste caso o aplicativo escreve as informações em um banco de dados, mas lê de outro.

Um diagrama mostrando o banco primário e suas réplicas de leitura

A vantagem de usar uma réplica é que a escrita em banco é mais custosa do que a leitura. Então, se uma consulta for feita durante um processo de inserção de dados, dependendo da quantidade de dados que está sendo escrito no database, ela pode acabar ficando muito mais lenta, causando um problema de timeout ou até tirando seu sistema do ar.

Esse é apenas um dos exemplos que é abordado na própria documentação oficial do Django. Como mostra este link.

Obviamente, esse não é o único motivo para se ter mais de um database no sistema, podem existir inúmeros, incluindo o que me fez pesquisar sobre isso. Vou falar mais sobre o meu caso específico no final desta publicação.

Definindo os databases

O primeiro passo para utilizar mais de um database no Django é definir seus bancos de dados nas configurações do arquivo settings.py.

DATABASES = {
    'default': {},
    'primary': {
        'NAME': 'primary',
        'ENGINE': 'django.db.backends.mysql',
        'USER': 'mysql_user',
        'PASSWORD': 'spam',
    },
    'replica1': {
        'NAME': 'replica1',
        'ENGINE': 'django.db.backends.mysql',
        'USER': 'mysql_user',
        'PASSWORD': 'eggs',
    },
    'replica2': {
        'NAME': 'replica2',
        'ENGINE': 'django.db.backends.mysql',
        'USER': 'mysql_user',
        'PASSWORD': 'bacon',
    },
}

Com esse código, retirado da documentação do Django, estamos definindo um banco de dados principal MySQL e mais duas réplicas.

A partir de agora você pode alternar o banco manualmente chamando o using() do QuerySet. Ele recebe apenas um argumento, o nome do banco de dados.

>>> # Isso irá rodar a consulta no banco 'default'.
>>> Author.objects.all()
>>> # Isso também.
>>> Author.objects.using('default').all()
>>> # Já esse irá rodar a consulta no banco 'other'.
>>> Author.objects.using('other').all()

Acredito que esse approach não deve agradar muito o desenvolvedor. Ter que definir manualmente qual o banco a ser utilizado em cada query, não é lá muito prático, certo?

Pra evitar isso temos o Database Router, o roteador de banco de dados nativo do Django.

Criando um Database Router

O roteador de banco de dados simplesmente define qual banco é usado para cada tipo de operação.

No exemplo abaixo, também retirado da documentação do Django, mostro como rotear entre os bancos. Nesse caso, vamos ler das réplicas e escrever no primário.

import random
class PrimaryReplicaRouter(object):
    def db_for_read(self, model, **hints):
        """
        Consultas vão aleatoriamente para uma das réplicas.
        """
        return random.choice(['replica1', 'replica2'])
    def db_for_write(self, model, **hints):
        """
        Escritas sempre são feitas no banco primário.
        """
        return 'primary'
    def allow_relation(self, obj1, obj2, **hints):
        """
        Relações entre objetos são permitidas se ambos estiverem no 
        pool primary/replica.
        """
        db_list = ('primary', 'replica1', 'replica2')
        if obj1._state.db in db_list and obj2._state.db in db_list:
            return True
        return None
    def allow_migrate(self, db, app_label, model_name=None, **hints):
        """
        All non-auth models end up in this pool.
        """
        return True

Note que o roteador de banco de dados disponibiliza quatro métodos. São eles:

  • db_for_write()
  • db_for_read()
  • allow_relation()
  • allow_migrate()

Na leitura escolhemos aleatoriamente uma das duas réplicas e na escrita escolhemos o banco primário. Perceba que simplesmente retornamos o nome do banco, que definimos nas configurações de database no settings.py.

Para que o router seja aplicado, precisamos adicionar a constante DATABASE_ROUTERS no arquivo settings.py, como mostro abaixo.

DATABASE_ROUTERS = ['caminho.para.o.PrimaryReplicaRouter']

Este é um exemplo simplificado do que pode ser feito com os database routers. Apenas resumi a documentação do Django sobre o assunto até agora.

Partimos para algo diferente, então.

Uma solução mais genérica

Procurando sobre o assunto na internet, cheguei até esta postagem de 2011 que mostra uma solução mais genérica para o database router e ainda com possibilidade de utilizar para mais de um app do Django.

from django.conf import settings
class DatabaseAppsRouter(object):
    """
    A router to control all database operations on models for different databases.
    In case an app is not set in settings.DATABASE_APPS_MAPPING, the router will fallback to the `default` database.
    Settings example:
    DATABASE_APPS_MAPPING = {'app1': 'db1', 'app2': 'db2'}
    """
    def db_for_read(self, model, **hints):
        """"Point all read operations to the specific database."""
        if settings.DATABASE_APPS_MAPPING.has_key(model._meta.app_label):
            return settings.DATABASE_APPS_MAPPING[model._meta.app_label]
        return None
    def db_for_write(self, model, **hints):
        """Point all write operations to the specific database."""
        if settings.DATABASE_APPS_MAPPING.has_key(model._meta.app_label):
            return settings.DATABASE_APPS_MAPPING[model._meta.app_label]
        return None
    def allow_relation(self, obj1, obj2, **hints):
        """Allow any relation between apps that use the same database."""
        db_obj1 = settings.DATABASE_APPS_MAPPING.get(obj1._meta.app_label)
        db_obj2 = settings.DATABASE_APPS_MAPPING.get(obj2._meta.app_label)
        if db_obj1 and db_obj2:
            if db_obj1 == db_obj2:
                return True
            else:
                return False
        return None
    def allow_syncdb(self, db, model):
        """Make sure that apps only appear in the related database."""
        if db in settings.DATABASE_APPS_MAPPING.values():
            return settings.DATABASE_APPS_MAPPING.get(model._meta.app_label) == db
        elif settings.DATABASE_APPS_MAPPING.has_key(model._meta.app_label):
            return False
        return None

Agora basta mapear os apps com os databases no seu settings.py:

# Fonte: http://stackoverflow.com/a/18548287
DATABASE_ROUTERS = ['caminho.para.o.DatabaseAppsRouter']
DATABASE_APPS_MAPPING = {'nome_do_app': 'nome_do_db',
                         'nome_do_outro_app': 'nome_do_outro_db'}

No Meta do meu model eu defino o app_label, como no exemplo abaixo.

class Author(models.Model):
    name = models.CharFiels(max_length=120)
    class Meta:
        app_label = 'nome_do_app'

No meu caso de uso dessa solução, eu precisava que apenas um model lesse de um banco da dados diferente dos outros. Então, somente nele, eu adicionei o app_label mapeado com o banco de dados diferente.

Todos os outros models, que não possuem essa configuração, usam o banco default automaticamente, apenas o model com o app_label definido passa a usar o outro database.

Essa solução simplificou muito minha vida, pois agora eu posso continuar fazendo as consultas normalmente, porque o database router vai rotear tudo de maneira automática. Não preciso mais me preocupar com qual banco ele está buscando a informação em cada model.

Concluindo

Bem, é isso que tenho para mostrar. Sei que a abordagem é simplificada, mas foi a primeira vez que trabalhei nesse problema e quis dividir o que aprendi por aqui.

Espero que tenha ficado tranquilo de entender sobre o roteamento de banco de dados no Django. Caso eu não tenho sido claro em algum detalhe, entre em contanto.

Publicado originalmente no Medium.

Categorias
Quadrinhos Tailer Vídeo

Vídeos de desenho – Como desenhar histórias em quadrinhos

Categorias
Vídeo

Tutorial – Os passos para criar uma ilustração

Quais são os passos para criar uma ilustração? Neste vídeo falo rapidamente sobre o passo-a-passo da criação de um desenho, por onde começar e no que prestar a atenção em cada momento.

Deixe seu comentário com a sua dúvida ou sugestão para o próximo vídeo!

Categorias
Vídeo

Tutorial – Desenhando com Pincel e Nanquim

Dicas para desenhar com pincel e nanquim! Neste vídeo falo sobre materiais para arte-final! Desenhando a personagem Arya Stark de Game Of Thrones.

Se você tem alguma sugestão para os vídeos de tutorial, deixe seu comentário abaixo!