Receiving and Processing Email: Heroku, Sendgrid, and possibly Mailman - ruby-on-rails

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?

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.

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

Related

Rails Mailer: send emails to recipients based on matching criteria

I have managed to configure my ActionMailer to send emails to recipients based on a new 'submission'. However, the way my app works is that it takes the submissions 'Desired Location' field and matches it up to the 'Company Business location' field in another model called Agents to give an index view that is matched by location depending on user. i.e if i submit a submission with a location of london, then only agents with a location of london will be able to see it. Which brings me to my emails, is there anyway to create a mailer that works in the same way? So only send emails to agents who match the desired location of the submission?
Mailer
class NewSubmissionMailer < ApplicationMailer
def submission_email(submission)
#submission = submission
mail(to: #submission.Email, subject: 'Welcome to Ottom8')
end
end
Submissions Controller
respond_to do |format|
if #submission.save
# Tell the UserMailer to send a welcome email after save
NewSubmissionMailer.submission_email(#submission).deliver_now
Code to match both models
def index
#submissions = Submission.where(:Desired_Location => current_agent.Company_Business_Location)
end
Thanks
respond_to do |format|
if #submission.save
# Tell the UserMailer to send a welcome email after save
NewSubmissionMailer.submission_email(#submission).deliver_now
# Send emails to matching agents
NewSubmissionMailer.matching_agents_email(#submission).deliver_now
and then in the mailer ::matching_agents_email:
def matching_agents_email(submission)
#submission = submission
agents = Agent.where(:Company_Business_Location => #submission.Desired_Location)
mail(to: agents.pluck(:email) # ... Rest of email logic. )

How to prevent rails mailer to open new gmail login session each time it sends an email

I have a task scheduled to send thousands of reporting emails to different users of my app. I configured Gmail to send the emails. However, after a few hundred of emails sent, I get the following error:
Net::SMTPAuthenticationError: 454 4.7.0 Too many login attempts,
please try again later.
As mentioned in this question, I set up a DNS record but it still doesn't work.
How can I change the configuration of the rails mailer in order to only open once the Gmail session in order to be able to send thousands emails in a row?
My task:
task :send_weekly_stats => :environment do
User.each do |u|
UserMailer.weekly_report_email(u.id).deliver_now
end
end
My mailer action:
def weekly_report_email(user_id)
#user = User.find(user_id)
#email = #user.email
#subject = 'Weekly Report'
mail(to: #email, subject: #subject)
end

Sendgrid API for Ruby on Rails

I can't seem to find a step by step tutorial on how to integrate the Sendgrid web API in to a Ruby on Rails application. I'm pretty new to this so maybe I'm missing something obvious.
I would like to use the Sendgrid web API instead of the smtp delivery method (mailgun talks about the benefits of the web API over the SMTP method here: https://documentation.mailgun.com/quickstart-sending.html, and I was thinking that Sendgrid would either have the same benefits or I would potentially switch to mailgun later).
After installing the sendgrid gem (https://github.com/sendgrid/sendgrid-ruby), the documentation tells me to "Create a new client with your SendGrid API Key", and that I can do it 2 ways:
require 'sendgrid-ruby'
# As a hash
client = SendGrid::Client.new(api_key: 'YOUR_SENDGRID_APIKEY')
# Or as a block
client = SendGrid::Client.new do |c|
c.api_key = 'YOUR_SENDGRID_APIKEY'
end
Where specifically in my application am I supposed to put this code? Should I put this in my mailer, my application mailer or in the config/environments/production.rb file?
I took a look at this tutorial that walks through how to set up the Mailgun API: https://launchschool.com/blog/handling-emails-in-rails
According to this tutorial it looks like the line client = SendGrid::Client.new(api_key: 'YOUR_SENDGRID_APIKEY') should actually go in to the mailer method itself. See below for the launchschool.com example (presumably replacing the mailgun specific info with the sendgrid info):
class ExampleMailer < ActionMailer::Base
def sample_email(user)
#user = user
mg_client = Mailgun::Client.new ENV['api_key']
message_params = {:from => ENV['gmail_username'],
:to => #user.email,
:subject => 'Sample Mail using Mailgun API',
:text => 'This mail is sent using Mailgun API via mailgun-ruby'}
mg_client.send_message ENV['domain'], message_params
end
end
Additionally, how do I get my mailer method to send a mailer view instead of simple text as outlined in the launchschool example? For example, instead of sending the text 'This mail is sent using...' I would like to send a mailer view (something like account_activation.html.erb).
Finally, I am using Devise in my application, and I would like to have Devise use the web API to send emails (ie password reset, etc). Does this mean I need to create a custom mailer for Devise? If so, how do I do that?
According to Devise (https://github.com/plataformatec/devise/wiki/How-To:-Use-custom-mailer), I should "create a class that extends Devise::Mailer". Does that mean I simply make a file within my mailer folder with the info laid out in the docs? Do I need a separate mailer for Devise or can I have an existing mailer inherit from the Devise mailer? Finally, how do I tell devise to use the sendgrid web api to send emails (instead of the simple smtp method)?
Sorry for the long question, but hopefully others find it useful.
Thanks!
I would create a mailer class to do this
class SendgridWebMailer < ActionMailer::Base
include Sendgrid
def initialize
#client = SendGrid::API.new(api_key: ENV['SENDGRID_API_KEY']).client
end
def send_some_email(record, token)
mail = Mail.new
// do your mail setup here
mail = Mail.new
mail.from = Email.new(email: YOUR_EMAIL_HERE)
mail.subject = YOUR_SUBJECT
// I personally use sendgrid templates, but if you would like to use html -
content = Content.new(
type: 'text/html',
value: ApplicationController.render(
template: PATH_TO_TEMPLATE,
layout: nil,
assigns: IF_NEEDED || {}
)
mail.contents = content
personalization = Personalization.new
personalization.to = Email.new(email: EMAIL, name: NAME)
personalization.subject = SUBJECT
mail.personalizations = personalization
#client.mail._('send').post(request_body: mail.to_json)
end
end
Call it using
SendgridWebMailer.send_some_email(record, token).deliver_later
For Devise
class MyDeviseMailer < Devise::Mailer
helper :application # gives access to all helpers defined within `application_helper`.
include Devise::Controllers::UrlHelpers # Optional. eg. `confirmation_url`
def reset_password_instructions(record, token, opts={})
SendgridWebMailer.send_some_email(record, token).deliver_later
end
end
in config/devise.rb
# Configure the class responsible to send e-mails.
config.mailer = 'MyDeviseMailer'
EDIT:
This method will, unfortunately, only work with remote images.
Okay, so here's how I did it. There may be a better way to do this, but this worked for me.
So with sending an email you would originally send it by doing something like UserMailer.reset_email(user).deliver. So remove the deliver and save it to a variable:
object = UserMailer.reset_email(user)
Deeply nested in this ish is the body of the email. The place where it lies may change with context, so my advice is to return the object to the frontend so that you can dig into it and find it. For me, the body resided here:
object = UserMailer.reset_email(user)
body = object.body.parts.last.body.raw_source
Okay so now you got the raw source. Now to send it, here's a method I created:
def self.sendEmail(from,to,subject,message)
from = Email.new(email: from)
to = Email.new(email: to)
content = Content.new(type: 'text/html', value: message)
mail = Mail.new(from, subject, to, content)
sg = SendGrid::API.new(api_key: ENV['SENDGRID_API_KEY'])
response = sg.client.mail._('send').post(request_body: mail.to_json)
puts response.status_code
puts response.body
puts response.headers
end
Call it as such (in my case it's in the user model):
User.sendEmail('noreply#waydope.com',user.email,'Reset Password', body)
Make sure to change the content type from plain to html, or you will just get the raw code. Hope this helps.
Here's a step-by-step guide to help you integrate SendGrid into your RoR's app. Hope it helps!
Create a SendGrid account first at : http://sendgrid.com/
Create a new rails folder (sendgrid_confirmation)
rails new sendgrid_confirmation
Navigate to ‘sendgrid_confirmation’ folder
cd sendgrid_confirmation
Open ‘sendgrid_confirmation’ in your text editor (Sublime)
Create a user model (User is a test model, you can create any other model as per your project’s use)
rails generate scaffold user name email login
rake db:migrate
Include sendgrid-rails gem in your Gemfile
gem 'sendgrid-rails', '~> 2.0'
Run bundle install in your terminal
bundle install
Use secrets.yml to define the SendGrid API credentials: (config/secrets.yml)
production:
sendgrid_username: your-sendgrid-username
sendgrid_password: your-sendgrid-password
(Emails are not sent in development and test environments. So you can define it only for production.)
Generate a Mailer class. Mailer classes function as our controllers for email views.
rails generate mailer UserNotifier
Open app/mailers/user_notifier.rb and add the following mailer action that sends users a sign-up mail
class UserNotifier < ActionMailer::Base
default :from => 'any_from_address#example.com'
# send a signup email to the user, pass in the user object that contains the user's email address
def send_signup_email(user)
#user = user
mail(:to => #user.email,
:subject => 'Thanks for signing up for our amazing app')
end
end
Create a file app/views/User_notifier/send_signup_email.html.erb as follows: (This will create a view that corresponds to our action and outputs HTML for our email)
<!DOCTYPE html>
<html>
<head>
<meta content='text/html; charset=UTF-8' http-equiv='Content-Type' />
</head>
<body>
<h1>Thanks for signing up, <%= #user.name %>!</h1>
<p>Thanks for joining and have a great day! Now sign in and do awesome things!</p>
</body>
</html>
Go to the Users Controller (app/controllers/users_controller.rb) and add a call to UserNotifier.send_signup_email when a user is saved.
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
UserNotifier.send_signup_email(#user).deliver
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
else
format.html { render :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
Update your config/environment.rb to point your ActionMailer settings to SendGrid’s servers. Your environment.rb file should look like the following:
# Load the Rails application.
require File.expand_path('../application', __FILE__)
# Initialize the Rails application.
Rails.application.initialize!
ActionMailer::Base.smtp_settings = {
:user_name => ‘your_sendgrid_username’,
:password => 'your_sendgrid_password’,
:domain => ‘your_domain.com',
:address => 'smtp.sendgrid.net',
:port => 587,
:authentication => :plain,
:enable_starttls_auto => true
}
Point your config/routes.rb file to load the index page on load. Add the following in your routes.rb file:
get ‘/’ => ‘users#index’
And that is it! Now when you create a new user, you should receive an email (on the user.email you provided) from any_from_address#example.com. You can change this from:e-mail from app/mailers/user_notifier.rb. Change the default :from address and it should do that. You can add email parameters in the send_signup_mail method inside the same file and add details that you’d like to add. You can also change the message body from app/views/user_notifier/send_signup_email.html.erb to display whatever content you want to.

How to verify if an account activation email was sent or not in Ruby on Rails web application

I was building a web app with account activation feature. I am following Michael Hartl ROR tutorial and I am not getting any account activation email to my inbox. I tried to search online on how to verify if at all an email was sent. Like there should be an SMTP server we configure to send emails right? Did not find anything helpful. Can someone help pls?
My account_activations_controller.rb
class AccountActivationsController < ApplicationController
def edit
user = User.find_by(email: params[:email])
if user && !user.activated? && user.authenticated?(:activation, params[:id])
user.activate
log_in user
flash[:success] = "Account activated!"
redirect_to user
else
flash[:danger] = "Invalid activation link"
redirect_to root_url
end
end
end
User_mailer.rb
class UserMailer < ApplicationMailer
def account_activation(user)
#user = user
mail to: user.email, subject: "Account activation"
end
end
application_mailer.rb
class ApplicationMailer < ActionMailer::Base
default from: "noreply#example.com"
layout 'mailer'
end
development.rb
config.action_mailer.raise_delivery_errors = true
config.action_mailer.raise_delivery_errors = true
config.action_mailer.delivery_method = :test
host = 'localhost:3000'
config.action_mailer.default_url_options = { :host=> host }
Sandeep,
Keep reading the tutorial. I used an older version Michael's tutorial to learn rails, but came back when I wanted to send confirmation emails as well. I don't think you should expect an actual email to be sent (at least not in development mode), rails 4.1.x has mailer previews that you can view, which I think is what the tutorial covers.
When you get to a production environment, then you will need to set up an external server to send the actual email, but that will be in your production.rb file. If you are just learning, then you have many options, including setting up a gmail account (separate from your private account) in which to send a limited number of emails per day.
Here is the link I think you should look at:
listing 10.15
My experience with Michael Hartl's tutorial was that it was almost flawless, you may have an issue or two with your particular system setup, but I think for the most part any issues I had were related to me not following the text properly.
Hope this help, good luck!
Reading from the bottom of page 491: "Note that you will not receive an actual email in a development environment, but it will show up in your server logs. Section 10.3 discusses how to send email for real in a production environment."
So if you're wanting to follow the text explicitly, I'd checkout the goodies in Section 10.3. Hope that helps!
Add this to your config/environments/development.rb file
config.action_mailer.raise_delivery_errors = true
so you get notified if there is an error on email delivery.
Other than that you can add a field to the users table like activation_sent_at and populate it when sending the email so you can refer to it later.

Email notifications not working using rails

I will preface this saying that I know almost nothing about rails. But I am trying to fix this issue involving rails so any help would be greatly appreciated (and also, if you could dumb it down for me that would be great!)
We have a rails email notification set up for two of our sites. If a user fills out an application on our English site, then a notification email is sent to person A. If a user fills out an application on our French site, then a notification email is sent to person B.
However, at the moment it seems like all emails are going to person A regardless of whether an application is filled out on the English or French site. How can I fix this?
Here is the code from the admin_mailer.rb file:
class AdminMailer < ActionMailer::Base
default from: 'noreply#canwise.com'
def contact_email
#contact = Contact.last
mail to: 'email1#1test.com'
end
def application_email
#application = Application.last
mail to: 'email1#test.com'
end
def eps_contact_email
#contact = Contact.last
mail to: "email2#test.com"
end
def eps_application_email
#application = Application.last
mail to: 'email2#test.com'
end
def salesforce_application_failure(application)
subject = "Application #{application.id} submitted by #{application.firstName} #{application.lastName} failed to submit to salesforce."
mail(to: 'test#test.com', subject: subject) do |format|
format.text { render text: '' }
end
end
end
And here is the code from the application.rb file:
def email_notification
if provider == 'www.frenchsite.com'
AdminMailer.eps_application_email.deliver
else
AdminMailer.application_email.deliver
end
end
HELP PLEASE!
If the emails are all going to email1#test.com, then it only means that your provider for french is not matching 'www.frenchsite.com'. The 'if' statement is always resulting in false.

Resources