Posts de September, 2010

[Emerson Macedo] Hospedagem gratuita NodeJS com Joyent Node Smartmachines

Thursday, September 30th, 2010

Já faz algum tempo que a Joyent está investindo em  NodeJS e pelo visto com força total. Passado 1 mês do Node Knockout,  a Joyent está disponibilizando hospedagem beta para o NodeJS. Essa liberação está acontecendo gradualmente e atualmente é necessário um token. Basicamente você solicita esse token e entra numa fila. Não demora muito e eles enviam um email pra você avisando que o token já está disponível. A partir daí basta recupera-lo e usar para criar sua instância.

Uma coisa legal também é que a Joyent criou uma API REST, onde é possível fazer tudo pela linha de comando. Vamos criar e fazer deploy de uma aplicação simples.

Criando a conta:

$ curl -k https://api.no.de/account \
    -F "email=user@domain.com" \
    -F "username=user" \
    -F "password=pass" \
    -F "password_confirmation=pass"

Adicionando uma chave ssh:

 $ curl -k -u user:pass https://api.no.de/sshkeys \
    -F "name=seunome" -F "key=@/Users/seuusuario/.ssh/id_rsa.pub"

Solicitando um coupon para provisionar uma máquina:

$ curl -k -u user:pass https://api.no.de/heart -X POST

A partir daí, você deve esperar o seu email chegar, avisando que o coupon está liberado. Como eu disse, essa liberação é gradual. Portanto, muita calma nessa hora. Talvez você tenha que voltar no artigo depois, portanto guarde o link :)

Provisionando sua máquina (quando o coupon estiver liberado):

Pegue o coupon:

$ curl -k -u user:pass https://api.no.de/coupons

Se você fizer essa busca antes do coupon estar liberado virá uma resposta vazia.

Provisione com o coupon recebido:

 $ curl -k -u user:pass https://api.no.de/smartmachines/node \
    -F "coupon=123456789abcdefghijk" \
    -F "subdomain="seusubdominio"

Nesse momento você já tem uma máquina apontando para o endereço: htttp://seusubdominio.no.de

Primeiro deploy

O deploy para uma Node Smartmachine é feito via git, assim como no heroku. Sendo assim, vamos criar um projeto simples, com um hello world e fazer o deploy:

$ mkdir seuprojeto && cd seuprojeto

Crie um arquivo chamado server.js no editor de sua preferência e adicione o seguinte código:

var http = require('http');

var server = http.createServer(function (request, response) {
  response.writeHead(200, {'Content-Type': 'text/plain'});
  response.end('Hello NodeJS Smartmachine\n');
});

server.listen(Number(process.env.PORT));

Pegue as informações (IP) da sua máquina para configurar o repositório remoto do git:

$ curl -k -u user:pass https://api.no.de/smartmachines/node

Inicialize o repositório git, faça o primeiro commit e adicione o repositório remoto:

git init
git add .
git commit -am "Primeiro commit"
git remote add joyent ssh://node@seuip/repo

Agora vamos ao que mais interessa. Deploy !!!!

git push joyent master

Pronto!!! Agora acesse htttp://seusubdominio.no.de e você deve ver a mensagem Hello NodeJS Smartmachine.

Gerênciando pela interface web

Para gerenciar sua conta visualmente basta entrar em http://no.de e entrar com seu usuário e senha. É uma ótima opção também. Nesse site também é possível criar novas contas, adicionar chaves ssh e provisionar máquinas. Não é possível

Conclusão

Essa hospedagem de NodeJS me parece bem promissora, e eu sinceramente estou gostando muito. A hospedagem permite instalar pacotes usando NPM e também instalar algumas outras coisas. No próximo artigo vou explorar detalhes de administração dessa hospedagem.

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

[Rafael Biriba] Net-sftp: Solução para timeout durante o upload

Friday, September 24th, 2010

Desenvolvendo um script em ruby para upload de arquivos, utilizando a gem net-sftp, acabei encontrando um problema. A falta de timeout durante a transferência.

Decidi escrever esse post, para compartilhar a minha solução e também receber opniões e outras soluções para o problema. Fiz algo parecido quando escrevi o post sobre o cron de 15 em 15 segundos e obtive resultados bem legais.

