-
Notifications
You must be signed in to change notification settings - Fork 119
Home
Keith Schacht edited this page Dec 31, 2017
·
8 revisions
Here is an example of working code. I spent way too many hours just trying to get a list of conversations from Gmail that I could manipulate so hopefully this helps someone else.
System:
- Ruby 2.3.5
- On a Mac with Chrome installed, it will launch Chrome to authenticate
Notables:
- The oauth2 code is adapted from Google's ruby example for the Gmail API. Follow their guide first to get your app created and a local YML key. Once their code works you'll see the code below is almost identical, notably the SCOPE needed to be changed to authenticate for IMAP. Oh, and every 60 minutes you have to re-authenticate with IMAP, I could not figure out how to fix that.
- I didn't want email messages I wanted conversations so I wrote a couple classes that fake conversations
- The reason I use
gmail.mailbox(...)
rather thangmail.inbox.
is because it's required in order for.archive!
to work. If you're in the inbox view you can't remove a message from the inbox view.
require 'google/apis/gmail_v1'
require 'googleauth'
require 'googleauth/stores/file_token_store'
require 'gmail'
require 'fileutils'
require 'forwardable'
require 'set'
OOB_URI = 'urn:ietf:wg:oauth:2.0:oob'
APPLICATION_NAME = 'Taskmaster'
CLIENT_SECRETS_PATH = 'client_secret.json'
CREDENTIALS_PATH = File.join(Dir.home, '.credentials',
"taskmaster.yaml")
SCOPE = "https://mail.google.com/" # gmail API: Google::Apis::GmailV1::AUTH_GMAIL_MODIFY
class GmailAuthorize
def token; @service.authorization.access_token; end
def initialize
# Initialize the API
@service = Google::Apis::GmailV1::GmailService.new
@service.client_options.application_name = APPLICATION_NAME
@service.authorization = authorize
@service.authorization.access_token
end
##
# Ensure valid credentials, either by restoring from the saved credentials
# files or intitiating an OAuth2 authorization. If authorization is required,
# the user's default browser will be launched to approve the request.
#
# @return [Google::Auth::UserRefreshCredentials] OAuth2 credentials
def authorize
FileUtils.mkdir_p(File.dirname(CREDENTIALS_PATH))
client_id = Google::Auth::ClientId.from_file(CLIENT_SECRETS_PATH)
token_store = Google::Auth::Stores::FileTokenStore.new(file: CREDENTIALS_PATH)
authorizer = Google::Auth::UserAuthorizer.new(
client_id, SCOPE, token_store)
user_id = 'default'
credentials = authorizer.get_credentials(user_id)
if credentials.nil? || credentials.expires_at < Time.now
url = authorizer.get_authorization_url(
base_url: OOB_URI)
puts "Open the following URL in the browser and enter the " +
"resulting code after authorization"
puts url
system("/usr/bin/open -a '/Applications/Google Chrome.app' '#{url}'")
print "paste response: "
code = gets
credentials = authorizer.get_and_store_credentials_from_code(
user_id: user_id, code: code, base_url: OOB_URI)
end
credentials
end
end
class Conversations
include Enumerable
extend Forwardable
def_delegators :to_a, :each, :<<, :[], :collect, :select, :first, :last, :count,
:length; :reverse
def to_a; @conversations.values; end
def initialize(emails)
@emails = emails
@conversations = {}
emails.reverse.each_with_index do |email, i|
print "#{i+1}.."
if @conversations.has_key?(email.thr_id)
@conversations[email.thr_id].add_email(email)
else
@conversations[email.thr_id] = Conversation.new(email)
end
end
end
def inspect
"#<Conversations count=#{count}>"
end
end
class Conversation
def emails; @emails.values; end
def subject; @subject; end
def full_name; @full_name; end
def labels; @labels; end
def label; @labels.first; end
def id; @id; end
def permalink; "https://mail.google.com/mail/#all/#{@id.to_i.to_s(16)}"; end
def length; @emails.keys.length; end
def initialize(email)
@emails = {}
@subject = email.subject
@full_name = email.from.first.name
@id = email.thr_id
@labels = Set.new()
add_email(email)
end
def add_email(email)
@emails[email.msg_id] = email
email.labels.each { |l| @labels << l if l.is_a?(String) }
email
end
def archive!
emails.each { |email| email.archive! }
end
def star!
emails.first.star!
end
def inspect
"#<Conversation id=#{id} length=#{length} subject=#{subject}>"
end
end
puts "Checking inbox..."
gmail = Gmail.connect(:xoauth2, "xxxxx@gmail.com", GmailAuthorize.new.token)
puts gmail.mailbox('[Gmail]/All Mail').emails(gm: 'in:inbox').count
conversations = Conversations.new(gmail.mailbox('[Gmail]/All Mail').emails(gm: 'in:inbox'))