Product
Maneja los emails entrantes como un profesional [API de Mailgun 2.0]
¡Llegar tan atrás en nuestro blog es como ver un viejo álbum de fotos familiar! Este artículo se publicó originalmente en 2011.
Enviar emails solía ser difícil, pero con la API de envío de emails dejamos atrás el SMTP y el MIME para convertirlo en una experiencia sencilla o, me atrevería a decir, agradable.

Sin embargo, si tu aplicación necesita participar en conversaciones por email, como suele ser el caso de las aplicaciones empresariales, el envío es apenas la mitad del trabajo. Imagina que quieres crear un bot basado en emails que se integre en el flujo de trabajo de tu aplicación empresarial. Piensa en los obstáculos que tendrías que superar:
Problemas de transporte. De alguna forma, los mensajes entrantes deben llegar a tu aplicación. Esto significa que debe haber un registro MX en algún lugar que apunte a un servidor de email, capaz de reenviar el email entrante a tu código. Esto esconde muchas complicaciones. Una de ellas es que probablemente quieras generar rebotes adecuados de “buzón no válido” para notificar a los remitentes si se han equivocado en la dirección.
Problemas con la gestión del spam. Una vez que empiezas a aceptar emails para un dominio MX determinado, debes tener en cuenta que, con el tiempo, la mayoría del email entrante será spam. E incluso si tu política de aceptación de emails se basa en una lista de permitidos, no siempre es aconsejable que tu aplicación deba lidiar con ataques de spam.
Problemas de análisis de MIME. Ya hemos hablado de esto antes, pero analizar MIME es un suplicio en la mayoría de los lenguajes de programación. Las bibliotecas de análisis de MIME no son muchas, y gran parte de ellas toleran bastante mal el tráfico del mundo real.
Contenido del mensaje. Tus problemas de análisis van mucho más allá del MIME. La mayoría de los emails entrantes suelen ser respuestas a mensajes enviados anteriormente, y normalmente querrás eliminar esas partes citadas gigantes. A menudo, también es recomendable extraer la firma de una persona del cuerpo del mensaje.
Parece bastante abrumador para algo tan simple como introducir un fragmento de texto en una aplicación. ¿No sería genial tener algo similar a Sinatra o Flask y poder decir simplemente en 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)
A la comunidad de Ruby le encantan los bloques. A la comunidad de Ruby le encantaría poder decir:
email_in ".*@myapp.com" do |message_obj|rn # sweet profit-making code...rn end
La idea de estos ejemplos de código es similar al mecanismo de enrutamiento incluido en los frameworks MVC modernos, pero en lugar de emparejar las URL con acciones del controlador, queremos definir rutas que vinculen el patrón de dirección del destinatario a una función de tu código.
Pero ¿cómo se implementa email_in() teniendo en cuenta las dificultades mencionadas?
¡Las rutas de Mailgun al rescate!
Las diseñamos pensando específicamente en este caso de uso y son, con diferencia, la forma más agradable de crear una aplicación de mensajería por email bidireccional. Confía en nuestra experiencia, somos especialistas del email. Pero basta de cháchara, pongámonos manos a la obra.
Primero, vamos a definir una nueva ruta en el panel de control de Mailgun. Esta ruta vinculará la dirección del destinatario con la expresión regular “.*@myapp.com” y, si coincide, hará dos cosas:
- Analizará el mensaje y realizará una solicitud POST en la URL “/emailin” de tu aplicación.
- También reenviará una copia del mensaje al buzón del equipo de desarrollo, por ejemplo, a developer@myapp.com.

Ahora podemos manejar los mensajes que van a @myapp.com añadiendo el siguiente código en la aplicación 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)
Maneja los emails entrantes como un profesional [API de Mailgun 2.0]
Vamos a enviar un mensaje de prueba con Gmail a nuestra aplicación web para ver qué se publica.
Esta es la captura de pantalla del mensaje tal y como aparece en Gmail. Fíjate en que tiene un cuerpo real de lo que se ha escrito (stripped-text), la firma y la parte citada que en la mayoría de los casos es irrelevante:

Los datos publicados mediante POST se muestran a continuación como un volcado de la solicitud MultiDict de Python. Ten en cuenta que, además de los parámetros sintéticos como “recipient” o “stripped-text”, Mailgun también publica mediante POST todos los encabezados MIME en la aplicación, de modo que tienes acceso completo a todo:
Parámetros POST (volcados en el registro como MultiDict de 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.
Archivos enviados por POST:
('attachment-1', FileStorage: 'application.pdg')
Repasemos. ¿Qué tenemos aquí?
- El tráfico de email entrante se compara con una expresión regular aplicada a un destinatario del mensaje.
- Los mensajes coincidentes se analizan, se comprueba si son spam y se envían mediante HTTP POST a la URL de tu aplicación.
- Las partes citadas del mensaje se separan (se eliminan) y se detecta la firma.
- Si tu aplicación está inactiva y no responde, los mensajes se pondrán en cola y se realizarán intentos de entrega posteriores durante un máximo de tres días.
- Además, los mensajes coincidentes pueden almacenarse opcionalmente en un buzón con fines de archivo, copia de seguridad o depuración.
- Ninguna de estas complejidades te importará ya. Tú solo tienes que centrarte en escribir código rentable.
Y, por cierto, puedes manipular las rutas de email mediante programación a través de una API. Esto te permite crear un decorador mágico @email_in() estilo Flask o Sinatra que lo unirá todo.
Las rutas de Mailgun pueden hacer mucho más que un simple emparejamiento de direcciones del destinatario. Admiten capturas de expresiones regulares, comportamientos del tipo “si no hay coincidencia con nada más”, tienen prioridad de ejecución… Básicamente, son un sencillo lenguaje de programación de enrutamiento de email.
Ahí lo tienes. Una auténtica pasada. Ahora pongámonos manos a la obra y libremos al mundo de los absurdos emails «no-reply@». Ya era hora.
¡Un saludo!
—
El equipo de Mailgun
Edición: no dudes en participar en el debate de HN