Receiving and Processing Email: Heroku, Sendgrid, and possibly Mailman

940 Views Asked by At

My app creates a unique email for each user, and users send email to that address for processing. Using Sendgrid, I've piped incoming emails to my domain (hosted on Heroku) to an address:

site.com/receive_email

I use the TO field to determine the user, since the email address is randomly generated.

I've experimented using an external script like Mailman, but since I'm hosted on Heroku I'd need to have a worker running full time to keep this process going. Not really looking for that at the moment for this test app.

That leaves processing it as a POST request. I have access to POST hash (params["subject"], etc.) at receive_emails.

This is where I get stuck

Would you suggest to deal with raw data from the POST params, or can I use something like Mailman or ActionMailer to process the email for me?

2

There are 2 best solutions below

0
On

I haven't used Sendgrid to turn emails into post requests, but it works fine with cloudmailin, a heroku addon. Here is an example where someone sends an email to your application, it is processed by cloudmailin/sendgrid and turned into a post, and then sends it to your controller, and then the controller looks through the message params, finds the sender from the email address, and if the sender doesn't already exist, creates an account for her:

class CreateUserFromIncomingEmailController < ApplicationController

  require 'mail'

  skip_before_filter :verify_authenticity_token

  parse_message(params[:message])

  def create
    User.find_or_create_by_email(@sender)
  end

private

  def parse_message(message_params)
    @message    = Mail.new(message_params)
    @recipients = @message.to
    @sender     = @message.from.first
  end

end

Good luck.

2
On

ActionMailer already depends on the Mail gem, you could use it to parse the incoming email and extract the parts that you want. It is specially useful to deal with multipart emails.

require 'mail'

class IncomingEmails < ApplicationController
  skip_before_filter :verify_authenticity_token

  def receive_email
    comment = Comment.new(find_user, message_body)
    comment.save

  rescue
    # Reject the message
    logger.error { "Incoming email with invalid data." }
  end

  private

  def email_message
    @email_message ||= Mail.new(params[:message])
    # Alternatively, if you don't have all the info wrapped in a
    # params[:message] parameter:
    #
    # Mail.new do
    #   to      params[:to]
    #   from    params[:from]
    #   subject params[:subject]
    #   body    params[:body]
    # end
  end

  def find_user
    # Find the user by the randomly generated secret email address, using
    # the email found in the TO header of the email.
    User.find_by_secret_email(email_message.to.first) or raise "Unknown User"
  end

  def message_body
    # The message body could contain more than one part, for example when
    # the user sends an html and a text version of the message. In that case
    # the text version will come in the `#text_part` of the mail object.
    text_part = email_message.multipart? ? email_message.text_part : email_message.body
    text_part.decoded
  end
end