Conceptos Básicos de Action Mailbox

Esta guía le proporciona todo lo que necesita para comenzar a recibir correos electrónicos en su aplicación.

Después de leer esta guía, sabrá:


1 ¿Qué es Action Mailbox?

Action Mailbox enruta los correos electrónicos entrantes a buzones similares a controladores para su procesamiento en su aplicación Rails. Action Mailbox es para recibir correos electrónicos, mientras que Action Mailer es para enviarlos.

Los correos electrónicos entrantes se enrutan de manera asincrónica usando Active Job a uno o varios buzones dedicados. Estos correos electrónicos se convierten en registros de InboundEmail usando Active Record, que son capaces de interactuar directamente con el resto de su modelo de dominio.

Los registros de InboundEmail también proporcionan seguimiento del ciclo de vida, almacenamiento del correo electrónico original a través de Active Storage y manejo responsable de datos con incineración activada por defecto.

Action Mailbox incluye ingresses que permiten a su aplicación recibir correos electrónicos de proveedores externos como Mailgun, Mandrill, Postmark y SendGrid. También puede manejar correos electrónicos entrantes directamente a través de los ingresses integrados de Exim, Postfix y Qmail.

2 Configuración

Action Mailbox tiene varias partes móviles. Primero, ejecutará el instalador. Luego, elegirá y configurará un ingreso para manejar el correo electrónico entrante. Después estará listo para agregar el enrutamiento de Action Mailbox, crear buzones y comenzar a procesar correos electrónicos entrantes.

Para comenzar, instalemos Action Mailbox:

$ bin/rails action_mailbox:install

Esto creará un archivo application_mailbox.rb y copiará las migraciones.

$ bin/rails db:migrate

Esto ejecutará las migraciones de Action Mailbox y Active Storage.

La tabla de Action Mailbox action_mailbox_inbound_emails almacena mensajes entrantes y su estado de procesamiento.

En este punto, puede iniciar su servidor Rails y visitar http://localhost:3000/rails/conductor/action_mailbox/inbound_emails. Consulte Desarrollo Local y Pruebas para más información.

El siguiente paso es configurar un ingreso en su aplicación Rails para especificar cómo deben recibirse los correos electrónicos entrantes.

3 Configuración de Ingreso

Configurar el ingreso implica establecer credenciales e información de endpoint para el servicio de correo electrónico elegido. Aquí están los pasos para cada uno de los ingresos soportados.

3.1 Exim

Indique a Action Mailbox que acepte correos electrónicos de un relay SMTP:

# config/environments/production.rb
config.action_mailbox.ingress = :relay

Genere una contraseña fuerte que Action Mailbox pueda usar para autenticar solicitudes al ingreso relay.

Use bin/rails credentials:edit para agregar la contraseña a las credenciales cifradas de su aplicación bajo action_mailbox.ingress_password, donde Action Mailbox la encontrará automáticamente:

action_mailbox:
  ingress_password: ...

Alternativamente, proporcione la contraseña en la variable de entorno RAILS_INBOUND_EMAIL_PASSWORD.

Configure Exim para canalizar correos electrónicos entrantes a bin/rails action_mailbox:ingress:exim, proporcionando la URL del ingreso relay y la INGRESS_PASSWORD que generó anteriormente. Si su aplicación estuviera en https://example.com, el comando completo se vería así:

$ bin/rails action_mailbox:ingress:exim URL=https://example.com/rails/action_mailbox/relay/inbound_emails INGRESS_PASSWORD=...

3.2 Mailgun

Proporcione a Action Mailbox su clave de firma de Mailgun (que puede encontrar en Configuración -> Seguridad y Usuarios -> Seguridad de API en Mailgun), para que pueda autenticar solicitudes al ingreso de Mailgun.

Use bin/rails credentials:edit para agregar su clave de firma a las credenciales cifradas de su aplicación bajo action_mailbox.mailgun_signing_key, donde Action Mailbox la encontrará automáticamente:

