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 aprender novas tecnologias

Na área de desenvolvimento de software sempre somos bombardeados por notícias de novas tecnologias que estão dominando o mercado. E que, para ficar na “crista da onda” (e ser bem pago), é preciso trabalhar com essa ou aquela nova tecnologia.

Essa é a hora que o desespero bate e você pensa:

“Tenho que aprender esse novo framework senão vou ficar para trás!”

Tenho certeza que isso já aconteceu com muitos programadores por aí.

Aí é só aprender que ficará tudo bem, certo? Mas não é tão simples assim.

Para aprender uma coisa pra valer não basta seguir aquele tutorial maneiro do blog do especialista. Envolve muito mais do que isso.

Aprendendo Python

Em 2012 eu fiz minhas primeiras tentativas de aprender Python e Django, porque eu achava que o PHP não me daria futuro. Leia-se grana.

Em 2012 eu achava que Python me deixaria assim… Doce ilusão.

Objetivo bobo para aprender algo, na verdade. Em 2010 eu tinha tentado a mesma coisa com Java… Não rolou.

Eu fiz uns projetos pessoais como um portfólio e um controle financeiro seguindo alguns tutoriais e a documentação do Django, mas somente a caráter de teste. Não cheguei nem a arranhar a superfície do que o Python ou o Django poderiam me proporcionar como soluções.

Não existia um problema real ao qual eu precisava do Python para resolver.

Eu continuava conseguindo trabalhos somente com PHP, por causa da minha experiência profissional. Com isso acabei não aprendendo nada relacionado a Python na época.

Como não gostar de Python, não é?

Depois de um bom tempo, eu continuava com PHP nas empresas em que trabalhava, mas não queria desistir do Python porque tinha gostado de programar na linguagem.

Aí o objetivo era outro. Aprender porque eu gostava. Mas como?

Foi então que resolvi fazer alguns projetos freelance com o Flask, outro framework em Python. A partir daí comecei a entender e a dominar um pouco mais essa linguagem.

Foi uma tática muito mais inteligente.

Depois de um tempo fazendo esses freelances, acabei conseguindo um emprego para desenvolvedor em Python e hoje trabalho diariamente com ele.

Não-aprendendo AngularJS

Outra experiência parecida foi com o desenvolvimento de frontend.

Durante um período em que fiquei desempregado, comecei a procurar vagas e percebi que existia muita procura por desenvolvedor frontend. Como sempre tive conhecimento sólido com HTML/CSS e JavaScript (e jQuery), pensei que poderia conseguir alguma coisa.

O problema é que a maioria das vagas exigia conhecimentos em AngularJS ou outro framework JavaScript. Me candidatei para várias delas e apesar de chegar à fase final de quase todas, não consegui ser contratado.

Eu tenho certeza que, mesmo tendo bom domínio de HTML/CSS/Javascript, não fui contratado porque não tinha familiaridade com nenhum dos (na época novos) frameworks de frontend.

Durante esse tempo eu fiz alguns tutoriais de AngularJS para tentar melhorar essa deficiência e até para fazer testes para algumas das vagas de emprego. Mas confesso que tudo me parecia meio confuso… No final não aprendi nada. Nem sequer entendi a base do framework.

Acabei por desistir na época.

Um tempo depois, precisei utilizar AngularJS em um projeto. Foi um tempo pesquisando para acabar entendendo e aprendendo. Hoje tenho um projeto em produção usando essa tecnologia.

Graças a necessidade.

Aprendendo com a necessidade

A verdade é que cada um tem seu jeito de fixar conhecimento. Não estou aqui para impor regras a algo tão subjetivo como a forma de aprendizagem de cada um.

Quero compartilhar a minha forma de aprender: E ela chama-se necessidade.

Eu só consigo aprender algo para valer se realmente usá-lo em algum projeto. Um projeto real.

Pode ser pessoal ou comercial, mas que seja algo que eu queira ou precise mesmo fazer.

Para que eu consiga entender e fixar é preciso existir a necessidade de resolver um problema com aquela tecnologia.

Foi assim com o Python, quando comecei a focar em projetos freelance com a linguagem, passei a aprendê-la de verdade.

Resumindo: se não existir um problema real para resolver, dificilmente se chega tão fundo a ponto de conhecer as verdadeiras funcionalidades e qualidades de certa tecnologia.

Quer aprender uma nova tecnologia?

Procure a tecnologia que dê a solução para um problema que você tenha que resolver, ela será a melhor e a mais fácil para você aprender.

Fica a dica.

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
Crônicas Tecnologia

Ser um Programador Ninja…

…ou Programador Rockstar ou Mago da Programação…


Este texto foi originalmente publicado no Medium. Leia a versão original clicando aqui e recomende se você gostar.


Alguns bons anos atrás, costumava ser cool ver uma vaga de emprego com a expressão “programador ninja” ou algo do gênero. Mostrava que a empresa era maneira, joinha e que tinha aquele ambiente de trabalho diferentão.

“Buscamos um programador ninja”

Hoje, a coisa é um pouco diferente. Quando vejo uma vaga que busca um programador ninja ou um mago da programação, eu já desconfio.

O problema não é a expressão em si, que hoje em dia é brega e não pinta mais a empresa como “diferentona”. O problema é que ela costuma vir com uma lista absurda de exigências…

O cidadão tem que comprovar anos de experiência em diversas linguagens de programação, tem que gostar de programar em seu horário livre (?!) e, às vezes, é um diferencial, se não obrigação, que o camarada tenha projetos paralelos.

Ou seja, o cara não tem que ser um programador… Ele tem que programar de tudo e o tempo todo. Se tiver vida pessoal, não serve para mago da programação.

