Product

Gerencie e-mails de entrada como profissional [API do Mailgun 2.0]

Ir tão longe no nosso blog é como olhar um álbum de fotos antigo de família! Este post foi publicado originalmente lá em 2011.
Imagem para Gerencie e-mails de entrada como profissional [API do Mailgun 2.0]

Ir tão longe no nosso blog é como olhar um álbum de fotos antigo de família! Este post foi publicado originalmente lá em 2011.

Enviar e-mails costumava ser difícil, mas com a API de envio de e-mail do Mailgun fomos além do SMTP e do MIME e tornamos isso uma experiência trivial e, ouso dizer, agradável.

Baby with helmet with the word "Incoming!"

Mas se a sua aplicação precisa participar de conversas por e-mail, como costuma acontecer com apps corporativos, o envio é menos da metade da batalha. Digamos que você queira criar um bot baseado em e-mail que se conecte ao fluxo de trabalho dentro do seu app corporativo. Considere os obstáculos que você precisaria superar:

Problemas de transporte. As mensagens de entrada precisam chegar ao seu app de alguma forma. Isso significa que precisa haver um registro MX em algum lugar apontando para um servidor de e-mail, capaz de encaminhar o e-mail recebido para o seu código. Existem muitas pegadinhas aqui. Uma delas é que você provavelmente vai querer gerar uma devolução de “caixa de correio inválida” apropriada para notificar os remetentes se houver um erro de digitação no endereço.

Problemas de tratamento de spam. Assim que você começar a aceitar e-mails para um determinado domínio MX, deve perceber que, com o tempo, a maior parte dos e-mails recebidos será spam. E mesmo que sua política de aceitação de e-mail seja baseada em uma lista de permissões, fazer com que o seu app lide com ataques de spam nem sempre é o ideal.

Problemas de análise de MIME. Já falamos sobre isso antes, mas analisar o MIME é um processo doloroso na maioria das linguagens de programação. Não existem muitas bibliotecas de análise de MIME e várias sofrem com uma baixa tolerância ao tráfego do mundo real.

Conteúdo da mensagem. Seus problemas de análise vão muito além do MIME. A maioria dos e-mails recebidos geralmente são respostas a mensagens enviadas antes e, de modo geral, você quer remover as partes gigantes citadas. Muitas vezes, também é desejável extrair a assinatura de uma pessoa do corpo de uma mensagem.

Parece bastante assustador para algo tão simples quanto inserir um trecho de texto em uma aplicação. Não seria legal ter algo parecido com o Sinatra ou Flask e poder simplesmente dizer em Python:

                                

                                     @email_in(".*@myapp.com")rn    def incoming_message(message_obj):rn      # access various parts of the fully parsed incoming message:rn      message_obj.bodyrn      message_obj.body_without_quoted_textrn      message_obj.sender_signaturern    # stuff that matters:rn      make_profit_from(message_obj)
                                
                            

O pessoal do Ruby adora blocos. O pessoal do Ruby adoraria poder dizer:

                                

                                     email_in ".*@myapp.com" do |message_obj|rn      # sweet profit-making code...rn    end
                                
                            

A ideia por trás desses exemplos de código é parecida com o mecanismo de roteamento apresentado nas estruturas MVC modernas, mas, em vez de associar URLs às ações do controlador, queremos definir rotas que correspondam a um padrão de endereço de destinatário para uma função em seu código.

Mas como você pode implementar email_in() levando em conta as dificuldades listadas acima?

As rotas do Mailgun chegaram para salvar o dia!

Nós as projetamos especificamente com esse caso de uso em mente, e elas são, de longe, a forma mais agradável de criar um app de mensagens de e-mail bidirecional. Confie em nós, somos especialistas em e-mails!  Mas chega de papo, vamos colocar a mão na massa.

Primeiro, vamos definir uma nova rota no painel de controle do Mailgun. Essa rota vai associar o endereço do destinatário à expressão regular “.*@myapp.com” e, se houver correspondência, ela fará duas coisas:

  • Ela vai analisar a mensagem e enviar um POST para a URL “/emailin” do seu app.
  • Ela também vai encaminhar a cópia da mensagem para a caixa de correio de um desenvolvedor, por exemplo, developer@myapp.com
Mailgun's control panel