action_mailbox:
  mailgun_signing_key: ...

Alternativamente, proporcione su clave de firma en la variable de entorno MAILGUN_INGRESS_SIGNING_KEY.

Indique a Action Mailbox que acepte correos electrónicos de Mailgun:

# config/environments/production.rb
config.action_mailbox.ingress = :mailgun

Configure Mailgun para reenviar correos electrónicos entrantes a /rails/action_mailbox/mailgun/inbound_emails/mime. Si su aplicación estuviera en https://example.com, especificaría la URL completamente calificada https://example.com/rails/action_mailbox/mailgun/inbound_emails/mime.

3.3 Mandrill

Proporcione a Action Mailbox su clave API de Mandrill, para que pueda autenticar solicitudes al ingreso de Mandrill.

Use bin/rails credentials:edit para agregar su clave API a las credenciales cifradas de su aplicación bajo action_mailbox.mandrill_api_key, donde Action Mailbox la encontrará automáticamente:

action_mailbox:
  mandrill_api_key: ...

Alternativamente, proporcione su clave API en la variable de entorno MANDRILL_INGRESS_API_KEY.

Indique a Action Mailbox que acepte correos electrónicos de Mandrill:

# config/environments/production.rb
config.action_mailbox.ingress = :mandrill

Configure Mandrill para enrutar correos electrónicos entrantes a /rails/action_mailbox/mandrill/inbound_emails. Si su aplicación estuviera en https://example.com, especificaría la URL completamente calificada https://example.com/rails/action_mailbox/mandrill/inbound_emails.

3.4 Postfix

Indique a Action Mailbox que acepte correos electrónicos de un relay SMTP:

# config/environments/production.rb
config.action_mailbox.ingress = :relay

Genere una contraseña fuerte que Action Mailbox pueda usar para autenticar solicitudes al ingreso relay.

Use bin/rails credentials:edit para agregar la contraseña a las credenciales cifradas de su aplicación bajo action_mailbox.ingress_password, donde Action Mailbox la encontrará automáticamente:

action_mailbox:
  ingress_password: ...

Alternativamente, proporcione la contraseña en la variable de entorno RAILS_INBOUND_EMAIL_PASSWORD.

Configure Postfix para canalizar correos electrónicos entrantes a bin/rails action_mailbox:ingress:postfix, proporcionando la URL del ingreso de Postfix y la INGRESS_PASSWORD que generó anteriormente. Si su aplicación estuviera en https://example.com, el comando completo se vería así:

$ bin/rails action_mailbox:ingress:postfix URL=https://example.com/rails/action_mailbox/relay/inbound_emails INGRESS_PASSWORD=...

3.5 Postmark

Indique a Action Mailbox que acepte correos electrónicos de Postmark:

# config/environments/production.rb
config.action_mailbox.ingress = :postmark

Genere una contraseña fuerte que Action Mailbox pueda usar para autenticar solicitudes al ingreso de Postmark.

Use bin/rails credentials:edit para agregar la contraseña a las credenciales cifradas de su aplicación bajo action_mailbox.ingress_password, donde Action Mailbox la encontrará automáticamente:

action_mailbox:
  ingress_password: ...

Alternativamente, proporcione la contraseña en la variable de entorno RAILS_INBOUND_EMAIL_PASSWORD.

Configure el webhook de entrada de Postmark para reenviar correos electrónicos entrantes a /rails/action_mailbox/postmark/inbound_emails con el nombre de usuario actionmailbox y la contraseña que generó anteriormente. Si su aplicación estuviera en https://example.com, configuraría Postmark con la siguiente URL completamente calificada:

https://actionmailbox:PASSWORD@example.com/rails/action_mailbox/postmark/inbound_emails

NOTA: Al configurar su webhook de entrada de Postmark, asegúrese de marcar la casilla etiquetada "Incluir contenido de correo electrónico sin procesar en la carga útil JSON". Action Mailbox necesita el contenido del correo electrónico sin procesar para funcionar.