Introdução ao problema
A gem net-sftp é bastante utilizada em scripts ruby que requerem alguma transferência de arquivo utilizando uma conexão segura, como por exemplo, upload, download, criação de diretórios e etc… Utilizando a gem net-ssh para estabelecer essa conexão.

Primeiramente, vamor ver um exemplo do problema:

1
2
3
4
5
require 'rubygems'
require 'net/sftp'
Net::SFTP.start('192.168.0.2', 'rafaelbiriba', {:password => 'teste', :timeout => 3}) do |sftp|
  sftp.upload!("/Users/rafaelbiriba/Projects/temp/video-teste-sftp.mp4", "/home/rafaelbiriba/video-teste-sftp.mp4")
end

O código acima faz o upload de um arquivo de mais de 400 Mb. Se durante o upload houver um problema na sua conexão, o upload fica “esperando” a rede voltar, o que é um problemão, pois ele trava a execução do script, sem dar nenhum sinal de vida.

De acordo com a documentação do net-sftp, você pode utilizar o método “upload” no lugar de “upload!” para não travar a execução do script. Mas ainda sim o problema continua. Fazendo isso, você vai precisar rodar um loop até que o upload termine. Ou seja, se a conexão cair, o método só vai terminar(e sair do loop) quando a conexão voltar. Então essa opção foi logo descartada.

O problema mesmo só acontece quando há perda de conexão depois que a sessão inicia. (O parâmetro :timeout apenas estabelece o limite de espera para iniciar a sessão e não durante a transferência dos arquivos.)

Se o net-sftp / net-ssh aceitasse a opção “ServerAliveInterval” (Parametro de configuração do ssh), ele iria verificar a conexão durante a transferência e ao alcançar o limite (“ServerAliveCountMax”) seria disparado o timeout. Infelizmente, de acordo com a documentação do net-ssh esses parâmetros não estão disponíveis.

Seguindo com a leitura e busca pela solução na documentação do net-sftp, achei uma possível pista para encontrar a solução:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
require 'rubygems'
require 'net/sftp'
Net::SFTP.start('192.168.0.2', 'rafaelbiriba', {:password => 'teste', :timeout => 3}) do |sftp|
  sftp.upload!("/Users/rafaelbiriba/Projects/temp/video-teste-sftp.mp4", "/home/rafaelbiriba/video-teste-sftp.mp4") do |event, uploader, *args|
    case event
    when :open then
      # args[0] : file metadata
      puts "starting upload: #{args[0].local} -> #{args[0].remote} (#{args[0].size} bytes}"
    when :put then
      # args[0] : file metadata
      # args[1] : byte offset in remote file
      # args[2] : data being written (as string)
      puts "writing #{args[2].length} bytes to #{args[0].remote} starting at #{args[1]}"
    when :finish then
      puts "all done!"
    end
  end
end

Acima temos um exemplo simples retirado da documentação para o controle do progresso de upload.

Quando o upload inicia, o case recebe o evento “:open” onde podemos obter o tamanho total do arquivo e logo em seguida recebe os eventos “:put” onde é possível verificar  o quanto (em bytes) do arquivo já foi enviado, possibilitando assim a criação da porcentagem do upload.

Como o foco aqui não é mostrar como obter o progresso (posso mostrar isso exclusivamente em outro post), não vou entrar muito em detalhes sobre isso. O que realmente inporta nesses eventos são que durante o upload (:put), ao perder a conexão, nenhum evento é disparado, ou seja, o script não tem como sabe que a conexão caiu e a execução fica travada no upload.

Minha solução:

Depois de todo esse estudo detalhado sobre o problema, comecei a pensar numa possível solução. Eu precisava utilizar alguma coisa que pudesse verificar e dar timeout no upload caso ele parasse de responder/transferir.

Foi então que pesquisei, testei e implementei o rufus-scheduler,  um agendador de tarefas que roda dentro do script ruby(mas numa outra thread), onde posso definir um intervalo de execução para executar alguma coisa, como se fosse um cron.

Então a solução foi: Assim que o upload começar, eu defino um tempo de 10 segundos para ele executar uma tarefa com “raise exception”. Então toda vez que o upload responder ao evento (:put), informando que conseguiu subir mais um trecho do arquivo, eu reseto o tempo de execução da tarefa e defino novamente em 10 segundos. Fazendo isso, quando a conexão cair ou o upload travar por algum motivo, ele vai parar de responder ao evento “:put”, e então a tarefa agendada para 10 segundos vai disparar o raise, interrompendo a execução do script.

