16

Maybe i've just got a bad brain today, but i suddenly can't figure out how to read an email with ruby's net/imap library. I've been at it for several hours. I typed several variations of "ruby imap read body" into google and have explored many sites. All of them have examples on how to pull attachments, sync e-mail servers, work with gmail etc... Some (like chilkat) are actually shareware libraries that do this for you. I have looked at the net/imap documentation and though there are several examples for doing everything but reading the body text.

I've worked with net/imap before but i've only needed to pull down the attachments in an inbox to a directory. I've asked in #ruby and #rubyonrails to no avail - no one could figure it out.


imap=Net::IMAP.new('mail.xks.com') #of course these are not real
imap.login('web1_xk3','pxx2006')
imap.select('INBOX')
imap.search(["SENTSINCE",(Time.now-1.day).strftime("%d-%b-%Y")]).each do |msgid|
     body = imap.fetch(msgid,"BODY")[0].attr["BODY"]
     ##insert code for getting the text from the body here
     ##body.class is BodyTypeText 
end

P.S. Though I do make a reference to active support in the code, this is sans-rails so that means no action-mailer

awesome_person
  • 1,223
  • 1
  • 10
  • 19

3 Answers3

26

Piggybacking off @Bjer's answer, here's a complete solution using the mail gem and gmail_xoauth gem to log into gmail using OAuth2 and parses all the emails:

imap = Net::IMAP.new('imap.gmail.com', 993, usessl = true, certs = nil, verify = false)
imap.authenticate('XOAUTH2', 'example@gmail.com', 'access_token_goes_here')
imap.select('INBOX')
imap.search(['ALL']).each do |message_id|

    msg = imap.fetch(message_id,'RFC822')[0].attr['RFC822']
    mail = Mail.read_from_string msg

    puts mail.subject
    puts mail.text_part.body.to_s
    puts mail.html_part.body.to_s

end
Andrew
  • 227,796
  • 193
  • 515
  • 708
  • Does `mail` gem has any dependency for `xoauth` because when I follow your code, it throws error `unknown auth type - "XOAUTH2"`. I am doing `require 'net/imap'` and `require mail` before your code – Jashwant Oct 24 '12 at 11:05
  • 1
    ah, yes, sorry about that. In order to use Google's XOAUTH2 you will need the [`gmail_xoauth`](https://github.com/nfo/gmail_xoauth) gem – Andrew Oct 24 '12 at 19:34
  • Oh, I am already using that plugin but after seeing your post, I thought that this great plugin with do the XOAUTH2 authentication too, so removed that. I am using both now. `gmail_xoauth` for authentication and `mail` for managing mails in better way. Its working fine ! – Jashwant Oct 24 '12 at 19:36
  • also, if you want to try using Google, [here's an example of using omniauth to get the access token](http://stackoverflow.com/a/12850157/48523) – Andrew Oct 24 '12 at 19:37
  • Is there different attribute equivalent to RFC822 for body extraction to work without fetching whole RFC822? It gets extremely slow when tere are big attachments. – Mahesh Jun 12 '13 at 15:01
  • @Andrew, I want to fetch the body and decode it but exclude fetching attachments completely. My problem is with imap.fetch(message_id,'RFC822')[0].attr['RFC822'] because it is extremely slow if there are attachments. – Mahesh Jun 12 '13 at 18:44
  • @Mahesh sorry, I don't have enough experience with it to answer your question. Try the different examples in the readme, then try asking on the [mailing list](http://groups.google.com/group/mail-ruby). Post here when you find the answer. – Andrew Jun 12 '13 at 22:45
23

If you just want just the body content of the message you can use:

body = imap.fetch(message_id,'BODY[TEXT]')[0].attr['BODY[TEXT]']

The IMAP API is a bit esoteric though. If you want to deal with the whole message, I would recommend using TMail to parse it into an easier to use format:

msg = imap.fetch(message_id,'RFC822')[0].attr['RFC822']
mail = TMail::Mail.parse(msg)
body = mail.body
geofflane
  • 2,681
  • 22
  • 21
7

Or if you're on ruby 1.9.x TMail seems to have problems
I'm using Mail (https://github.com/mikel/mail )

body = imap.fetch(message_id,'BODY[TEXT]')[0].attr['BODY[TEXT]']
msg = imap.fetch(-1,'RFC822')[0].attr['RFC822']
mail = Mail.read_from_string msg

body = mail.body
from = mail.from 
sivabudh
  • 31,807
  • 63
  • 162
  • 228
Bjer
  • 119
  • 1
  • 2