3.6 Qmail

Indique a Action Mailbox que acepte correos electrónicos de un relay SMTP:

# config/environments/production.rb
config.action_mailbox.ingress = :relay

Genere una contraseña fuerte que Action Mailbox pueda usar para autenticar solicitudes al ingreso relay.

Use bin/rails credentials:edit para agregar la contraseña a las credenciales cifradas de su aplicación bajo action_mailbox.ingress_password, donde Action Mailbox la encontrará automáticamente:

action_mailbox:
  ingress_password: ...

Alternativamente, proporcione la contraseña en la variable de entorno RAILS_INBOUND_EMAIL_PASSWORD.

Configure Qmail para canalizar correos electrónicos entrantes a bin/rails action_mailbox:ingress:qmail, proporcionando la URL del ingreso relay y la INGRESS_PASSWORD que generó anteriormente. Si su aplicación estuviera en https://example.com, el comando completo se vería así:

$ bin/rails action_mailbox:ingress:qmail URL=https://example.com/rails/action_mailbox/relay/inbound_emails INGRESS_PASSWORD=...

3.7 SendGrid

Indique a Action Mailbox que acepte correos electrónicos de SendGrid:

# config/environments/production.rb
config.action_mailbox.ingress = :sendgrid

Genere una contraseña fuerte que Action Mailbox pueda usar para autenticar solicitudes al ingreso de SendGrid.

Use bin/rails credentials:edit para agregar la contraseña a las credenciales cifradas de su aplicación bajo action_mailbox.ingress_password, donde Action Mailbox la encontrará automáticamente:

action_mailbox:
  ingress_password: ...

Alternativamente, proporcione la contraseña en la variable de entorno RAILS_INBOUND_EMAIL_PASSWORD.

Configure SendGrid Inbound Parse para reenviar correos electrónicos entrantes a /rails/action_mailbox/sendgrid/inbound_emails con el nombre de usuario actionmailbox y la contraseña que generó anteriormente. Si su aplicación estuviera en https://example.com, configuraría SendGrid con la siguiente URL:

https://actionmailbox:PASSWORD@example.com/rails/action_mailbox/sendgrid/inbound_emails

NOTA: Al configurar su webhook de SendGrid Inbound Parse, asegúrese de marcar la casilla etiquetada “Publicar el mensaje MIME completo y sin procesar.” Action Mailbox necesita el mensaje MIME sin procesar para funcionar.

4 Procesamiento de Correos Electrónicos Entrantes

El procesamiento de correos electrónicos entrantes generalmente implica usar el contenido del correo electrónico para crear modelos, actualizar vistas, poner en cola trabajo en segundo plano, etc., en su aplicación Rails.

Antes de que pueda comenzar a procesar correos electrónicos entrantes, deberá configurar el enrutamiento de Action Mailbox y crear buzones.

4.1 Configurar Enrutamiento

Después de que un correo electrónico entrante se recibe a través del ingreso configurado, necesita ser enviado a un buzón para su procesamiento real por su aplicación. Al igual que el enrutador de Rails que despacha URLs a controladores, el enrutamiento en Action Mailbox define qué correos electrónicos van a qué buzones para su procesamiento. Las rutas se agregan al archivo application_mailbox.rb usando expresiones regulares:

# app/mailboxes/application_mailbox.rb
class ApplicationMailbox < ActionMailbox::Base
  routing(/^save@/i     => :forwards)
  routing(/@replies\./i => :replies)
end

La expresión regular coincide con los campos to, cc o bcc del correo electrónico entrante. Por ejemplo, lo anterior coincidirá con cualquier correo electrónico enviado a save@ a un buzón "forwards". Hay otras formas de enrutar un correo electrónico, consulte ActionMailbox::Base para más información.

Necesitamos crear ese buzón "forwards" a continuación.

4.2 Crear un Buzón