Depois de alguns testes, tanto de desempenho quanto de falhas, percebi que essa resolução serviu perfeitamente. Segue o exemplo do código utilizado:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
require 'rubygems'
require 'rufus/scheduler'
require 'net/sftp'
 
@job = nil
@scheduler = Rufus::Scheduler::PlainScheduler.start_new
def @scheduler.handle_exception (job, exception)
  abort(exception.message)
end
 
def job_scheduler
  @job.unschedule unless @job.nil?
  @job = @scheduler.every "10s" do
    raise "SFTP Connection Lost or upload freezed timeout"
  end
end
 
Net::SFTP.start('192.168.0.2', 'rafaelbiriba', {:password => 'teste', :timeout => 3}) do |sftp|
  sftp.upload!("/Users/rafaelbiriba/Projects/temp/video-teste-sftp.mp4", "/home/rafaelbiriba/video-teste-sftp.mp4") do |event, uploader, *args|
    case event
    when :open then
      puts "starting upload: #{args[0].local} -> #{args[0].remote} (#{args[0].size} bytes}"
      job_scheduler
    when :put then
      puts "writing #{args[2].length} bytes to #{args[0].remote} starting at #{args[1]}"
      job_scheduler
    when :finish then
      puts "all done!"
     @job.unschedule unless @job.nil?
    end
  end
end

Quando o upload inicia (:open) e durante a transferência (:put), ele chama o método job_scheduler que é responsável por resetar a tarefa anterior(se houver) e agendar uma nova com timeout de 10 segundos.

Conforme expliquei acima, se o upload travar, o tempo limite de 10 segundos é alcançado e uma exceção é disparada.

Como o agendador do rufus roda em outra thread, qualquer exceção disparada é capturada e tratada pelo próprio rufus, fazendo com que a execução do script continue. Então, sobrescrevi o método (handle_exception) e ao receber a exceção, gero um abort que irá interromper todo o script.

Infelizmente, são poucos os sites relacionados sobre esse assunto, o que dificultou bastante a busca e o desenvolvimento. Mesmo assim, acredito ter resolvido o problema com uma solução bem razoável.

Já falei em cima, mas vale ressaltar:
“Decidi escrever esse post, para compartilhar a minha solução e também receber opniões e outras soluções para o problema. Fiz algo parecido quando escrevi o post sobre o cron de 15 em 15 segundos e obtive resultados bem legais.”

Estou pensando em tentar solucionar isso direto no código do net-sftp e quem sabe mandar um patch para o autor. De qualquer forma, fico no aguardo de qualquer comentário e/ou soluções melhores.

handle_exception

Google Bookmarks Twitter Yahoo Messenger Orkut Hotmail Google Gmail Delicious Share/Bookmark

Leia também:


[Andrews Medina] Testando tags e filtros customizados no Django

Saturday, September 18th, 2010

O sistema de templates do Django traz nativamente uma grande variedade de tags e filtros, mas, em vários casos é necessário criar tags e filtros customizados.

Criar tags e filtros está bem documentado na documentação oficial do Django, mas, como testar tags e filtros customizados é algo que gera muitas dúvidas em vários desenvolvedores.

Uma forma simples para testar filtros e tags é utilizar a própria Template API do Django para criar e renderizar uma template que utilize um filtro ou tag a ser testado.

Um teste para um filtro ‘reverse’ que inverte uma string, seria da seguinte maneira:



from django.test import TestCase

from django.template import Context, Template

class TestFilter(TestCase):

def test_lower_filter(self):

html = ‘{% load reverse %}’

html += ‘{{ username|reverse }}’

template = Template(html)

context = Context({’username’: ‘ozzy’})

assert ‘yzzo’ == template.render(context)

Assim é possivel fazer TDD no desenvolvimento de filtros e tags sem sofrimento.

[Enrico Batista] Instalando nodejs e npm no Ubuntu sem sudo

Friday, September 17th, 2010

