Author Archive for luizirber

Minha (quase) vida bandida

Blog juntando moscas, deixa eu ressucitar um projetinho de fim de semana para ver se anima um pouco.

A ideia inicial desse post surgiu na PythonBrasil do ano passado. Pensei em fazer uma lightning talk, mas não ficou pronta a tempo (Nota: sempre bom deixar alguma lightning talk preparada).

Como começou a história: recebi email de uma prima, pedindo para que os amigos e parentes ajudassem a votar na filha dela em um site de roupas infantis. A criança mais votada participaria de um comercial e ia embolsar um monte de roupas.

Ok, ok. Odeio esse tipo de spam, mas também não custa ajudar, né? Fui na página de votação. Votei uma vez, depois de preencher um captcha, e tentei votar de novo para ver o que acontecia. “Você votou na última hora, aguarde para votar novamente”. Hmm. Como será que o controle disso é feito?

Abri o código. Hmm, essa função em javascript aqui que processa o evento do botão, ela chama uma URL…

  function votoAprovar(cadastroId){
    captcha = document.getElementById('cadastroCaptcha').value;
    window.location = 'voto_v.php?votoStatus=1&cadastroId='+cadastroId+"&captcha="+captcha;
  }

Opa. E se eu tentar acessar essa URL direto?

voto_v.php?votoStatus=1&cadastroId=98374&captcha=adb356

Tenho que acertar o captcha. Onde está o captcha? Ah, olha só, o link da imagem é um arquivo captcha.php, será que dá para acessar direto? Deu.

Essa é a imagem original.

Resumindo, eu tinha a URL para votar, e atualizando o captcha eu conseguia votar quantas vezes quisesse. Mas ficar fazendo isso na mão é chato. Como será que funciona identificação de captcha? Uma pesquisa rapidinha e caí nesse site. E em Python, para facilitar ainda mais minha vida.

Brinquei um pouco com o PIL, e consegui deixar a imagem com caracteres bem definidos. Incrivelmente, só precisei converter para escala de cinza, e aplicar um limiar.

def captcha_to_greyscale(captcha):
    if captcha.mode == 'L':
        return captcha
    captcha = captcha.convert('L', (.4, .4, .4, 0))
    return captcha

def light_pixels_to_white_pixels(pixels, w, h):
    for x in xrange(w):
        for y in xrange(h):
            if pixels[x, y] > 50:
                pixels[x, y] = 255
    return pixels

def clean_captcha(img):
    img2 = captcha_to_greyscale(img)

    w, h = img2.size
    light_pixels_to_white_pixels(img2.load(), w, h)

    return img2

Imagem, depois de processada pelo PIL.

E, conforme ia acumulando mais imagens, vi que a minha vida seria mais fácil ainda: o captcha só tinha caracteres hexadecimais, então nem precisaria mapear o alfabeto inteiro, só de zero a nove e de ‘a’ até ‘f’. Depois de limpar algumas imagens e juntá-las numa pasta, rodei o treinador do tesseract-ocr, e depois dos arquivos de treinos prontos, tinha 100% de acerto nas imagens. Sigh, que maravilha de captcha…

Agora, testar. Criei um perfil falso, e me assustei. Tentei rodar o script para ver se contava um voto, e quando abri o perfil já tinha 5! Aparentemente, as mães fazem um “vote-no-meu-filho-que-eu-voto-no-seu”, e como os perfis mais novos aparecem na página principal, me acharam rapidinho. Ok, rodemos um loop então, cem votos. Yep, todos contados.

    br = mechanize.Browser()

    page = br.open('******/captcha/captcha.php')
    img_str = StringIO(page.read())

    img = Image.open(img_str)
    output = clean_captcha(img)

    fp = open('tmp.tif', 'wb')
    output.save(fp, format='tiff')
    fp.close()

    getoutput('tesseract tmp.tif output -l captcha')
    fp = open('output.txt')
    captcha = fp.read()[:6]
    fp.close()

    cadId = 28477

    vote_page = '******/voto_v.php?votoStatus=1&cadastroId=%d&captcha=%s' % (cadId, captcha)
    br.open(vote_page)

Omiti o endereço do site, mas basicamente o script é esse.

Agora chega o grande momento, o clímax da história, onde o herói escolhe entre a fama e fortuna ou o que parece moralmente certo. (Que grandioso!). “Com grandes poderes vêm grandes responsabilidades!”. E todo esse lero-lero.

Apesar de o propósito inicial ter sido ajudar a minha prima, rodar o script me pareceu uma ajuda grande demais. E meu objetivo era testar o buraco no sistema de votação, não me aproveitar dele. Acabei deixando pra lá, e o código ficou mofando no meu computador.

