ActionMailbox — Set up

·

·

Overview:

Action Mailbox routes incoming emails to controller-like mailboxes for processing in Rails. In this document, we discuss setting up ActionMailbox using Mailgun. This setup will work not only with Mailgun but also with any other emailing services.

The setup is split into two parts,

  1. Changes in Rails to process the emails.
  2. Of course we need to configure the emailing service to allow rails server to read the email.

Rails code change:

We need to add this line in the application.rb for the action mailbox to work.

# app/config/application.rb
require "action_mailbox/engine"

Then run the below command to create necessary tables and configuration. (Setup section)

bin/rails action_mailbox:install
bin/rails db:migrate

Like routes.rb, to route the web request to specific controller, we need a email routes to do the same. For email routes we specify them in ApplicationMailbox. Taking the same example from here.

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

In the above example, any regex that matches /^save@/i will redirect them to forwards mailbox.

So we need to create a forwards mailbox. It can be created by below command. These are our controllers for emails.

$ bin/rails generate mailbox forwards

The mailbox code looks like this. The mailbox should have method process to process the email.

# app/mailboxes/forwards_mailbox.rb
class ForwardsMailbox < ApplicationMailbox
  # Callbacks specify prerequisites to processing
  before_processing :pre_process
  def process
    # Code to process the email
  end
  private
def pre_process
    # Before processing callback
  end
end

In my case, I need to process the attachments according to the recipient email address. I could have write two different mailbox and with routes redirect them to specific mailbox by regex, but here I am giving example to read mail details.

In the below example, I have a separate constant to store the regex. This will be useful when I have to make use of it in multiple places. And also if you see I have environment specific email address to process them in different envs.

# app/values/constants/emails.rb
module Constants
  module Emails
    PROCESS_ATTACHMENT_EMAIL_ADDRESS = /process\+(.+)@#{ENV["RECEIVING_EMAIL_DOMAIN"]}/i.freeze
  end
end
# app/mailboxes/application_mailbox.rb
class ApplicationMailbox < ActionMailbox::Base
  # routing /something/i => :somewhere
  routing Constants::Emails::PROCESS_ATTACHMENT_EMAIL_ADDRESS => :process_attachments
end
# app/mailboxes/process_attachments_mailbox.rb
class ProcessAttachmentsMailbox < ApplicationMailbox
  def process
    case processor
when "one"
      process_one
when "two"
      process_two
end
  private
# This will return the processor.
  # Return value with any of the below 'to' addresses
  # process+one@staging_domain.com  => one
  # process+two@staging_domain.com  => two
  def processor
    matcher = Constants::Emails::PROCESS_ATTACHMENT_EMAIL_ADDRESS
recipient = mail.recipients.find { |r| matcher.match?(r) }
recipient[matcher, 1]
  end
def process_one
    # Process one
    # Attachments 
    mail.attachments.each do |attachment|
      next unless attachment.content_type.start_with?("text/csv")
# Save the file to /tmp. You can process the file instead.
      filename = "#{time_stamp}_#{attachment.filename}"
      filename = File.join("tmp", filename)
File.open(filename, "w+b", 0o644) do |f|
        f.write attachment.decoded.gsub(/\r+\n+/, "\r\n")
      end
    end
  end
def process_two
    # Process two
  end
end

I hope I haven’t confused with the above example. I just thought to share the accessible mail variables.

mail.to
mail.from
mail.subject
mail.attachments
mail.content

How can I test in the dev environment?

To test incoming emails in development without actually sending or receiving real emails, there’s a conductor controller mounted at http://localhost:3000/rails/conductor/action_mailbox/inbound_emails, which gives you an index of all the InboundEmails in the system, their state of processing, and a form to create a new InboundEmail as well.

If your application is api_only app, edit application.rb to set config.api_only = false in order to render views.

Setting up Mailgun

Ref: Receiving emails — Mailgun documentation and Domain Verification — Mailgun documentation

Step 1:

As the first step, we need a new domain for this email setup. Say, if the company email address is @company.com, we need to create a subdomain in Mailgun like staging.company.com / production.company.com. We don’t want to disturb the existing domain. You can follow the Step 1 in this Mailgun document or or the document provided by your emailing service. Once added, the domain setting page will show up the DNS records that needs to be added on your domain provider’s website. We need both MX records and TXT records to be configured in the domain provider’s page.

Step 2:

Once the above step is done, we need to set up MX records for the domain that we used for email. Use subdomain as the domain is already used for your primary emailing system.

i.e. For staging, set up MX records for domain staging.company.com and for production, set up MX records for domain production.company.com

There are two records provided by mailgun. (mxa.mailgun.org and mxb.mailgun.org). You need to set up both.

Step 3:

To verify the domain setup made in mailgun, we need to add TXT records as well. You can get those records from mailgun’s domain setting page.

If all the steps are done, after a few minutes / hours, the admin will receive the email about the setup status.

Routes in Mailgun:

Now that we need to route the emails received by mailgun. In the above staging example, let say someone sends email to process+one@staging.company.com Mailgun have to redirect them to the rails server, isn’t it? So we need to configure them in Mailgun.

You can create routes in the Receiving section of mailgun. (Ref: https://documentation.mailgun.com/en/latest/api-routes.html#routes)

In my example, the routes will looks like this

match_recipient("process+.*?@staging.company.com") Priority: 0
forward("https://staging.company.com/rails/action_mailbox/mailgun/inbound_emails/mime")
stop()

Http path /rails/action_mailbox/mailgun/inbound_emails/mime is where rails server is listening for incoming emails.

Testing Routes:

If you need to test those routes, go to this url — http://bin.mailgun.net/. It will generate a unique url for testing. Configure the same in your routes, so that when you send an email to configure address, you can test them in the unique url provided by http://bin.mailgun.net/. Sample screenshot below.

Once routes are configured, send email to test@staging.company.com to test receiving emails. As you can see we are catching all emails for the domain @staging.company.com

Final Step:

Once everything is tested individually, configure Mailgun to forward inbound emails to /rails/action_mailbox/mailgun/inbound_emails/mime. If your application lives at https://company.com, you would specify the fully-qualified URL https://company.com/rails/action_mailbox/mailgun/inbound_emails/mime.

Example below.

match_recipient("process+.*?@production.company.com") Priority: 0forward("https://www.company.com/rails/action_mailbox/mailgun/inbound_emails/mime")
stop()

That’s it, folks! Good luck experimenting with ActionMailbox and Mailgun!

Author



Leave a Reply

Your email address will not be published. Required fields are marked *