O npm, instalador de pacotes do nodejs, recomenda que não seja instalado usando sudo, pois você acabaria instalando código de terceiros, com livre acesso ao seu sistema de arquivos. É mais fácil instalar o npm sem sudo se o próprio node também for instalado assim. Instalando o node sem sudo (fonte): # Clone do projeto […]

[Enrico Batista] Instalando nodejs e npm no Ubuntu sem sudo

Friday, September 17th, 2010

O npm, instalador de pacotes do nodejs, recomenda que não seja instalado usando sudo, pois você acabaria instalando código de terceiros, com livre acesso ao seu sistema de arquivos. É mais fácil instalar o npm sem sudo se o próprio node também for instalado assim. Instalando o node sem sudo (fonte): # Clone do projeto [...]

[Rafael Biriba] FFmpeg: Convertendo um video em um gif animado

Friday, September 17th, 2010

FFmpeg: http://www.ffmpeg.org/

Há alguns dias atrás, me perguntaram se tinha como converter trechos de vídeos para um arquivo .gif, para ser usado em lugares que só aceitam imagens, como foruns, msn e etc…

Abaixo segue um exemplo simples que faz isso, usando FFmpeg:

ffmpeg -i teste.avi -pix_fmt rgb24 -f gif teste.gif

Detalhes:

  • -i = especifica o arquivo de entrada (teste.avi)
  • -pix_fmt = especifica o formato dos pixels (rgb24, formato suportado pelo gif  )
  • -f = força a saida para .gif
  • teste.gif é o arquivo de saída do comando

Se você precisa de uma solução mais específica, abaixo segue alguns outros comandos e as explicações dos parâmetros:

GIF com tamanho final específico e com loop:

ffmpeg -i teste.avi -pix_fmt rgb24 -r 10.0 -loop_output 0 -f gif -s 120x120 teste.gif

Detalhes:

  • -r = especifica o frame rate do gif
  • -loop_output = especifica quantas vezes o gif vai entrar em loop (zero para infinito)
  • -s = especifica o tamanho do gif em pixels

GIF com inicio definido pelo tempo e final limitado por frames:

ffmpeg -i widescreen.avi -pix_fmt rgb24 -r 10.0 -loop_output 0 -vframes 100 -ss 00:00:02 -f gif teste.gif

Detalhes:

  • -ss = especifica a posição inicial do video para gravação (formato: hh:mm:ss)
  • -vframes = limita a quantidade de frames que serão gravados a partir do video original
  • No exemplo, o gif será formado apenas pelos 100 primeiros frames depois da posição 2 segundos

GIF com inicio definido pelo tempo e final limitado pelo tempo:

ffmpeg -i widescreen.avi -pix_fmt rgb24 -r 10.0 -loop_output 0 -ss 00:00:02 -t 00:00:04 -f gif teste.gif

Detalhes:

  • -ss = especifica a posição inicial do video para gravação (formato: hh:mm:ss)
  • -t = especifica a posição final de gravação do video (formato: hh:mm:ss)
  • No exemplo, o gif será criado a partir do segundo 2 até o segundo 4 do vídeo.

Você também pode experimente a combinação dos parâmetros, até encontrar o comando que lhe sirva melhor… ;)

Google Bookmarks Twitter Yahoo Messenger Orkut Hotmail Google Gmail Delicious Share/Bookmark

Leia também:

[Enrico Batista] Desligar caps-lock automaticamente no Vim

Monday, September 13th, 2010

Estou usando o Vim mais a cada dia e existe algo que vem me incomodando há algum tempo. Para escrever trechos em maiúsculas, gosto de ativar o caps-lock e quando saio do modo de edição para o modo de comando as maiúsculas têm funções diferentes, então, por exemplo, ao invés de descer uma linha com […]

[Enrico Batista] Desligar caps-lock automaticamente no Vim

Monday, September 13th, 2010

Estou usando o Vim mais a cada dia e existe algo que vem me incomodando há algum tempo. Para escrever trechos em maiúsculas, gosto de ativar o caps-lock e quando saio do modo de edição para o modo de comando as maiúsculas têm funções diferentes, então, por exemplo, ao invés de descer uma linha com [...]

[Francisco Souza] Flying with tipfy on Google App Engine

Wednesday, September 8th, 2010