# Generar nuevo buzón
$ bin/rails generate mailbox forwards

Esto crea app/mailboxes/forwards_mailbox.rb, con una clase ForwardsMailbox y un método process.

4.3 Procesar Correo Electrónico

Al procesar un InboundEmail, puede obtener la versión analizada del correo electrónico como un objeto Mail con InboundEmail#mail. También puede obtener la fuente sin procesar directamente usando el método #source. Con el objeto Mail, puede acceder a los campos relevantes, como mail.to, mail.body.decoded, etc.

irb> mail
=> #<Mail::Message:33780, Multipart: false, Headers: <Date: Wed, 31 Jan 2024 22:18:40 -0600>, <From: someone@hey.com>, <To: save@example.com>, <Message-ID: <65bb1ba066830_50303a70397e@Bhumis-MacBook-Pro.local.mail>>, <In-Reply-To: >, <Subject: Hello Action Mailbox>, <Mime-Version: 1.0>, <Content-Type: text/plain; charset=UTF-8>, <Content-Transfer-Encoding: 7bit>, <x-original-to: >>
irb> mail.to
=> ["save@example.com"]
irb> mail.from
=> ["someone@hey.com"]
irb> mail.date
=> Wed, 31 Jan 2024 22:18:40 -0600
irb> mail.subject
=> "Hello Action Mailbox"
irb> mail.body.decoded
=> "This is the body of the email message."
# mail.decoded, una abreviatura de mail.body.decoded, también funciona
irb> mail.decoded
=> "This is the body of the email message."
irb> mail.body
=> <Mail::Body:0x00007fc74cbf46c0 @boundary=nil, @preamble=nil, @epilogue=nil, @charset="US-ASCII", @part_sort_order=["text/plain", "text/enriched", "text/html", "multipart/alternative"], @parts=[], @raw_source="This is the body of the email message.", @ascii_only=true, @encoding="7bit">

4.4 Estado del Correo Electrónico Entrante

Mientras el correo electrónico se enruta a un buzón coincidente y se procesa, Action Mailbox actualiza el estado del correo electrónico almacenado en la tabla action_mailbox_inbound_emails con uno de los siguientes valores:

  • pending: Recibido por uno de los controladores de ingreso y programado para enrutamiento.
  • processing: Durante el procesamiento activo, mientras un buzón específico ejecuta su método process.
  • delivered: Procesado con éxito por el buzón específico.
  • failed: Se produjo una excepción durante la ejecución del método process del buzón específico.
  • bounced: Rechazado para procesamiento por el buzón específico y devuelto al remitente.

Si el correo electrónico está marcado como delivered, failed o bounced, se considera "procesado" y se marca para incineración.

5 Ejemplo

Aquí hay un ejemplo de un Action Mailbox que procesa correos electrónicos para crear "forwards" para el proyecto del usuario.

El callback before_processing se utiliza para asegurar que se cumplan ciertas condiciones antes de que se llame al método process. En este caso, before_processing verifica que el usuario tenga al menos un proyecto. Otros callbacks soportados por Action Mailbox son after_processing y around_processing.

El correo electrónico puede ser devuelto usando bounced_with si el "forwarder" no tiene proyectos. El "forwarder" es un User con el mismo correo electrónico que mail.from.

Si el "forwarder" tiene al menos un proyecto, el método record_forward crea un modelo de Active Record en la aplicación usando los datos del correo electrónico mail.subject y mail.decoded. De lo contrario, envía un correo electrónico, usando Action Mailer, solicitando al "forwarder" que elija un proyecto.