Hoje, quando fui escrever o post, vi que já aconteceu a segunda edição do concurso, e miseravelmente o sistema é exatamente o mesmo. Vou mandar esse post para a empresa, quem sabe para o próximo corrijam.

UPDATE: O Lameiro deu a dica nos comentários: buscando no google.com.br por “captcha PHP” temos, como primeiro hit, um tutorial ensinando a gerar o captcha que esse site usa. E, como ele bem notou, deve ter muitos sites no Brasil com esse mesmo problema.

Fica a dica: nunca confie no primeiro hit do google para implementar a sua solução de segurança. Aliás, não confie em nenhuma, até saber como ela funciona.

Robô Shrek

Na última quarta-feira aconteceu a Feira de Informática Aplicada, ligada à Semana de Computação da UFSCar. Eu e Alphalpha resolvemos participar para testar o Arduino que eu tinha comprado no começo do ano. O tempo era curto, mas mesmo assim fomos em frente e juntamos algumas peças que sobraram do robô do GEDAI, um N800, um Arduino Duemilanove e montamos nosso próprio robô, chamado de Shrek.

Por que Shrek? Porque Shrek é um ogro, ogros são como cebolas (fedem são feitos de camadas), e nosso robô é feito de várias camadas simples que, juntas, fazem algo complexo.

Como funciona? O Arduino controla os motores, e recebe dados pelo USB (como uma porta serial) vindos do N800. O N800 está conectado em uma rede wifi, e recebe comandos via socket. Além disso, também envia vídeo e áudio para a aplicação (que no momento roda em um PC), e a aplicação envia os comandos e exibe o vídeo e o áudio.

Devido ao pouco tempo, apenas 3 comandos simples foram implementados (frente, giro à esquerda, giro à direita), mas nosso objetivo estava cumprido: a comunicação entre as partes estava funcionando direitinho, e agora podemos partir para incrementá-lo.

Todo o código está disponível no Google Code, e queremos levá-lo para o FISL (depois de arranjarmos motores melhores).

OMG Kitties!

Ontem foi o dia da toalha. Uma imagem vale mais que mil palavras.
Dia da Toalha 2009
À esquerda, minha querida toalha. Ao fundo, World of Warcraft (sim, cometi esse erro. Seja o que Deus quiser). E na frente, em primeiro plano, Ada.
Quem é Ada? Semana passada minha namorada veio perguntar se eu já tinha ouvido falar de Ada Byron, condessa de Lovelace (não confundir com Linda Lovelace, condessa de… clique no link e veja). Eu disse “claro, a primeira programadora”, e começamos a conversar sobre isso. Ela reclamou da falta de reconhecimento da importância dela, e acabamos concluindo que Ada é um nome legal.
Na sexta, fomos até a Arca de São Franscisco, uma entidade de proteção aos animais em São Carlos. E lá achamos essa gatinha, que deveria ter ficado em outro lugar, mas por incompatibilidade de gênios foi rejeitada e acabou ficando no apartamento mesmo. E resolvemos batizar ela como Ada. Diga-se que é a primeira vez que tenho um gato, o único animal de estimação que tive foi uma coelha, quando eu tinha uns 6 anos. Mas tô curtindo por enquanto =]
E acabei descobrindo que essa tirinha do XKCD realmente é verdade:

Randall Munroe, mestre.

Alguém aí tem dicas?

Hand

Ontem, enquanto gravava um DVD, comecei a generalizar os scripts que geram os feeds do post anterior. E disso surgiu o Hand, um gerador de feeds RSS.

Como funciona?

O meu objetivo inicial era gerar feeds para sites que não os disponibilizavam, recorrendo ao bom e velho screen scraping. Comecei fazendo o feed dos quadrinhos da Folha, o mais complicado, pois era necessário fazer autenticação de usuário e percorrer várias páginas para extrair links. Ao fazer o dos Malvados, segui a mesma estrutura de funções, e comecei a perceber que dava para generalizar bastante o processo.

Eis que surge Hand. No fundo é uma classe que implementa alguns métodos (build_date, generate_description, build_feed, process), e exige que você derive a classe e implemente o método generate_data. generate_data é um método que retorna uma lista de dicionários, com cada dicionário contendo os campos title, page_link, description, pubDate e guid correspondentes a um item do feed. Simples assim.

E funciona?

Yep. Mantenho quatro feeds no momento:

Onde posso ver esta maravilha?

O código está disponível no Google Code, mas ainda está bem cru, preciso empacotá-lo direito.

Quais os próximos passos?

