2

I want the SMTP message_id of a mail using deliver_later(sidekiq).

I am able to get the id in the return obj of deliver_now but not deliver_later.

Message_id is also missing in the return value of mail() function inside my_action_mailer

  • when I send mail using-

sent_mail = some_mail().deliver_later

the response is a sidekiq job object which looks like this-

#<ActionMailer::DeliveryJob:0x007fefa1454780 @arguments=["xxxxxxx", "xxxxxxx", "deliver_now", xxxxxxx], @job_id="69b13253-55f9-4db9-a10a-0e8e4b998584", @queue_name="mailers", @priority=nil, @executions=0, @provider_job_id="6f75131d0c485534322cd728">
  • when I send the mail using -

sent_mail = some_mail().deliver_now

the response is a object object which looks like this-

<Mail::Message:xx, Multipart: true, Headers: <Date: xx>, <From: "xx" <xx@xx.com>>, <Reply-To: "xx" <xx@xx.com>>, <To: ["xx@xx.com"]>, <Message-ID: <5a7863381167a_ba183fdfebc3f928907cb@xx-xx-xx.xx.mail>>, <Subject: xx>, <Mime-Version: 1.0>, <Content-Type: multipart/mixed; boundary="--==_mimepart_xx"; charset=UTF-8>, <Content-Transfer-Encoding: 7bit>>

If you'll look closely at the above output, the return class is Mail::Message and it contains "Message-ID", which is what I need.

  • Also, when we call mail() function inside my mailer class, it also returns Mail::Message object but this obj dosent not contains Message-ID returned from SMTP server.

Is there any way in which I can get a Message-ID with using deliver_later

sarthak0415
  • 83
  • 1
  • 6

3 Answers3

1

This is the standard way of handling this case:

Overwrite ActionMailer::DeliveryJob, something like this:

class EmailDeliveryJob < ActionMailer::DeliveryJob
  def perform(mailer, mail_method, delivery_method, *args)
    response = mailer.constantize.public_send(mail_method, *args).send(delivery_method)

    # Use response.message_id as you want
  end
end

In your mailer,

class UserMailer < ApplicationMailer
  self.delivery_job = EmailDeliveryJob

  def welcome_email(user)
    mail to: user.email, subject: "Welcome!"
  end
end
Sandip Mane
  • 1,420
  • 1
  • 9
  • 14
0

I had the same problem but I decided to use Mailgun HTTPS API instead, but inside a Sidekiq Job.

So I call the job MailgunJob.perform_later(stuff_to_send)

And in the job

response = RestClient.post "https://mailgun_path", data
result = JSON.parse(response.body)
message_id = result['id']

Could you maybe use a job, and inside the job use deliver_now?

If you have found a better solution, can you write it here?

viktorsmari
  • 179
  • 1
  • 3
  • 12
0

One way would be to use an ActionMailer observer.

First, add the record id to your mailer message with a header or the #metadata attribute:

class YourMailer < ApplicationMailer

  def your_email(record)
    self.message['X-Your-Record-Id'] = record.id
    # or metadata['record_id'] = record.id

    ...
  end
end

Create an observer:

# app/models/custom_observer.rb

class CustomObserver
  def self.delivered_email(m)
    return unless m.delivered?

    # Access m.message_id or any header you want e.g. m['X-Your-Record-Id']
    Record.find(m['X-Your-Record-Id'].to_s).update_column(:message_id, m.message_id)
  end
end

Register the observer:

# config/initializers/action_mailer.rb

ActionMailer::Base.register_observer(CustomObserver)

And you're good to go. This works with #deliver and #deliver_later compared to creating a custom ActionMailer::MailDeliveryJob (which only works with #deliver_later).

Daniel
  • 4,082
  • 3
  • 27
  • 46