- Products
- Solutions Use casesBy industry
- Developers
- Resources Connect
- Pricing
Looking for emails is straightforward, however, trying to get them all in a single place with less noise can be a difficult task. Thanks to the Nylas Ruby API, we can make email threading possible.
If you want to learn more about Email Threads, read the blog post Stay Threaded: How to Manage and Control Email Threads
If you already have the Nylas Ruby SDK installed and your Ruby environment is configured, then continue along with the blog.
Otherwise, I would recommend that you read the post How to Send Emails with the Nylas Ruby SDK where the basic setup is explained.
Before we jump into the code, let’s see how our application actually works. We will have a single input field accepting the email address to get all the related email threads and messages included in those threads:
We’re going to list all email threads related to the address we used, as long as they have at least two messages:
The email threads are presented in an accordion, and when we open one, we will get the emails in a sequence, with the contact image and with the noise removed. So no emails, phone numbers or reply texts.
As we can see, both simple and nice.
To create a Ruby web application, our best option is to use Sinatra, one of the most popular Micro Frameworks in the Ruby world. We might need to install some additional gems:
$ gem install sinatra $ gem install puma $ gem install nokogiri
Once installed, we’re ready to go:
First, we’re going to create a folder called EmailThreading, and inside we’re going to create two folders, one called views and other called public.
Let’s create a file called EmailThreading.rb in the EmailThreading folder, and add the following code:
# Import your dependencies require 'dotenv/load' require 'nylas' require 'sinatra' require 'nokogiri' require 'date' # Initialize your Nylas API client nylas = Nylas::API.new( app_id: ENV["CLIENT_ID"], app_secret: ENV["CLIENT_SECRET"], access_token: ENV["ACCESS_TOKEN"] ) # Use the Nokogiri gem to clean up the email response def clean_content(raw_html) html = raw_html.encode('UTF-8', invalid: :replace, undef: :replace, replace: '', universal_newline: true).gsub(/\P{ASCII}/, '') parser = Nokogiri::HTML(html, nil, Encoding::UTF_8.to_s) parser.xpath('//script')&.remove parser.xpath('//style')&.remove parser.xpath('//text()').map(&:text).join('<br> ') end # Get the contact associated to the email address def get_contact(nylas, email) contact = nylas.contacts.where(email: email) if contact[0] != nil return contact[0] end end # Download the contact picture if it's not stored already def download_contact_picture(nylas, id) if id != nil contact = nylas.contacts.find(id) picture = contact.picture file_name = id + ".png" File.open("public/" + file_name,"wb") do |f| f.write File.open(picture, 'rb') {|file| file.read } end end end # When calling the application for the first time get '/' do _threads = [] # Call the page erb :main, :layout => :layout, :locals => {:threads => _threads} end # When asking for the email threading post '/search' do # Get parameter from form search = params[:search] # Search all threads related to the email address threads = nylas.threads.where(from: search,in: 'inbox') _threads = [] # Loop through all the threads threads.each{ |thread| _thread = [] _messages = [] _pictures = [] _names = [] # Look for threads with more than 1 message if thread.message_ids.length() > 1 # Get the subject of the first email _thread.push(thread.subject) # Loop through all messages contained in the thread thread.message_ids.each{ |message| # Get information from the message message = nylas.messages.find(message) # Try to get the contact information contact = get_contact(nylas, message.from[0].email) if contact != nil and contact != "" # If the contact is available, downloads its profile picture download_contact_picture(nylas, contact.id) end # Remove extra information from the message, like appended # message, email and phone number _messages.push(clean_content(message.body). gsub(/(\bOn.*\b)(?!.*\1)/,""). gsub(/[a-z0-9._-]+@[a-z0-9._-]+\.[a-z]{2,3}\b/i,""). gsub(/(\+\d{1,2}\s)?\(?\d{3}\)?[\s.-]\d{3}[\s.-]\d{4}/,""). gsub(/twitter:.+/i,"")) # Convert date to something readable datetime = Time.at(message.date).to_datetime date = datetime.to_s.scan(/\d{4}-\d{2}-\d{2}/) time = datetime.to_s.scan(/\d{2}:\d{2}:\d{2}/) if contact == nil or contact == "" _pictures.push("NotFound.png") _names.push("Not Found" + " on " + date[0] + " at " + time[0]) else # If there's a contact, pass picture information, # name and date and time of message _pictures.push(contact.id + ".png") _names.push(contact.given_name + " " + contact.surname + " on " + date[0] + " at " + time[0]) end } _thread.push(_messages) _thread.push(_pictures) _thread.push(_names) _threads.push(_thread) end } # Call the page and display threads erb :main, :layout => :layout, :locals => {:threads => _threads} end
Inside the views folder, we need to create two different files, let’s start with layout.erb:
<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- Call the TailwindCSS and Flowbite libraries --> <script src="https://cdn.tailwindcss.com"></script> <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/flowbite.min.css" /> <title>Nylas’ Email Threading</title> <body> <%= yield %> <script src="https://unpkg.com/[email protected]/dist/flowbite.js"></script> </body> </html>
We’re calling both the TailwindCSS and Flowbite libraries to handle CSS.
We need to create then the file main.erb:
<div class="grid bg-green-300 border-green-600 border-b p-4 m-4 rounded place-items-center"> <p class="text-6xl text-center">Email Threading</p><br> <!-- Create the form--> <form method = "post" action="search"> <div class="flex bg-blue-300 border-blue-600 border-b p-4 m-4 rounded place-items-center"> <input type="text" name="search" value="" size="50"></input> <button type="submit" class="block bg-blue-500 hover:bg-blue-700 text-white text-lg mx-auto py-2 px-4 rounded-full">Search</button> </div> </form> <!-- Do we have any threads? --> <% if threads != [] %> <div id="accordion-collapse" data-accordion="collapse"> <!-- Counter to generate accordion elements --> <% counter = 1 %> <!-- Loop through each thread --> <% threads.each do |thread| %> <!-- Define values for the accordion elements --> <% heading = "accordion-collapse-heading-" + counter.to_s %> <% body = "accordion-collapse-body-" + counter.to_s %> <% _body = "#accordion-collapse-body-" + counter.to_s %> <h2 id=<%= heading %> > <button type="button" class="flex items-center justify-between w-full p-5 font-medium text-left text-gray-500 border border-b-0 border-gray-200 focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-800 dark:border-gray-700 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800" data-accordion-target=<%= _body %> aria-expanded="false" aria-controls=<%= body %>> <!-- Title of the thread --> <span><%= thread[0] %></span> <svg data-accordion-icon class="w-6 h-6 shrink-0" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg> </button> </h2> <div id=<%= body %> class="hidden" aria-labelledby=<%= heading %>> <div class="p-5 font-light border border-b-0 border-gray-200 dark:border-gray-700"> <!-- Get size of thread array --> <% count = thread[1].length() %> <!-- Define amount of elements on the grid --> <% count_str = "grid-rows-" + count.to_s %> <div class="grid <%= count_str %> grid-flow-col gap-4"> <!-- Counter to access array elements --> <% counter = 0 %> <!-- Loop through each email --> <% thread[1].each do |message| %> <div class="col-span-2 ..."> <!-- Display image and date/time of email --> <img class="mx-auto" src="<%= thread[2][counter] %>"><b> <p class="text-center"><%= thread[3][counter] %></p></b><br> <!-- Display the email message --> <%= message %> </div> <% counter = counter + 1 %> <% end %> </div> </div> </div> <% end %> </div> <% end %> </div>
If you wonder about the public folder, it will only hold the contact profile picture, so there’s nothing we need to do there.
And that’s it. We’re ready to roll.
In order to run our application, we just need to type the following on the terminal window:
$ ruby EmailThreading.rb
Our application will be running on port 4567 of localhost, so we just need to open our favourite browser and go to the following address:
http://localhost:4567
If you want to learn more about our Email APIs, please go to our documentation Email API Overview as well Threads and Messages.
Don’t miss the action, watch our LiveStream Coding with Nylas:
Blag aka Alvaro Tejada Galindo is a Senior Developer Advocate at Nylas. He loves learning about programming and sharing knowledge with the community. When he’s not coding, he’s spending time with his wife, daughter and son. He loves Punk Music and reading all sorts of books.