O feed da Folha demora para ser gerado, porque toda vez que o script é rodado ele precisa consultar todas as páginas. Portanto penso em adicionar persistência, mas bem simples, um sqlite é mais que suficiente.

Além disso, quero descrever a configuração do feed (onde gerá-lo, qual template usar) num arquivo, e fazer a classe base ler essas opções. Assim fica ainda mais fácil fazer um novo feed.

Que nominho, hein?

Para quem não entendeu o nome: qual um bom para um gerador de feeds? Enquanto pensava, lembrei de uma música do NIN chamada ‘The Hand That Feeds’. E, além disso, ele também te dá uma mão para gerar feeds, certo? *TU-DUM-TISH*!

Feeds de quadrinhos

Infelizmente a Folha de São Paulo não disponibiliza feeds dos quadrinhos diários dela. Isso significa privar-nos de Laerte e Adão, mas não temam! Caso queiram tirinhas frescas toda a manhã no seu leitor de feeds favorito, basta usar o que eu fiz.

A idéia (sim, sou antigo, meus netos ainda vão dizer ‘meu vô é do tempo que se escrevia ideia com acento’) é simples, e foi baseada na do Leandro Siqueira: apesar do conteúdo da Folha ser exclusivo para assinantes, as imagens das tirinhas são acessíveis. Basta descobrir o padrão do nome delas. Mas percebi que as tirinhas de domingo não estavam aparecendo, pois existem autores diferentes nesse dia (Allan Sieber e irmãos Bá, atualmente). Então resolvi fazer um que fosse um pouquinho mais dinâmico, e deu certo, porque quando houve a transição das dominicais o feed continuou funcionando sem modificações.

Como foi feito? Python, Beautiful Soup e Mechanize. O script autentica no site, busca o índice dos quadrinhos, e acha o link das imagens para gerar o feed. Aliás, Beautiful Soup é uma das bibliotecas mais úteis que já usei, para mexer com HTML não tem nada melhor.

E, como o mais complicado já estava feito, semana passada fiz rapidinho um feed para os Malvados também. Até tem um feed lá, mas é só para o blog. Ainda não resolvi como fazer para mostrar as séries, mas as normais aparecem sem problemas no feed (acho, eu uso Google Reader e aparece. Por favor, testem em outros readers e me avisem).

Ando pensando em generalizar um pouco os dois scripts, para facilitar a escrita de screen scrapers, mas não sei se vale a pena, já que eles são extremamente dependentes da estrutura da página. Mas vamos ver o que sai =D

Tidbits

Um monte de coisas que merecem ser ditas, mas pequenas demais para posts separados. Vamos lá:

- A minha palestra na PyConBrasil desse ano está no Google Video. Desde metade de novembro, mas só descobri agora =D

- Aliás, eu dei uma palestra em um evento chamado Mobile Expert, promovido pela Editora Europa, na Livraria Cultura. Os slides estão aqui. O detalhe é que eles erraram meu nome (que é com Z), o nome da linguagem (é PYTHON) e o nome da empresa (Pinuts StudioS, com S no final). Mas foi bem massa mesmo assim.

- Liniers lançou o Macanudo #6 por conta própria, e a primeira edição, de 5000 exemplares, saiu com a capa em branco. E ele DESENHOU TODAS A MÃO. Quando fiquei sabendo disso logo dei um jeito de comprar dois, por uma loja argentina que vende na internet. Eles chegaram no começo de dezembro, e são umas belezinhas. Um ficou comigo, o outro dei de presente de natal para a Na.

- Que, fodasticamente, fez três miniaturas em biscuit de alguns personagens da tirinha e me deu de Natal. Olhaê:

Duende, Fellini y El Misterioso Hombre de Negro

- Finalizando com Liniers: ontem ele deu um susto naquelas pessoas que acompanham a tirinha e não entendem espanhol muito bem. Só que ontem era Dia dos Inocentes em países hispânicos, o equivalente ao nosso Primeiro de Abril. Tsc.

- Agora MTV pega na parabólica. Yay! Não que seja grande coisa, mas pelo menos passa alguns clipes legais de madrugada, e por pior que esteja a programação ainda é uma opção interessante quando comparada aos outros canais disponíveis na TV aberta…

- Fallout 2 é FOODA. Tudo bem que tá todo mundo falando do 3, mas esse não roda no meu PC. Então aproveitei para jogar o Restoration Project, mod que adiciona alguns detalhes que tinham sido planejados, mas não foram implementados. Sweet!

- Alguém aí sabe jogar Magic? Ganhei um deck de natal, mas preciso de oponentes (de preferência compreensivos, e dispostos a explicar tudo).

- Ela prova que GG é lindo.

Presente de aniversário

O meu aniversário foi dia 17, mas há pessoas impacientes no mundo. No dia 05 de novembro já tinha recebido um presente =]

Teaser:

O presente:

Tão legal quanto o presente é a reação das pessoas. Os que nunca ouviram falar de Garage Kit e Evangelion dizem ‘Mas isso não é coisa de menininha?’. Os que entendem dizem ‘Que foda!’

Eu? Eu adorei, qualquer que seja a opinião dos outros =D

Obrigado, Na!

PETAR

Fim de semana emocionante. No sábado de manhã fomos eu, Maja (a croata que esteve morando com a gente por dois meses, fazendo intercâmbio em Sanca) e a Stéfanie (a vizinha) para o PETAR, a convite do Sinfa. Saímos bem cedo de São Carlos, cinco da manhã, passamos por São Paulo para encontrar o Sinfa e a namorada dele, e depois vamos em direção ao parque. Que, como fica na região sul de São Paulo, é longe pra dedéu. Chegamos lá só às quatro e tanto da tarde.

Mas já chegamos preocupados. Ao sair de São Carlos o tempo estava quente, chegando em Sampa chovia, e quanto mais para o sul íamos, mais a chuva castigava. E, ao chegar no PETAR, ela não tinha parado. Péssimo sinal, tínhamos poucas roupas para frio, e estávamos indo para o núcleo mais isolado e selvagem do lugar, o Caboclos.

Depois de sacolejar por mais uma hora em estradas de chão batido, chegamos ao local do acampamento. Várias cabanas, mas todas reservadas para pesquisadores. Vamos dar uma olhada no local do camping, e meus ossos já ficam gelados só de imaginar como vai ser a noite…

Eu e a Maja montamos rapidinho a barraca onde eu, ela e a Stéfanie vamos dormir, e depois vamos ajudar o Sinfa a montar a dele e da namorada (que, por sinal, é bem maior). Mais uns 20 minutos, e estamos suficientemente ensopados para começar a amaldiçoar a viagem. Ah, que vontade de estar em casa, assistindo a um filminho e comendo pipoca…

Mas como diz o sábio, já que tá que vá. Jantamos, e quando começamos a nos ajeitar para dormir, percebemos que a nossa barraca não é tão impermeável assim. Começamos a discutir seriamente a possibilidade de seguir viagem e ir para Curitiba. Mas antes que a situação piore, vamos todos para a barraca do Sinfa, e lá jogamos um pouco de truco antes de ir dormir (eu e Maja ganhamos, apesar de eu quase nunca jogar e de ser a primeira vez dela jogando =] )

No domingo de manhã somos acordados pela chegada do guia, mas a vontade de sair da cama era nula. Eu e a Stéfanie já tínhamos idéia de onde nos metemos, e somos contra seguir com a trilha planejada, até porque demoraríamos demais para ir embora e chegaríamos muito tarde em São Carlos. Mas a Maja ainda achava que seria uma trilha bem aberta e tranquila, com uma caverna acessível. Como eram 3 votos contra 2, vamos todos fazer a trilha.

Como eu sou um simplório, me diverti horrores. Cada vez que eu escorregava e caia na lama eu dava risada, possivelmente pela insanidade da coisa. E situações dessas são ótimas para deixar o humor negro e a ironia aflorar, então eu e a Stéfanie estávamos impossíveis. Pena que o guia não entendia metade das piadas que a gente fazia.

Aliás, o guia. Ele fez curso técnico em mecatrônica, mas enquanto não arranja um emprego trabalha no PETAR como guia. Ele deu umas explicações que deixariam cientistas de cabelo em pé (destaque para o Bagre Cego), mas era gente fina. E comentou, em certo ponto, que achou que nós seríamos um grupo de aventureiros experientes loucões, por querer ir na chuva na trilha mais difícil do parque. HAH! Um computeiro, uma croata que nunca viu mato na vida, e uma anêmica? Loucos com certeza, aventureiros contra a vontade, mas experientes nunca…

Depois de três horas de muitos tombos, arranhões e lama, chegamos até a caverna. Para descobrir que não podemos entrar, porque o rio está muito alto. Sem muitas opções, começamos a volta, agora por outro lado. Atravessamos o rio duas vezes (na segunda, muita correnteza), subimos uma encosta íngreme auxiliados por cordas, e cada vez mais me sinto um escoteirinho. O guia não está dando muita bola, mas a Maja e a Stéfanie estão exaustas, e várias vezes tenho que ajudá-las a atravessar alguns pontos. Aliás, é admirável a calma do guia: em vários pontos um escorregão levaria a uma queda beeeem horrenda, pelo menos uns 30 metros rolando ribanceira abaixo, e ele nem aí.