# app/mailboxes/forwards_mailbox.rb
class ForwardsMailbox < ApplicationMailbox
  # Los callbacks especifican requisitos previos para el procesamiento
  before_processing :require_projects

  def process
    # Registrar el forward en el único proyecto, o…
    if forwarder.projects.one?
      record_forward
    else
      # …involucrar un segundo Action Mailer para preguntar en qué proyecto reenviar.
      request_forwarding_project
    end
  end

  private
    def require_projects
      if forwarder.projects.none?
        # Use Action Mailers para devolver correos electrónicos entrantes al remitente: esto detiene el procesamiento
        bounce_with Forwards::BounceMailer.no_projects(inbound_email, forwarder: forwarder)
      end
    end

    def record_forward
      forwarder.forwards.create subject: mail.subject, content: mail.decoded
    end

    def request_forwarding_project
      Forwards::RoutingMailer.choose_project(inbound_email, forwarder: forwarder).deliver_now
    end

    def forwarder
      @forwarder ||= User.find_by(email_address: mail.from)
    end
end

6 Desarrollo Local y Pruebas

Es útil poder probar correos electrónicos entrantes en desarrollo sin realmente enviar y recibir correos electrónicos reales. Para lograr esto, hay un controlador de conductor montado en /rails/conductor/action_mailbox/inbound_emails, que le da un índice de todos los InboundEmails en el sistema, su estado de procesamiento y un formulario para crear un nuevo InboundEmail también.

Aquí hay un ejemplo de prueba de un correo electrónico entrante con Action Mailbox TestHelpers.

class ForwardsMailboxTest < ActionMailbox::TestCase
  test "registrar directamente un forward de cliente para un forwarder y un forwardee correspondiente a un proyecto" do
    assert_difference -> { people(:david).buckets.first.recordings.count } do
      receive_inbound_email_from_mail \
        to: 'save@example.com',
        from: people(:david).email_address,
        subject: "Fwd: Status update?",
        body: <<~BODY
          --- Begin forwarded message ---
          From: Frank Holland <frank@microsoft.com>

          What's the status?
        BODY
    end

    recording = people(:david).buckets.first.recordings.last
    assert_equal people(:david), recording.creator
    assert_equal "Status update?", recording.forward.subject
    assert_match "What's the status?", recording.forward.content.to_s
  end
end

Por favor, consulte la API de ActionMailbox::TestHelper para obtener más métodos de ayuda para pruebas.

7 Incineración de InboundEmails

Por defecto, un InboundEmail que ha sido procesado será incinerado después de 30 días. El InboundEmail se considera procesado cuando su estado cambia a delivered, failed o bounced.

La incineración real se realiza a través del IncinerationJob que está programado para ejecutarse después del tiempo de config.action_mailbox.incinerate_after. Este valor está configurado en 30.days por defecto, pero puede cambiarlo en su configuración de production.rb. (Tenga en cuenta que esta programación de incineración a futuro depende de que su cola de trabajos pueda mantener trabajos durante tanto tiempo).

La incineración de datos por defecto asegura que no está reteniendo datos de personas innecesariamente después de que puedan haber cancelado sus cuentas o eliminado su contenido.

La intención con el procesamiento de Action Mailbox es que a medida que procesa un correo electrónico, debe extraer todos los datos que necesita del correo electrónico y persistirlos en modelos de dominio en su aplicación. El InboundEmail permanece en el sistema durante el tiempo configurado para permitir depuración y análisis forense y luego será eliminado.


Comentarios

Se te anima a ayudar a mejorar la calidad de esta guía.

Por favor contribuye si ves algún error tipográfico o errores fácticos. Para comenzar, puedes leer nuestra sección de contribuciones a la documentación.

También puedes encontrar contenido incompleto o cosas que no están actualizadas. Por favor agrega cualquier documentación faltante para main. Asegúrate de revisar Guías Edge primero para verificar si los problemas ya están resueltos o no en la rama principal. Revisa las Guías de Ruby on Rails para estilo y convenciones.

Si por alguna razón detectas algo que corregir pero no puedes hacerlo tú mismo, por favor abre un issue.

Y por último, pero no menos importante, cualquier tipo de discusión sobre la documentación de Ruby on Rails es muy bienvenida en el Foro oficial de Ruby on Rails.