Agora, podemos gerenciar as mensagens que entram em @myapp.com adicionando o seguinte código ao aplicativo web:

                                

                                    @app.route("/mailin", methods=["POST"])  rndef mailin():  rn    # see if the message is spam:rn    is_spam = request.form['X-Mailgun-SFlag'] == 'Yes'rnrn    # access some of the email parsed values:rn    request.form['From']rn    request.form['To']rn    request.form['subject']rnrn    # stripped text does not include the original (quoted) message, only whatrn    # a user has typed:rn    request.form['stripped-text']rn    request.form['stripped-signature']rnrn    # enumerate through all attachments in the message and savern    # them to disk with their original filenames:rn    for attachment in request.files.values():rn        attachment.filenamern        data = attachment.stream.read()rn        with open(attachment.filename, "w") as f:rn            f.write(data)
                                
                            

Gerencie e-mails de entrada como profissional [API do Mailgun 2.0]

Vamos enviar uma mensagem de teste usando o Gmail para nosso aplicativo web e ver o que é postado.
Aqui está a captura de tela da mensagem como ela aparece no Gmail. Observe que ela tem o corpo real do que foi escrito (stripped-text), a assinatura e a parte citada que, na maioria dos casos, é irrelevante:

Test email on web app from Ev Kontsevoy

Os dados do POST são mostrados abaixo como uma solicitação MultiDict do Python descarregada. Observe que, além de parâmetros sintéticos como “recipient” ou “stripped-text”, o Mailgun também envia via POST todos os cabeçalhos MIME no app, para que você tenha acesso total a tudo:

Parâmetros do POST (descarregados no log como MultiDict do Python)

                                

                                    ('From', u'Ev Kontsevoy '),rn('sender', u'ev@mailgunhq.com'),rn('To', u'Awesome Bot '),rn('attachment-count', u'1'),rn('Subject', u'Re: Your application')])rn('stripped-text', u'My application is attached.nThanks.'),rn('stripped-html', u'HTML version of stripped-text'),rn('body-html', u'[full html version of the message]'),rn('body-plain', u'[full text version of the message]'),rn('stripped-signature', u'-- nEv Kontsevoy,nCo-founder and CEO of Mailgun.net  - the emailnplatform for developers.'),rn('recipient', u'bot@hello.mailgun.org'),rn('subject', u'Re: Your application'),rn('timestamp', u'1320695889'),rn('signature', u'b8869291bd72f1ad38238429c370cb13a109eab01681a31b1f4a2751df1e3379'),rn('token', u'9ysf1gfmskxxsp1zqwpwrqf2qd4ctdmi5e$k-ajx$x0h846u88'),rn('In-Reply-To', u'Message-Id-of-original-message'),rn('Date', u'Mon, 7 Nov 2011 11:58:06 -0800'),rn('Message-Id', u'message-id-goes-here'),rn('X-Originating-Ip', u'[216.156.80.78]'),rnrn# NOTE: ALL message fields are parsed and pasted. If some fields (like "Cc") arern# missing here it only means they were absent from the message.
                                
                            

Arquivos enviados via POST:

('attachment-1', FileStorage: 'application.pdg')

Vamos revisar. O que temos aqui?

  • O tráfego de e-mail de entrada é correspondido com uma expressão regular aplicada a um destinatário da mensagem.
  • As mensagens correspondentes são analisadas, verificadas quanto a spam e enviadas via HTTP POST para a URL da sua aplicação.
  • As partes citadas da mensagem são separadas (stripped) e a assinatura é detectada.
  • Se sua aplicação estiver fora do ar e não responder, as mensagens vão entrar na fila e as tentativas de entrega subsequentes serão feitas por até 3 dias.
  • Além disso, as mensagens correspondentes podem ser opcionalmente armazenadas em uma caixa de correio para fins de arquivamento, backup ou depuração.
  • Nenhuma dessas complexidades importa mais para você. Você está ocupado programando aquele código incrível que gera lucros.

E, a propósito, manipular as rotas de e-mail pode ser feito de forma programática via uma API. Isso permite que você construa um decorador @email_in() mágico, estilo Flask/Sinatra, que uniria tudo.

As rotas do Mailgun podem fazer mais do que apenas uma simples correspondência de endereço de destinatário. Elas dão suporte a capturas de expressões regulares, possuem o comportamento match-if-nothing-else, têm prioridade de execução, e são basicamente uma linguagem de programação simples para roteamento de e-mail.

É isso. Simplesmente incrível. Agora, vamos ao trabalho e vamos livrar o mundo dos e-mails bobos “no-reply@”. Já estava na hora.

Paz!

Equipe do Mailgun

Editado: sinta-se à vontade para participar da discussão no HN