Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
PART 1 : Reply by Email to comments

Reply by Email to Comment

Goal:

Implementation of feature using which PublicLab users can comment directly on the node(question ,research note,event) just by replying to the email(notification email which users recieve on creation of new node)

Gem used 💎:

Mailman gem - A microframework for processing incoming email.

Implementation Steps:-

  1. Addmailman gem to the Gemfile

  2. Mailman will run in its own process I will write a script to run it in the application’s script directory.I am referencing it as mailman_server

  3. Initially, mailman_server script will look something like this :-

#!/usr/bin/env ruby
require "rubygems"
require "bundler/setup"
require "mailman"

Mailman::Application.run do
  default do
    puts "Received: #{message.subject}" 
  end
end    

We start the server by calling Mailman::Application.run and pass it a block. In this block we define what Mailman calls routes. These determine how the incoming mail is handled. We’ve used a default route in above example which will be called for every email that’s recieved. Now, how the replied emails sent by users are fetched here so that those emails can be used to generate comments after processing 😯

Here comes role of mailman gem - Mailman provides us 3 reciever options : POP3, IMAP, HTTP .I would be discussing POP3 in this page. Now,setting up the POP3 configuration is also simple.This is the example:-

Mailman.config.pop3 = {
  server: 'pop.gmail.com',
  port: 995,
  ssl: true,
  username: ENV["GMAIL_USERNAME"],
  password: ENV["GMAIL_PASSWORD"]
}

I have also made an demo video and a website of fetching of emails using mailman gem

Website link: https://mail-man-test.herokuapp.com/

Demo video link: https://www.youtube.com/watch?v=gMV4n4Hs7OA

4. Class Routing:

Instead of defining routes here in the script ,I will redirect the mails to the function in a Model where inside the function email is processed i.e., corresponding nid(node ID) is found using subject of the recieved email, we can use REGEX Expression to decode the nid or can also be done by equating the title of node with subject of email, after performing some operations on subject.Example of Model recieving method :-

class Article < ActiveRecord::Base
  def self.receive_mail(message)
    article_id = message.subject[/^Update (\d+)$/, 1]
    if article_id.present?
      Article.update(article_id, body: message.body.decoded)
    else
      Article.create! subject: message.subject, body: message.body.decoded, from: message.from.first
    end
  end
end

4.1 After the node is found the recieved email's body is used for creating the content of the comment 4.2 Using sender's email ID ,user is found from database. 4.3 A boolean term is also added in database and marked true if comment is generated through this process so that at time of rendering comments an email symbol can be attached. Finally, a new comment in the name of user,containing the email body corresponding to the node would be created successfully 🎉.

5. Logging

Logging is always beneficial and can come to our rescue anytime so I would enable logging by setting Mailman.config.loggerto a Logger instance. Example:

Mailman.config.logger = Logger.new('logs/mailman.log')

6. Polling 🕑

Polling is the process of checking for the new emails from the server. By default, polling time is 60 seconds i.e., arrival of new emails in the account(gmail account) is checked after every 60 seconds.We can easily change the polling time by mentioning poll interval like this :

Mailman.config.poll_interval = 90 # setting poll interval as 90 seconds

7. Exception Handling

What if some error pops up due to some bad/uncompatible email then what would happen,mailman server would crash in no time 😱 . We can validate in model function that comment is created only if node is found and many other validations.But, to be sure that server doesn't crash, we can add rescue clause to avoid any such instance,so that solves our problem 😄. Example:

Mailman::Application.run do
  default do
    begin
      Article.receive_mail(message)
    rescue
      Mailman.logger.error "Exception occurred while receiving message:\n#{message}"
      Mailman.logger.error [e, *e.backtrace].join("\n")
    end
  end

And,that completes the comment creation process using the recieved email.The resultant script would look something like this:

# script/mailman_server
#!/usr/bin/env ruby
require "rubygems"
require "bundler/setup"
require "mailman"

Mailman.config.logger = Logger.new("log/mailman.log")

Mailman.config.pop3 = {
  server: 'pop.gmail.com',
  port: 995,
  ssl: true,
  username: ENV["GMAIL_USERNAME"],
  password: ENV["GMAIL_PASSWORD"]
}

Mailman::Application.run do
  default do
    begin
      Article.receive_mail(message)
    rescue Exception => e
      Mailman.logger.error "Exception occurred while receiving message:\n#{message}"
      Mailman.logger.error [e, *e.backtrace].join("\n")
    end
  end
end

I have also made the demo video and website of the Reply by email to comment functionality.Please try it out and leave your comments below:

Demo Video link: https://youtu.be/b768EOUf6sg

Demo website link: https://mail-man-test.herokuapp.com/articles

Some Scenarios to handle

1. Unidentified Email ID

Firstly, the subject of the email received is checked and node ID is extracted from the subject. If subject is proper and node ID exists in database then Email ID is searched in database.In case the Email ID is not found then , an automated email would be sent to the unidentified user prompting him to sign up or update his profile with latest Email ID, so in automated Email all these steps would be presented to them.

2. Spam mails

If someone tries to spam our mailbox by sending multiple emails having same body then,an email would be send to moderator for marking user's profile as spam.Also,some user may try to send multiple emails with different body in that case body of the email is checked.Discussion on this can be done.

3. Empty Email body

As many email clients still allow sending email with empty body so we have to handle them too. In this case too, we can send automated reply to the user if he is identified - humbly asking them to include text in the email body.

Flowchart demonstrating the working of the Reply by Email to Comment feature

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.