I am trying to figure out how to read body of gmail using Ruby Gem "gmail"
I need to read past 10 days mails and make the objects as Json format
here is the code
require "gmail"
date1 = Date.today - 10
gmail = Gmail.connect("mailid", "password")
emails = gmail.inbox.emails(:after => date1)
emails.each_with_index do |mail, i|
#code
#mail.raw_message.body.decoded
end
Any suggestions how to read subject and body of mail
Thank you.
Assuming mail is instance of email, for reading subject:
mail.subject
for reading body
mail.body.raw_source
Related
Is there a way anyone knows to call the Google Maps APIs from Ruby for example?
With a key, you can access the APIs through simple HTTPS requests, which you can send using open-uri and parse using json.
require 'open-uri'
require 'ostruct'
require 'json'
def journey_between start, destination
key = "[Visit https://developers.google.com/maps/web/ to get a free key]"
url = "https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins=#{start}&destinations=#{destination}&key=#{key}"
json_response = open(url).read
journey_data = JSON.parse(json_response, object_class: OpenStruct).rows[0].elements[0]
return journey_data
end
journey = journey_between "London", "Glasgow"
puts journey.distance.text
#=> "412 mi"
puts journey.duration.text
#=> "6 hours 46 mins"
Unfortunately, you can't try this example without an API key. You can get one at https://developers.google.com/maps/web/ for free by registering a project under your Google account.
I am looking for an instance method from the ruby-gmail gem that would allow me to read either:
the body
or
subject
of a Gmail message.
After reviewing the documentation, found here, I couldn't find anything!?
There is a .message instance method found in the Gmail::Message class section; but it only returns, for lack of a better term, email "mumbo-jumbo," for the body.
My attempt:
#!/usr/local/bin/ruby
require 'gmail'
gmail = Gmail.connect('username', 'password')
emails = gmail.inbox.emails(:from => 'someone#mail.com')
emails.each do |email|
email.read
email.message
end
Now:
email.read does not work
email.message returns that, "mumbo-jumbo," mentioned above
Somebody else asked this question on SO but didn't get an answer.
This probably isn't exactly the answer to your question, but I will tell you what I have done in the past. I tried using the ruby-gmail gem but it didn't do what I wanted it to do in terms of reading a message. Or, at least, I couldn't get it to work. Instead I use the built-in Net::IMAP class to log in and get a message.
require 'net/imap'
imap = Net::IMAP.new('imap.gmail.com',993,true)
imap.login('<username>','<password>')
imap.select('INBOX')
subject_id = search_mail(imap, 'SUBJECT', '<mail_subject>')
subject_message = imap.fetch(subject_id,'RFC822')[0].attr['RFC822']
mail = Mail.read_from_string subject_message
body_message = mail.html_part.body
From here your message is stored in body_message and is HTML. If you want the entire email body you will probably need to learn how to use Nokogiri to parse it. If you just want a small bit of the message where you know some of the surrounding characters you can use a regex to find the part you are interested in.
I did find one page associated with the ruby-gmail gem that talks about using ruby-gmail to read a Gmail message. I made a cursory attempt at testing it tonight but apparently Google upped the security on my account and I couldn't get in using irb without tinkering with my Gmail configuration (according to the warning email I received). So I was unable to verify what is stated on that page, but as I mentioned my past attempts were unfruitful whereas Net::IMAP works for me.
EDIT:
I found this, which is pretty cool. You will need to add in
require 'cgi'
to your class.
I was able to implement it in this way. After I have my body_message, call the html2text method from that linked page (which I modified slightly and included below since you have to convert body_message to a string):
plain_text = html2text(body_message)
puts plain_text #Prints nicely formatted plain text to the terminal
Here is the slightly modified method:
def html2text(html)
text = html.to_s.
gsub(/( |\n|\s)+/im, ' ').squeeze(' ').strip.
gsub(/<([^\s]+)[^>]*(src|href)=\s*(.?)([^>\s]*)\3[^>]*>\4<\/\1>/i,
'\4')
links = []
linkregex = /<[^>]*(src|href)=\s*(.?)([^>\s]*)\2[^>]*>\s*/i
while linkregex.match(text)
links << $~[3]
text.sub!(linkregex, "[#{links.size}]")
end
text = CGI.unescapeHTML(
text.
gsub(/<(script|style)[^>]*>.*<\/\1>/im, '').
gsub(/<!--.*-->/m, '').
gsub(/<hr(| [^>]*)>/i, "___\n").
gsub(/<li(| [^>]*)>/i, "\n* ").
gsub(/<blockquote(| [^>]*)>/i, '> ').
gsub(/<(br)(| [^>]*)>/i, "\n").
gsub(/<(\/h[\d]+|p)(| [^>]*)>/i, "\n\n").
gsub(/<[^>]*>/, '')
).lstrip.gsub(/\n[ ]+/, "\n") + "\n"
for i in (0...links.size).to_a
text = text + "\n [#{i+1}] <#{CGI.unescapeHTML(links[i])}>" unless
links[i].nil?
end
links = nil
text
end
You also mentioned in your original question that you got mumbo-jumbo with this step:
email.message *returns mumbo-jumbo*
If the mumbo-jumbo is HTML, you can probably just use your existing code with this html2text method instead of switching over to Net::IMAP as I had discussed when I posted my original answer.
Nevermind, it's:
email.subject
email.body
silly me
ok, so how do I get the body in "readable" text? without all the encoding stuff and html?
Subject, text body and HTML body:
email.subject
if email.message.multipart?
text_body = email.message.text_part.body.decoded
html_body = email.message.html_part.body.decoded
else
# Only multipart messages contain a HTML body
text_body = email.message.body.decoded
html_body = text
end
Attachments:
email.message.attachments.each do |attachment|
path = "/tmp/#{attachment.filename}"
File.write(path, attachment.decoded)
# The MIME type might be useful
content_type = attachment.mime_type
end
require 'gmail'
gmail = Gmail.connect('username', 'password')
emails = gmail.inbox.emails(:from => 'someone#mail.com')
emails.each do |email|
puts email.subject
puts email.text_part.body.decoded
end
I have a ruby app and I am sending emails with this format found in the documentation http://ruby-doc.org/stdlib-2.0/libdoc/net/smtp/rdoc/Net/SMTP.html :
Net::SMTP.start('your.smtp.server', 25) do |smtp|
smtp.send_message msgstr, 'from#address', 'to#address'
end
This is my code:
def send_notification(exception)
msgstr = <<-END_OF_MESSAGE
From: Exchange Errors <exchangeerrors#5112.mysite.com>
To: Edmund Mai <emai#mysite.com>
Subject: test message
Date: Sat, 23 Jun 2001 16:26:43 +0900
Message-Id: <unique.message.id.string#mysite.com>
This is a test message.
END_OF_MESSAGE
Net::SMTP.start('localhost', 25) do |smtp|
smtp.send_message(msgstr, "exchangeerrors#5112.mysite.com", "emai#mysite.com")
end
end
However, the emails being sent have no subjects in them. The msgstr just becomes the body of the email. I don't see anywhere in the documentation on how to specify a mail subject. Does anyone know?
So I looked at the documentation and it looks like Net::SMTP doesn't support this. In the documentation it says this:
What is This Library NOT?¶ ↑
This library does NOT provide functions to compose internet mails. You must create them by yourself. If you want better mail support, try RubyMail or TMail. You can get both libraries from RAA. (www.ruby-lang.org/en/raa.html)
So I looked into the MailFactory gem (http://mailfactory.rubyforge.org/), which uses Net::SMTP actually:
require 'net/smtp'
require 'rubygems'
require 'mailfactory'
mail = MailFactory.new()
mail.to = "test#test.com"
mail.from = "sender#sender.com"
mail.subject = "Here are some files for you!"
mail.text = "This is what people with plain text mail readers will see"
mail.html = "A little something <b>special</b> for people with HTML readers"
mail.attach("/etc/fstab")
mail.attach("/some/other/file")
Net::SMTP.start('smtp1.testmailer.com', 25, 'mail.from.domain', fromaddress, password, :cram_md5) { |smtp|
mail.to = toaddress
smtp.send_message(mail.to_s(), fromaddress, toaddress)
}
and now it works!
I know this mostly useless now but, I tried sending email using Net::SMTP and was able to set the subject. maybe you could have tried
Subject: 'test message' instead of Subject: test message
I actually managed to send a mail with NET::SMTP with a subject, I think that the indentation is important (example taken from http://www.tutorialspoint.com/ruby/ruby_sending_email.htm) -
message = <<MESSAGE_END
From: Private Person <me#fromdomain.com>
To: A Test User <test#todomain.com>
Subject: SMTP e-mail test
This is a test e-mail message.
MESSAGE_END
Net::SMTP.start('localhost') do |smtp|
smtp.send_message message, 'me#fromdomain.com',
'test#todomain.com'
end
The Subject will be correct. If you print the variable "message" you'll get this output-
"From: Private Person <me#fromdomain.com>\nTo: A Test User <test#todomain.com>\nSubject: SMTP e-mail test\n\nThis is a test e-mail message.\n"
While this code, with spaces in front of "Subject" -
message = <<MESSAGE_END
From: Private Person <me#fromdomain.com>
To: A Test User <test#todomain.com>
Subject: SMTP e-mail test
This is a test e-mail message.
MESSAGE_END
Net::SMTP.start('localhost') do |smtp|
smtp.send_message message, 'me#fromdomain.com',
'test#todomain.com'
end
will print -
"From: Private Person <me#fromdomain.com>\nTo: A Test User <test#todomain.com>\n Subject: SMTP e-mail test\n\nThis is a test e-mail message.\n"
And then the subject won't work.
pop = Net::POP3.new mailhost
pop.start mailuser, mailpass
if pop.mails.empty?
puts "Mailbox empty."
else
pop.mails.each do |mail|
if mail.pop.has_attachments?
mail.pop.attachments.each do |attachment|
puts attachment.original_filename
end
end
end
end
gives undefined method 'has_attachments?' for #<String:0xb7cc4f7c>.
Is this example no longer working?
mail.pop returns string representation of email see corresponding docs. If you want to parse it and work with mail object you can do it like this:
email = Mail.new(mail.pop)
I really recommend you to take a look into docs - if you'll have big attachments you can run into memory issues and this thing is explained in docs.
Using the Mail gem (i.e. Rails + ActionMailer), is there a clean way to get the display name of the recipient?
I can get the address with:
mail.to.first
And I can get the formatted display name + address with:
mail.header_fields.select{ |f| f.name == "To" }.first.to_s
But how can I get just the display name part (i.e. before the < and >). I know somebody is going to suggest a Regex, but that's not what I'm looking for, since I'd then have to parse out any encoding, which is something the Mail gem probably already does. I'm the author of a popular Mailer library in PHP and am aware of the pitfalls of just assuming the bit before < and > is human-readable, in the headers, when 8-bit characters come into play.
I can do this:
mail.header_fields.select{ |f| f.name == "To" }.first.parse.individual_recipients.first.display_name.text_value
But there must be a better way? :)
Figured it out, sorry. For anyone else who hits this thread looking for the solution:
mail[:to].display_names.first
The gotcha is that bracket access and dotted access are different for this gem.
From the doc:
mail = Mail.new
mail.to = 'Mikel Lindsaar <mikel#test.lindsaar.net>, ada#test.lindsaar.net'
mail.to #=> ['mikel#test.lindsaar.net', 'ada#test.lindsaar.net']
mail[:to] #=> '#<Mail::Field:0x180e5e8 #field=#<Mail::ToField:0x180e1c4
mail['to'] #=> '#<Mail::Field:0x180e5e8 #field=#<Mail::ToField:0x180e1c4
mail['To'] #=> '#<Mail::Field:0x180e5e8 #field=#<Mail::ToField:0x180e1c4
mail[:to].encoded #=> 'To: Mikel Lindsaar <mikel#test.lindsaar.net>, ada#test.lindsaar.net\r\n'
mail[:to].decoded #=> 'Mikel Lindsaar <mikel#test.lindsaar.net>, ada#test.lindsaar.net'
mail[:to].addresses #=> ['mikel#test.lindsaar.net', 'ada#test.lindsaar.net']
mail[:to].formatted #=> ['Mikel Lindsaar <mikel#test.lindsaar.net>', 'ada#test.lindsaar.net']
So to get the display name, you can use #display_name
mail[:to].addrs.first.display_name #=> Mikel Lindsaar
Use #address to get the email address
mail[:from].addrs.first.address #=> mikel#test.lindsaar.net