O ninja-mago-rockstar da programação

Por algum motivo essas empresas acreditam que é preciso ser apaixonado pela programação para ser um bom profissional. E para demonstrar isso tem que ter aqueles projetos paralelos e, quem sabe, uns commits no seu Github nas madrugadas de sábado.

O que não percebem é que quem programa por prazer não necessariamente vai gostar de trabalhar no seu projeto de gestor administrativo ou de vendas. Hobbies que viram trabalho nem sempre são tão prazerosos. E isso não é tudo…


Nesta semana eu li um texto aqui no Medium chamado Programming Doesn’t Require Talent or Even Passion (Programar Não Exige Talento ou Mesmo Paixão). Eu recomendo a leitura para qualquer um que trabalhe na área.

Nele o autor fala que nunca antes uma habilidade foi tão mitificada como a programação.

“Você não precisa apenas ter talento, você também precisa ser apaixonado para se qualificar como um bom programador.”

É exatamente o que pedem nessas vagas de programador Rockstar dos dias de hoje.


Exemplo de uma vaga postada na lista Python BrasilNo texto citado, o autor ainda traz uma série de grandes nomes da área, como os criadores dos frameworks Rails e Django e da linguagem PHP, por exemplo, que demonstram claramente não serem grandes amantes da programação e muito menos gênios da magia do desenvolvimento de software.

“Na verdade eu odeio programar, mas eu amo resolver problemas.” Tradução da citação de Rasmus Lerdorf, criador do PHP.

Eu trabalho nessa área há cerca de treze anos e não tenho receio em dizer que os melhores profissionais que encontrei não eram grandes magos dos códigos. O que realmente importa nessa área é a entrega. É conseguir resolver os problemas apresentados de forma objetiva e simples e, quase sempre, em um curtíssimo espaço de tempo.

Um bom profissional da programação não precisa ser um gênio, um monstro, um ninja, um hacker… ele precisa saber resolver problemas e ter a capacidade e humildade de aprender o que precisar para atingir esse objetivo.

Além disso, esse mito atrapalha todo mundo que está envolvido ou quer se envolver na área.

“O mito do ‘programador gênio’ é extremamente perigoso. Por um lado, ele deixa o limiar de entrada muito alto, assustando um monte de aspirantes a programador. Por outro lado, também assombra os que já são programadores, porque isso significa que se você não é um mago na programação, você é ruim… …Programação é só um monte de habilidades que podem ser aprendidas e não exigem muito talento, e não é vergonha nenhuma ser um programador mediano.” Tradução livre de parte da citação de Jacob Kaplan-Moss, criador do Django.

Eu consegui aprender a programar, e nem sequer gostava disso…

“Depois de muito tempo percebi que eu tinha ficado melhor em programar e continuava sempre ocupando vagas em empresas nessa área, entretanto, sempre querendo sair para ‘trabalhar com o que gosto’.”

Esse é um trecho de um texto que escrevi em 2014 falando sobre a minha relação conturbada com a programação. Eu passei anos procurando trabalhar em outra área, porque realmente não gostava de programar.

O que mudou isso em mim foi conhecer o Python. Passei a curtir a simplicidade e objetividade de escrever códigos com essa linguagem. Hoje eu gosto do que faço. De vez em quando eu até programo por diversão no meu horário livre.

Eu não consigo me enxergar como um bom programador, mas sei que consigo atender as expectativas de quem me contrata. Resolvo os problemas e faço as minhas entregas. E isso me deixa orgulhoso do meu trabalho.

Não somos ninjas, gênios ou magos, somos profissionais da área de desenvolvimento de software. É isso que temos que ser das 9h as 18h, ou em qualquer que seja seu horário de trabalho, o que fazemos fora disso diz respeito somente a nós mesmos.

Quer ter projetos paralelos? Ama programar? Ótimo.

Mas se você gosta de nadar, andar de bicicleta, jogar futebol, passear com seu cachorro, ficar com sua família ou dormir por horas e horas no seu tempo livre, você continua sendo um programador.

Não é isso que vai definir sua qualidade como profissional.

Pode ser também…

Categorias
Tecnologia

Lendo RSS Feed somente com Javascript (jQuery)

Eu construi uma página estática em HTML, CSS e JS para ser uma espécie de portfólio e cartão pessoal para mim e queria colocar uma seção com os artigos deste blog nela. A melhor maneira de acessar esses artigos sem muito esforço é usando o RSS Feed do blog.

Mas a página é “estática”, então eu poderia usar no máximo o Javascript para ler o Feed do blog. Uma opção seria acessá-lo via Ajax e processar os dados em seguida, mas se o Feed for de outra origem (domínio diferente, que não era o meu caso, mas pode ser o seu) ia acabar esbarrando em um erro de segurança (CORS Error) e não funcionaria.

Então encontrei uma solução (no link de referência no final do artigo), usar uma API do Google que lê um Feed e retorna um JSON com as informações dele.

Segue abaixo uma versão do código que estou utilizando na página, totalmente comentado.

Os items que temos disponíveis para acesso, como mostra a imagem abaixo, são o autor, categorias e tags, o conteúdo inteiro em HTML, um pequeno snippet do conteúdo em texto puro, a URL do artigo, a data de publicação e o título. Eu senti falta do retorno das mídias, como fotos, por exemplo, mas se for necessário a opção seria pegá-las do próprio HTML que retorna em “content“.

É isso aí, qualquer dúvida ou sugestão deixe nos comentários abaixo. Para ver esse código em funcionamento acesse o site.

Referência: http://pt.stackoverflow.com/a/65758