Processando emails recebidos no Rails com MailMan

Processar e-mails recebidos em um aplicativo Rails parece uma tarefa complicada. A maioria das soluções disponíveis atualmente envolve a configuração de um servidor de e-mail dedicado só para isso. Não parece ser uma boa idéia.

Quem também acha isso é o Jonathan Rudenberg que junto com mais uma galera, desenvolveu no Ruby Summer of Code do ano passado, o Mailman. Um microframework para processamento de e-mails recebidos.

A funcionalidade é bem simples. Resumindo, você configura uma caixa postal que o Mailman vai varrer e uma callback para ser executada para cada e-mail que corresponder ao critério que você estabelecer.

Um exemplo simples:

require 'mailman'
Mailman::Application.run do
  to 'post-%id%@blog.com' do

    Post.find(params[:id]).comments << Comment.create(:body => message)
  end
end

Mas vamos devagar. Começando pelo começo.

Como sempre, no seu Gemfile inclua a gem do Mailman:

gem 'mailman', '0.4.0'

Execute o bundler:

bundle install

No seu Mailer, defina a callback a ser chamada para os e-mails recebidos:

class Mailer < ActionMailer::Base
  def receive(email)
    # o seu código entra aqui
  end
end

Depois disso é necessário configurar o Mailman. Recomendamos a criação de um script (em script/mailman). Como abaixo:

#!/usr/bin/env ruby
require File.expand_path('../../config/application', __FILE__)
require "mailman"

Mailman.config.poll_interval = 0
Mailman.config.ignore_stdin = true
Mailman.config.logger = Logger.new('log/mailman.log')
Mailman.config.pop3 = {
  :username => 'incoming@example.com',
  :password => '************',
  :server   => 'pop.gmail.com',
  :port     => 995,
  :ssl      => true
}

Mailman::Application.run do
  to 'dropbox+%domain%+%type%+%type_id%@%host%' do
    UserMailer.receive(message)
  end
end

Vamos analisar as configurações mais importantes:

Mailman.config.poll_interval: define de quanto em quanto tempo o Mailman deve executar. Nós vamos configurar o cron para executar o script/mailman periodicamente, então devemos setar essa configuração para zero. Assim ele vai executar apenas uma vez e sair.

Mailman.config.ignore_stdin: essa configuração serve para ignorar e-mails passados diretamente para o Mailman (como no comando: cat plainmessage.eml | ruby script/mailman).

Mailman.config.logger: seta o arquivo onde você quer que o log seja salvo.

Mailman.config.pop3: configura o mailman para usar pop3 como receiver. O Mailman pode ser configurado para trabalhar com três receivers: pop3, standard input ou maildir (onde ele varre uma pasta local a espera de novas mensagens).

No nosso caso estamos usando o Gmail através do Google Apps.

Logo abaixo vem o método Mailman::Application.run, que é onde a mágica acontece.

Nele nós podemos configurar o que, no linguajar do Mailman, são chamados de ‘rotas’. Uma rota consiste em um método ( to, from, cc, subject, body), uma string ou regex e um bloco.

O método diz onde o Mailman vai procurar pela string ou regex, no nosso caso estamos procurando no campo “to” do e-mail. Todo e-mail cujo “to” bater com a string vai ser passada para o bloco. As partes do “to” que forem capturadas pelo parte %domain%, por exemplo, estará disponível no bloco como params[:domain]. A mensagem então é passada para o método receive do Mailer.

Agora, com o script pronto, precisamos nos assegurar que ele rode regularmente no servidor. Para isso existe o Cron, e nós usaremos o Whenever para agendar a execução do script/mailman.

Adicione essas linhas no seu schedule.rb:

job_type :command, "cd :path && :task :output" # para executar o comando de dentro do aplicativo
every 1.minute do
  command "script/mailman"
end

Configure a periodicidade de acordo com sua necessidade e pronto. Não foi tão difícil!

2 comentários : 11.01.2011 12:21 PM

Criando um servidor smtp com postfix the simple way

Problema

O Redeparede dispara ~150k e-mails por mês. Não é mailing list, são as ações dos usuários mesmo: cadastro, recuperação de senha, notificação de comentários etc. Nós usávamos um servidor smtp da Engine Yard, mas recentemente movemos para o EC2 da Amazon e lá não temos no plano um servidor smtp. Tentamos usar alguns comerciais comuns, mas todos tinham limite de envio de e-mail por conta. Passamos então a usar o authsmtp, que é um serviço de smtp especializado, mas a conta começou a ficar cara a medida que estávamos disparando muitos e-mails.

A solução então foi fazer o que eu não queria inicialmente: configurar um smtp próprio. E-mail é sempre um problema. Configurar um servidor se mostrou um problema ainda maior do que eu imaginava. A nossa necessidade é muito simples: precisamos autenticar remotamente num servidor smtp para disparar e-mails. Apenas uma conta autenticada para enviar e-mails. Só isso. Só.

Buscar na internet sobre o assunto retorna configurações megalomaníacas usando mysql, postgresl, mil tipos diferentes de autenticação, mas não tem o simples!

Vamos ao trabalho.

DNS reverso

Você precisa ter o dns reverso configurado, sem isso todos os seus e-mails vão para caixa de spam. Você tem que verificar no lugar onde seu servidor está hospedado como configurar o reverso. No caso do Slicehost, onde hospedamos o nosso smtp, isso é configurado a partir de uma interface web bem simples. Para saber se está funcionando é só digitar no console:

dig -x 67.23.23.229 (substitua pelo ip do seu servidor, claro ;)

A resposta tem que ser algo parecido com:

; <<>> DiG 9.5.0-P2 <<>> -x 67.23.23.229
;; global options:  printcmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 57850
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 2, ADDITIONAL: 2

;; QUESTION SECTION:
;229.23.23.67.in-addr.arpa. IN  PTR

;; ANSWER SECTION:
229.23.23.67.in-addr.arpa. 86400 IN PTR smtp.redeparede.com. # <=== essa linha que importa, ela está dizendo que o nosso ip está apontando para o endereço correto

;; AUTHORITY SECTION:
23.23.67.in-addr.arpa.  3600    IN  NS  ns2.slicehost.net.
23.23.67.in-addr.arpa.  3600    IN  NS  ns1.slicehost.net.

;; ADDITIONAL SECTION:
ns1.slicehost.net.  95375   IN  A   67.23.4.57
ns2.slicehost.net.  88271   IN  A   173.45.224.132

;; Query time: 158 msec
;; SERVER: 201.17.0.114#53(201.17.0.114)
;; WHEN: Tue Sep 15 13:42:16 2009
;; MSG SIZE  rcvd: 157

Caso você não possua o dig instalado:

sudo aptitude install dnsutils

Configurando o SPF

O SPF é uma entrada TXT no DNS que diz quais servidores estão autorizados a enviar e-mails do seu domínio. Esse registro é consultado pelo servidor que recebe seu e-mail e isso ajuda a você não cair na caixa de spam. No nosso caso os servidores são do Google e o nosso próprio smtp. A linha fica assim:

"v=spf1 a mx include:smtp.redeparede.com include:aspmx.googlemail.com include:_spf.google.com ~all"

Instalando e configurando o Postfix

Nós queríamos a configuração mais simples de todas. O que conseguimos foi fazer o postfix autenticar usando a própria base de usuários/senhas do sistema. Para simplificar as coisas eu fiz um script que executa os passos em sequencia. Baixe e execute como root num servidor limpo. Foi testado apenas no Ubuntu 9.04 (Jaunty Jackalope).

install_and_configure_postfix_ubuntu.sh

Primeiro vai pedir o seu domínio. Assumi que o servidor será smtp.seudominio.com, se não for esse o seu desejo, edite o script e mude na mão. Na instalação do postfix ele vai perguntar algumas coisas, basta selecionar as opções padrão. Depois vai perguntar sobre chaves, isso faz parte da criptografia TLS. Eu não sei se essas senhas que digitamos na chave servem pra alguma coisa. Na dúvida anote-as em algum lugar, mas acho que não vai precisar delas pra mais nada. Quando o script terminar de rodar tudo já vai estar configurado e funcionando! Abra um cliente de e-mail como o Evolution e teste enviar um e-mail autenticando no seu novo servidor usando login e senha de algum usuário do sistema.

Abra o script e veja o que foi feito. O tutorial que me ajudou a escrever o script foi esse aqui: http://www.howtoforge.com/perfect_setup_debian_sarge_p4

Se você conhece algum outro servidor smtp que faça isso de forma mais simples, por favor, não deixe de compartilhar.

3 comentários : 15.09.2009 05:59 PM