Hooray, there is a bonus part in the series (after a looooooooooooong wait)! In the first blog post, about Django, I received a comment about the use of tipfy, a small Python web framework made specifically for Google App Engine. Like Flask, tipfy is not a full stack framework and we will not use a database abstraction layer, we will use just the Google App Engine Datastore API, but tipfy was designed for Google App Engine, so it is less laborious to work with tipfy on App Engine.

First, we have to download tipfy. There are two options on official tipfy page: an all-in-one package and a do-it-yourself packaged. I am lazy, so I downloaded and used the all-in-one package. That is so easy:

% wget http://www.tipfy.org/tipfy.build.tar.gz% tar -xvzf tipfy.0.6.2.build.tar.gz% mv project gaeseries

After it, we go to the project folder and see the project structure provided by tipfy. There is a directory called “app”, where the App Engine app is located. The app.yaml file is in the app directory, so we open that file and change the application id and the application version. Here is the app.yaml file:

application: gaeseriesversion: 4runtime: pythonapi_version: 1

derived_file_type:- python_precompiled

handlers:- url: /(robots\.txt|favicon\.ico)  static_files: static/\1  upload: static/(.*)

- url: /remote_api  script: $PYTHON_LIB/google/appengine/ext/remote_api/handler.py  login: admin

- url: /_ah/queue/deferred  script: main.py  login: admin

- url: /.*  script: main.py

After this, we can start to code our application. tipfy deals with requests using handlers. A handler is a class that has methods to deal with different kinds of requests. That remember me a little the Strut Actions (blergh), but tipfy is a Python framework, what means that it is easier to build web application using it!

Understanding tipfy: a URL is mapped to a handler that do something with the request and returns a response. So, we have to create two handlers: one to the list of posts and other to create a post, but let’s create first an application called blog, and a model called Post. Like Django, Flask and web2py, tipfy also works with applications inside a project.

To create an application, we just need to create a new Python package with the application name:

% mkdir blog% touch blog/__init__.py

After create the application structure, we install it by putting the application inside the “apps_installed” list on config.py file:

