Gmail Threading, Imap and Ruby

Accessing Thread ID via Gmail IMAP Extensions (xoauth2)

I did manage to solve it via IMAP using this monkey patch - https://gist.github.com/kellyredding/2712611

Although it would be worth exploring the Gmail API, as its far easier now - https://developers.google.com/gmail/

Gmail: Sync Inbox (IMAP & POP3 disabled)

Solution

In order to avoid the usage of sockets for this assignment I've tried to build an Chrome extension/apps, but this would become as riduculous as using sockets.

Thanks god Google Apps Script exists.

Code



var VENDOR_NAME_INDEX = 0;
var CONTACTS_INDEX = VENDOR_NAME_INDEX + 1;
var HAS_REPLIED_INDEX = CONTACTS_INDEX + 1;
var COMMENTS_INDEX = HAS_REPLIED_INDEX + 1;

var VENDORS_SPREADSHEET_URL = "https://docs.google.com/a/jabil.com/spreadsheet/ccc?key=0Aj7MWNXx-gzsdHJQbGhQZUVkMjBFeVNZX0dXdDZjYWc#gid=0";
var VENDORS_GMAIL_QUERY = 'in:sent has:attachment after:2013/06/26 before:2013/06/28 subject: "*Jabil CUU /// Shipping Letter*"';

// http://flesler.blogspot.mx/2008/11/fast-trim-function-for-javascript.html
String.prototype.trimLeft = function() {
var pivot = -1;
while (this.charCodeAt(++pivot) < 33);
return this.slice(pivot, this.length);
}

// http://flesler.blogspot.mx/2008/11/fast-trim-function-for-javascript.html
String.prototype.trimRight = function() {
var pivot = this.length;
while (this.charCodeAt(pivot--) < 33);
return this.slice(0, pivot);
}

vendors = [];
function processSentItems() {
var threads = GmailApp.search(VENDORS_GMAIL_QUERY);
var ss = SpreadsheetApp.openByUrl(VENDORS_SPREADSHEET_URL);

var data = ss.getDataRange();
var rows = data.getNumRows();
var values = data.getValues();

for (var i = 0; i < rows; ++i) {
var row = values[i];

var vendor = row[VENDOR_NAME_INDEX];
var contacts = row[CONTACTS_INDEX];
var replied_back = row[HAS_REPLIED_INDEX];
var comments = row[COMMENTS_INDEX];

vendors.push({
name: vendor,
contacts: contacts,
replied_back: replied_back,
comments: comments
});

}

threads.forEach(function(thread) {
var id = thread.getId();
var subject = thread.getFirstMessageSubject();
var vendorName = subject.split("-")[0];

vendorName = vendorName.trimLeft();
vendorName = vendorName.trimRight();

var vendor = getVendorByName(vendorName);

if (typeof vendor == "object") {
if (vendor.replied_back.toLowerCase() != "yes") {
thread.replyAll("Friendly reminder...")
}
}
});

};

function getVendorByName(vendorName) {
for (var i = 0; i < vendors.length; ++i) {
if (vendors[i].name == vendorName) {
return vendors[i];
}
}
};

Share a Net::IMAP connection between controller actions

How about you create a service object (singleton) that wraps you Net::IMAP. You can stick it in app/services/imap_service.rb or something like that. For an example on what that would look like:

require 'singleton' # This is part of the standard library
require 'connection_pool' # https://github.com/mperham/connection_pool

class IMAPService
include Singleton

def initialize
@imap = ConnectionPool.new(size: 15) { Net::IMAP.new(server, 993, true) }
end

def inbox(user, password)
@imap.with do |conn|
conn.login(user, password)
conn.select("INBOX")
end
end
end

You access this singleton like IMAPService.instance e.g. IMAPService.instance.inbox(user, password). I added in the connect_pool gem as per our discussion to make sure this is thread safe. There is no attr_reader :imap on IMAPService. However, you can add one so that you can directly access the connection pool in your code if you don't want to include all of the necessary methods here (although I recommend using the service object if possible). Then you can do IMAPService.instance.imap.with { |conn| conn.login(user, password) } and don't need to rely on methods in IMAPService.

It's worth noting that you don't have to use the Singleton mixin. There is a really good article on Implementing "the lovely" Singleton which will show you both ways to do it.

Header in gmail for thread hinting

There are a couple of mail headers that other mail clients use to help threading - not sure if gmail supports them. The first is the standard RFC-822 "In-Reply-To: <messageid>", and the second is the non-standard (stolen from Usenet) "References: <messageid>,<messageid>,...".

Ruby on Rails with IMAP IDLE for multiple accounts

There is no need to spawn a new thread for each IMAP session. These can be done in a single thread.

Maintain an Array (or Hash) of all users and their IMAP sessions. Spawn a thread, in that thread, send IDLE keep-alive to each of the connections one after the other. Run the loop periodically. This will definitely give you far more concurrency than your current approach.

A long term approach will be to use EventMachine. That will allow using many IMAP connections in the same thread. If you are processing web requests in the same process, you should create a separate thread for Event Machine. This approach can provide you phenomenal concurrency. See https://github.com/ConradIrwin/em-imap for Eventmachine compatible IMAP library.



Related Topics



Leave a reply



Submit