Perto do fim, a coisa está realmente feia. Maja desesperada porque vai ficar doente (err, e realmente ficou…), Stéfanie com hipoglicemia, Leandro e namorada um pouco mais atrás de nós. E eu? Sei lá de onde, mas ainda com energia para seguir adiante. Que dizer, sei sim, mas isso é assunto para outro post. Por mais que tenha sido uma roubada, que a gente tivesse que tomar banho frio logo depois (energia elétrica? pfff), que tívessemos que voltar para Sanca dividindo o único par de tênis limpos, eu estava me divertindo. Nada melhor do que rir de nós mesmos =D

Na segunda nem consegui ir trabalhar, foi muito difícil simplesmente sair da cama. Acho que a adrenalina da hora não deixou perceber que eu estava me estropiando todo.

Portanto, recomendações: Se for ao PETAR, comece pela caverna de Santana, e depois se arrisque nas mais selvagens. Vale a pena, mas evite ir na chuva! =D

UPDATE: já que tive um comentário ilustre por parte da Stéfanie, fique explicado que:

1) Ela ajudou a pregar no chão e a por a cobertura. Portanto, ajudou na montagem da nossa barraca.

2) Sim, eu fiquei a maior parte do tempo atrás dela. Ou seja, não fui de muita ajuda para ela (só quando ela escorregava e quase caia no chão, já no final da trilha). Ah, e também quando ela tava com hipoglicemia, servindo Negresco e dando apoio. Portanto, meu lado escoteirinho auxiliador apareceu mais para a Maja e, às vezes, pra namorada do Sinfa.

3) E ela sabe muito bem de onde eu tirei energia =]

#iphonedev-br

Buenas. Estava eu no #python-br quando o Elyézer perguntou algo sobre Python e Objective-C. Indiquei o PyObjC, e ele perguntou se ele estava disponível no iPhone. Hmm, não está.

Mas achei mais alguém desenvolvendo pra iPhone!

Antes que ficasse off-topic demais pra #python-br, fomos para o canal #iphonedev-br no Freenode. E agora precisamos popularizá-lo!

Então, se você procura por um lugar em português para discutir as agruras do Objective C (que, na minha opinião, é beeeem melhor que C++ …), apareça no #iphonedev-br @ irc.freenode.org .

UPDATE: Se você não tem um cliente IRC instalado, o mibbit pode te salvar. Dica do Frank!

Minha primeira hackeada

Memes de internet são como powerpoints no email: você pode até seguí-los ou lê-los, mas não deve repassar adiante essas pragas. Como o GG levantou a bola vou chutá-la, mas não recomendo que mais ninguém faça o mesmo! =D

Minha primeira hackeada? O primeiro computador lá de casa era um 286, comprado em 1993. Só rodava um editor de texto chamado Pangloss, e um misto de agenda, organizador pessoal e planilha chamado Every. E sei lá como, eu ainda conseguia me divertir com a bagaça. A minha mãe arranjou um curso de informática do Banco do Brasil chamado INFO2000, que ensinava enquanto contava uma historinha muito legalzinha sobre um viajante espacial.

E todos os softwares citados são tão velhos ou obscuros que não achei nada no Google sobre eles. Vou tentar recuperá-los da próxima vez que for para casa e achar mais informações.

Um dia meu primo levou ‘Outrun’ e ‘Prince of Persia’ em dois disquetes. E daí eu tive bastante o que fazer =D

Mas, hackeada mesmo, eu lembro que só fui fazer em um 486, em 1994. Um amigo meu trouxe vários Commander Keen (2, 3 e 5, se não me engano), e eu fiz um .BAT com um menu para acessar os jogos. Inútil, mas foi meu primeiro ’shell script’: tinha que imprimir mensagens, ler a entrada do usuário, e executar o jogo correto.

Além disso, eu lembro que tive que fuçar várias vezes no CONFIG.SYS e no AUTOEXEC.BAT para desabilitar algumas coisas a fim de liberar memória (ah, os saudosos 640KB…) para algum jogo. Pra quem tinha nove anos na época e não tinha com quem aprender, até que está bom, né?

Uma outra história interessante: quando eu ia na quarta série as freiras do meu colégio compraram um computador e deixaram na secretaria. E, certo dia, a secretária foi me chamar na sala de aula, porque não estava conseguindo fazer algo (não lembro o quê, fazem 14 anos, oras!). Fui lá, resolvi, e voltei para a aula. Suporte técnico desde criança, hehehe.