# -*- coding: utf-8 -*-"""    config    ~~~~~~

    Configuration settings.

    :copyright: 2009 by tipfy.org.    :license: BSD, see LICENSE for more details."""config = {}

# Configurations for the 'tipfy' module.config['tipfy'] = {    # Enable debugger. It will be loaded only in development.    'middleware': [        'tipfy.ext.debugger.DebuggerMiddleware',    ],    # Enable the Hello, World! app example.    'apps_installed': [        'apps.hello_world',        'apps.blog',    ],}

See the line 22. Inside the application folder, let’s create a Python module called models.py. This module is exactly the same of Flask post:

from google.appengine.ext import db

class Post(db.Model):    title = db.StringProperty(required = True)    content = db.TextProperty(required = True)    when = db.DateTimeProperty(auto_now_add = True)    author = db.UserProperty(required = True)

After create the model, let’s start building the project by creating the post listing handler. The handlers will be in a module called handlers.py, inside the application folder. Here is the handlers.py code:

# -*- coding: utf-8 -*-from tipfy import RequestHandlerfrom tipfy.ext.jinja2 import render_responsefrom models import Post

class PostListingHandler(RequestHandler):    def get(self):        posts = Post.all()        return render_response('list_posts.html', posts=posts)

See that we get a list containing all posts from the database and send it to the list_posts.html template. Like Flask, tipfy uses Jinja2 as template engine by default. Following the same way, let’s create a base.html file who represents the layout of the project. This file should be inside the templates folder and contains the following code:

<html>    <head>      <meta http-equiv="Content-type" content="text/html; charset=utf-8"/>      <title>{% block title %}{% endblock %}</title>    </head>    <body id="">        {% block content %}{% endblock %}    </body></html>

And now we can create the list_posts.html template extending the base.html template:

{% extends "base.html" %}

{% block title %}    Posts list{% endblock %}

{% block content %}    Listing all posts:

    <ul>        {% for post in posts %}            <li>                {{ post.title }} (written by {{ post.author.nickname() }})                {{ post.content }}            </li>        {% endfor %}    </ul>{% endblock %}

Can we access the list of posts now by the URL? No, we can’t yet. Now we have to map the handler to a URL, and we will be able to access the list of posts through the browser. On tipfy, all URL mappings of an application are located in a Python module called urls.py. Create it with the following code:

from tipfy import Rule

def get_rules(app):    rules = [        Rule('/posts', endpoint='post-listing', handler='apps.blog.handlers.PostListingHandler'),    ]

    return rules

It is very simple: a Python module containing a function called get_rules, that receives the app object as parameter and return a list containing the rules of the application (each rule is an instance of tipfy.Rule class). Now we can finally see the empty post list on the browser, by running the App Engine development server and touching the http://localhost:8080/posts URL on the browser. Run the following command on the project root:

% /usr/local/google_appengine/dev_appserver.py app

And check the browser at http://localhost:8080/posts. And we see the empty list. Now, let’s create the protected handler which will create a new post. tipfy has an auth extension, who makes very easy to deal with authentication using the native Google App Engine users API. To use that, we need to configure the session extension, changing the conf.py module, by adding the following code lines:

config['tipfy.ext.session'] = {    'secret_key' : 'just_dev_testH978DAGV9B9sha_W92S',}

Now we are ready to create the NewPostHandler. We will need to deal with forms, and tipfy has an extension for integration with WTForms, so we have to download and install WTForms and that extension in the project:

% wget http://bitbucket.org/simplecodes/wtforms/get/tip.tar.bz2% tar -xvf tip.tar.bz2% cp -r wtforms/wtforms/ ~/Projetos/gaeseries/app/lib/% wget http://pypi.python.org/packages/source/t/tipfy.ext.wtforms/tipfy.ext.wtforms-0.6.tar.gz% tar -xvzf tipfy.ext.wtforms-0.6.tar.gz% cp -r tipfy.ext.wtforms-0.6/tipfy ~/Projetos/gaeseries/app/distlib

Now we have WTForms extension installed and ready to be used. Let’s create the PostForm class, and then create the handler. I put both classes in the handlers.py file (yeah, including the form). Here is the PostForm class code:

class PostForm(Form):    csrf_protection = True    title = fields.TextField('Title', validators=[validators.Required()])    content = fields.TextAreaField('Content', validators=[validators.Required()])

Add this class to the handlers.py module:

class NewPostHandler(RequestHandler, AppEngineAuthMixin, AllSessionMixins):    middleware = [SessionMiddleware]

    @login_required    def get(self, **kwargs):        return render_response('new_post.html', form=self.form)

    @login_required    def post(self, **kwargs):        if self.form.validate():            post = Post(                title = self.form.title.data,                content = self.form.content.data,                author = self.auth_session            )            post.put()            return redirect('/posts')        return self.get(**kwargs)

    @cached_property    def form(self):        return PostForm(self.request)

A lot of news here: first, tipfy explores the multi-inheritance Python feature and if you will use the auth extension by the native App Engine users API, you have to create you handler class extending AppEngineAuthMixin and AllSessionMixins classes, and add to the middleware list the SessionMiddleware class. See more at the tipfy docs.

The last step is create the new_post.html template and deploy the application. Here is the new_post.html template code:

{% extends "base.html" %}

{% block title %}    New post{% endblock %}

{% block content %}    <form action="" method="post" accept-charset="utf-8">        <p>            <label for="title">{{ form.title.label }}</label>

            {{ form.title|safe }}

            {% if form.title.errors %}            <ul class="errors">                {% for error in form.title.errors %}                <li>{{ error }}</li>                {% endfor %}            </ul>            {% endif %}        </p>        <p>            <label for="content">{{ form.content.label }}</label>

            {{ form.content|safe }}

            {% if form.content.errors %}            <ul class="errors">                {% for error in form.content.errors %}                <li>{{ error }}</li>                {% endfor %}            </ul>            {% endif %}        </p>        <p><input type="submit" value="Save post"/></p>    </form>{% endblock %}

Now, we can deploy the application on Google App Engine by simply running this command:

% /usr/local/google_appengine/appcfg.py update app

And you can check the deployed application live here: http://4.latest.gaeseries.appspot.com.

The code is available at Github: https://github.com/fsouza/gaeseries/tree/tipfy.

[Rafael Biriba] Captcha no Travian: Será o fim dos scripts ?

Wednesday, September 8th, 2010

Apesar de afastado à algum tempo do jogo, resolvi escrever este post para esclarecer algumas coisas sobre a nova implementação de segurança do Travian.

Como todos já devem ter notado, principalmente a galera que está jogando com scripts no greasemonkey, a equipe de desenvolvedores do Travian adotou um novo modo de evitar os scripts… Captcha !

Mas primeiro, o que é o Captcha (wiki) ?
Captcha é um tipo de teste cognitivo mais utilizado como ferramenta anti-spam. Na prática, são testes com textos confusos, onde um computador ou um robô ou neste caso um script do travian seja incapaz de responder corretamente.

O captcha é gerado aleatóriamente pelo servidor, e você deve responder corretamente para poder continuar a navegar. Atualmente já existem meios para quebrar essa segurança por captcha, porém, não há nada desenvolvido para o travian ainda.

O problema no travian…
Alguns servidores no travian, e logo serão em todos, já possuem captcha funcionando. Existem um algorítmo rodando aleatoriamente no servidor do travian, para descobrir quem está usando scripts no jogo, e ao detectar alguém, ele habilita o captcha para essa pessoa e até algumas penalidades a mais.

Como ele descobre ?
Toda vez que você está jogando, a cada clique que você der, será uma requisição feita ao servidor do travian. Esse algorítmo verifica o intervalo de tempo com que essa requisição é feita,… Se for um intervalo curto e similar aos outros, ele acha que é um robô (script) e automaticamente habilita o captcha.

Logicamente, se você abrir muitas abas do seu navegador, e fizer muitos ataques rapidamente navegando entre as abas, é bem provável que você receba o captcha.

Punições
Como eu falei no inicio, estou meio afatado do travian e por isso não sei se o sistema de punição já está funcionando. Mas se estiver, funciona da seguinte forma:

  • Detectado pela primeira vez: 24 horas de captcha e armazens são esvaziados
  • Detectado pela segunda vez: 24 horas de captcha, armazens são esvaziados e 25% das tropas são perdidas
  • Detectado pela terceira vez: 24 horas de captcha, armazens são esvaziados e 50% das tropas são perdidas
  • Detectado pela quarta vez:  O jogador recebe banimento e o multi-hunter é avisado.

É muito importante relembrar que todas essas punições e verificações são feitas automaticamente. O algorítmo roda aleatoriamente no servidor do travian, então se você tiver usando script e ainda não foi pego, pode ter sido apenas sorte por enquanto…

Defeitos no sistema
Essa implementação ficou uma droga. Existe um video no youtube (http://www.youtube.com/watch?v=IRD8tBD3oRM) que mostra um caso onde o captcha aparece sem utilizar nenhum script. Como eu falei acima, se você for rápido no mouse, pode ser que seja punido como um robô.

E os scripts do greasemonkey ? O que fazer ?
Se o sistema de punição com o captcha estiver mesmo 100% funcionando, eu sugiro que desabilitem o greasemonkey. Até pior que isso, além de não usar mais os scripts, seria bom também evitar muitos cliques simultâneos e jogar mais “relaxadamente” até esse problema ser melhor resolvido.

E agora, o que fazer ?
Na verdade, não há muito o que fazer… Todo esse processo de detecção não depende dos multi-hunters… Eles nem podem fazer nada a respeito.
Alguns jogadores e ex-jogadores estão se mobilizando e reclamando, muito por sinal, no forum do travian. Nos foruns do mundo todo há reclamações de mau funcionamento do novo sistema. Eu gosto da idéia de reclamar no fórum. É a única coisa que podemos fazer e que está ao nosso alcance.

Concluindo…
Na minha opnião, acho muito difícil detectar um script rodando no greasemonkey… Ele simula cliques do mouse… Se você for tão rápido quanto os scripts, será detectado e punido como ele. Resta agora torcer para que eles desabilitem a ferramenta, ou então melhore a forma de detecção.

A pergunta que não quer calar: Captcha no Travian: Problema ou Solução ?
Será mesmo que a equipe conseguiu evitar os scripts finalmente ?
Espero ter conseguido esclarecer um pouco sobre o assunto… Se você tiver uma opnião, um outro ponto de vista, ou até mesmo uma experiência do jogo sobre isso, não deixe de compartilhar conosco.

Até a próxima pessoal…


Leia também: