google-gmail-api 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.bundle/config +1 -0
- data/.gitignore +32 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +40 -0
- data/Gemfile +3 -0
- data/LICENSE +20 -0
- data/README.md +294 -0
- data/Rakefile +32 -0
- data/TODO.md +3 -0
- data/account.yml.example +17 -0
- data/gmail.gemspec +41 -0
- data/lib/gmail.rb +159 -0
- data/lib/gmail/api_resource.rb +12 -0
- data/lib/gmail/base/create.rb +16 -0
- data/lib/gmail/base/delete.rb +31 -0
- data/lib/gmail/base/get.rb +17 -0
- data/lib/gmail/base/list.rb +54 -0
- data/lib/gmail/base/modify.rb +66 -0
- data/lib/gmail/base/trash.rb +33 -0
- data/lib/gmail/base/update.rb +26 -0
- data/lib/gmail/draft.rb +54 -0
- data/lib/gmail/gmail_object.rb +147 -0
- data/lib/gmail/label.rb +52 -0
- data/lib/gmail/message.rb +242 -0
- data/lib/gmail/thread.rb +38 -0
- data/lib/gmail/util.rb +45 -0
- data/lib/gmail/version.rb +3 -0
- data/test/gmail/api_resource_test.rb +47 -0
- data/test/gmail/draft_test.rb +104 -0
- data/test/gmail/gmail_object_test.rb +39 -0
- data/test/gmail/label_test.rb +129 -0
- data/test/gmail/message_test.rb +365 -0
- data/test/gmail/thread_test.rb +184 -0
- data/test/gmail/util_test.rb +18 -0
- data/test/test_data.rb +127 -0
- data/test/test_helper.rb +38 -0
- metadata +262 -0
data/TODO.md
ADDED
data/account.yml.example
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# Auth Method
|
2
|
+
google_api_auth_method: ENV['GOOGLE_API_AUTH_METHOD']
|
3
|
+
|
4
|
+
#App Info
|
5
|
+
application_name: ENV['APP_NAME']
|
6
|
+
application_version: ENV['APP_VERSION']
|
7
|
+
|
8
|
+
# Web Application Auth
|
9
|
+
client_id: ENV['GOOGLE_API_CLIENT_ID']
|
10
|
+
client_secret: ENV['GOOGLE_API_CLIENT_SECRET']
|
11
|
+
refresh_token: ENV['GOOGLE_API_REFRESH_TOKEN']
|
12
|
+
|
13
|
+
# Service Account Auth
|
14
|
+
google_api_key: ENV['GOOGLE_API_KEY']
|
15
|
+
google_api_email: ENV['GOOGLE_API_EMAIL']
|
16
|
+
email_account: ENV['GOOGLE_ACCOUNT']
|
17
|
+
auth_scopes: ENV['GOOGLE_AUTH_SCOPES']
|
data/gmail.gemspec
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
$:.push File.expand_path('../lib', __FILE__)
|
4
|
+
require 'gmail/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "google-gmail-api"
|
8
|
+
s.summary = "A Ruby interface to Gmail API (NO IMAP, NO SMTP), with all the tools you will need."
|
9
|
+
s.description = "A Ruby interface to Gmail API (NO IMAP, NO SMTP).
|
10
|
+
Search, read and send multipart emails; archive, mark as read/unread,
|
11
|
+
delete emails; and manage labels. Everything is done through the Gmail API without going through IMAP or SMTP Protocol
|
12
|
+
"
|
13
|
+
s.version = Gmail::VERSION
|
14
|
+
s.platform = Gem::Platform::RUBY
|
15
|
+
s.authors = ["Julien Hobeika","Keiran Betteley", 'Evaldas Grudzinskas']
|
16
|
+
s.homepage = "https://github.com/evaldasg/google-gmail-api"
|
17
|
+
s.licenses = ['MIT']
|
18
|
+
|
19
|
+
# runtime dependencies
|
20
|
+
s.required_ruby_version = '>= 1.9.3'
|
21
|
+
s.add_dependency "mime", ">= 0.1"
|
22
|
+
s.add_dependency "mail", ">= 2.2.1"
|
23
|
+
s.add_dependency "activesupport"
|
24
|
+
s.add_dependency 'google-api-client', "0.8.6"
|
25
|
+
s.add_dependency "hooks", ">=0.4.0"
|
26
|
+
s.add_dependency "hashie", ">=3.3.2"
|
27
|
+
s.add_dependency "stringex"
|
28
|
+
|
29
|
+
# development dependencies
|
30
|
+
s.add_development_dependency "rake"
|
31
|
+
s.add_development_dependency "test-unit"
|
32
|
+
s.add_development_dependency('mocha', '~> 1.0.0')
|
33
|
+
s.add_development_dependency('shoulda', '~> 3.5.0')
|
34
|
+
|
35
|
+
s.add_development_dependency "gem-release"
|
36
|
+
|
37
|
+
s.files = `git ls-files`.split("\n")
|
38
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
39
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
40
|
+
s.require_paths = ["lib"]
|
41
|
+
end
|
data/lib/gmail.rb
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
require 'mail'
|
2
|
+
require 'hashie'
|
3
|
+
require 'hooks'
|
4
|
+
require 'google/api_client'
|
5
|
+
require 'gmail/gmail_object'
|
6
|
+
require 'gmail/api_resource'
|
7
|
+
#base
|
8
|
+
require 'gmail/base/create'
|
9
|
+
require 'gmail/base/delete'
|
10
|
+
require 'gmail/base/get'
|
11
|
+
require 'gmail/base/list'
|
12
|
+
require 'gmail/base/update'
|
13
|
+
require 'gmail/base/modify'
|
14
|
+
require 'gmail/base/trash'
|
15
|
+
|
16
|
+
#object
|
17
|
+
require 'gmail/util'
|
18
|
+
require 'gmail/message'
|
19
|
+
require 'gmail/draft'
|
20
|
+
require 'gmail/thread'
|
21
|
+
require 'gmail/label'
|
22
|
+
|
23
|
+
module Gmail
|
24
|
+
|
25
|
+
class << self
|
26
|
+
attr_accessor :auth_method, :client_id, :client_secret,
|
27
|
+
:refresh_token, :auth_scopes, :email_account, :application_name, :application_version
|
28
|
+
attr_reader :service, :client, :mailbox_email
|
29
|
+
def new hash
|
30
|
+
[:auth_method, :client_id, :client_secret, :refresh_token, :auth_scopes, :email_account, :application_name, :application_version].each do |accessor|
|
31
|
+
Gmail.send("#{accessor}=", hash[accessor.to_s])
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
Google::APIClient.logger.level = 3
|
37
|
+
@service = Google::APIClient.new.discovered_api('gmail', 'v1')
|
38
|
+
Google::APIClient.logger.level = 2
|
39
|
+
|
40
|
+
begin
|
41
|
+
Gmail.new YAML.load_file("account.yml") # for development purpose
|
42
|
+
rescue
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.request(method, params={}, body={}, auth_method=@auth_method)
|
47
|
+
|
48
|
+
params[:userId] ||= "me"
|
49
|
+
case auth_method
|
50
|
+
when "web_application"
|
51
|
+
if @client.nil?
|
52
|
+
self.connect
|
53
|
+
end
|
54
|
+
when "service_account"
|
55
|
+
if @client.nil?
|
56
|
+
self.service_account_connect
|
57
|
+
elsif self.client.authorization.principal != @email_account
|
58
|
+
self.service_account_connect
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
if body.empty?
|
63
|
+
response = @client.execute(
|
64
|
+
:api_method => method,
|
65
|
+
:parameters => params,
|
66
|
+
|
67
|
+
:headers => {'Content-Type' => 'application/json'})
|
68
|
+
else
|
69
|
+
|
70
|
+
response = @client.execute(
|
71
|
+
:api_method => method,
|
72
|
+
:parameters => params,
|
73
|
+
:body_object => body,
|
74
|
+
:headers => {'Content-Type' => 'application/json'})
|
75
|
+
end
|
76
|
+
parse(response)
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.mailbox_email
|
81
|
+
@mailbox_email ||= self.request(@service.users.to_h['gmail.users.getProfile'])[:emailAddress]
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
|
86
|
+
def self.connect(client_id=@client_id, client_secret=@client_secret, refresh_token=@refresh_token)
|
87
|
+
unless client_id
|
88
|
+
raise 'No client_id specified'
|
89
|
+
end
|
90
|
+
|
91
|
+
unless client_secret
|
92
|
+
raise 'No client_secret specified'
|
93
|
+
end
|
94
|
+
|
95
|
+
unless refresh_token
|
96
|
+
raise 'No refresh_token specified'
|
97
|
+
end
|
98
|
+
|
99
|
+
@client = Google::APIClient.new(
|
100
|
+
application_name: @application_name,
|
101
|
+
application_version: @application_version
|
102
|
+
)
|
103
|
+
@client.authorization.client_id = client_id
|
104
|
+
@client.authorization.client_secret = client_secret
|
105
|
+
@client.authorization.refresh_token = refresh_token
|
106
|
+
@client.authorization.grant_type = 'refresh_token'
|
107
|
+
@client.authorization.fetch_access_token!
|
108
|
+
@client.auto_refresh_token = true
|
109
|
+
|
110
|
+
#@service = @client.discovered_api('gmail', 'v1')
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.service_account_connect(
|
115
|
+
client_id=@client_id, client_secret=@client_secret,
|
116
|
+
email_account=@email_account, auth_scopes=@auth_scopes,
|
117
|
+
application_name=@application_name, application_version=@application_version
|
118
|
+
)
|
119
|
+
puts "Authenticating service account - #{email_account}"
|
120
|
+
|
121
|
+
|
122
|
+
@client = Google::APIClient.new(application_name: application_name, application_version: application_version)
|
123
|
+
|
124
|
+
|
125
|
+
|
126
|
+
key = Google::APIClient::KeyUtils.load_from_pem(
|
127
|
+
client_secret,
|
128
|
+
'notasecret')
|
129
|
+
asserter = Google::APIClient::JWTAsserter.new(
|
130
|
+
client_id,
|
131
|
+
auth_scopes,
|
132
|
+
key
|
133
|
+
)
|
134
|
+
@client.authorization = asserter.authorize(email_account)
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
def self.parse(response)
|
139
|
+
begin
|
140
|
+
|
141
|
+
if response.body.empty?
|
142
|
+
return response.body
|
143
|
+
else
|
144
|
+
response = JSON.parse(response.body)
|
145
|
+
end
|
146
|
+
|
147
|
+
rescue JSON::ParserError
|
148
|
+
raise "error code: #{response.error},body: #{response.body})"
|
149
|
+
end
|
150
|
+
|
151
|
+
r = Gmail::Util.symbolize_names(response)
|
152
|
+
if r[:error]
|
153
|
+
raise "#{r[:error]}"
|
154
|
+
end
|
155
|
+
r
|
156
|
+
end
|
157
|
+
|
158
|
+
|
159
|
+
end # Gmail
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Gmail
|
2
|
+
module Base
|
3
|
+
module Create
|
4
|
+
module ClassMethods
|
5
|
+
def create(body, opts={})
|
6
|
+
response = Gmail.request(base_method.send("create"), {}, body)
|
7
|
+
Util.convert_to_gmail_object(response, class_name.downcase)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.included(base)
|
12
|
+
base.extend(ClassMethods)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Gmail
|
2
|
+
module Base
|
3
|
+
module Delete
|
4
|
+
def delete(opts={})
|
5
|
+
response = Gmail.request(self.class.base_method.send("delete"),{id: id})
|
6
|
+
if response == ""
|
7
|
+
true
|
8
|
+
else
|
9
|
+
false
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def delete(id, opts={})
|
15
|
+
response = Gmail.request(base_method.send("delete"),{id: id})
|
16
|
+
if response == ""
|
17
|
+
true
|
18
|
+
else
|
19
|
+
false
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.included(base)
|
25
|
+
base.extend(ClassMethods)
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Gmail
|
2
|
+
module Base
|
3
|
+
module Get
|
4
|
+
module ClassMethods
|
5
|
+
def get(id)
|
6
|
+
response = Gmail.request(base_method.send("get"), {id: id})
|
7
|
+
Util.convert_to_gmail_object(response, class_name.downcase)
|
8
|
+
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.included(base)
|
13
|
+
base.extend(ClassMethods)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Gmail
|
2
|
+
module Base
|
3
|
+
module List
|
4
|
+
module ClassMethods
|
5
|
+
def all(filters={}, opts={})
|
6
|
+
max_results = filters[:maxResults] || 100
|
7
|
+
opts[:items] ||= []
|
8
|
+
|
9
|
+
if max_results == -1
|
10
|
+
filters.merge!({maxResults: 100})
|
11
|
+
end
|
12
|
+
response = Gmail.request(base_method.send("list"), filters)
|
13
|
+
items = response["#{class_name.downcase}s".to_sym] || []
|
14
|
+
next_page_token = response[:nextPageToken]
|
15
|
+
opts[:items] = opts[:items] + items
|
16
|
+
|
17
|
+
if items.count < 100 || items.count < max_results
|
18
|
+
Util.convert_to_gmail_object(opts[:items], class_name.downcase)
|
19
|
+
else
|
20
|
+
max_results = (max_results == -1)?-1:max_results-items.count
|
21
|
+
all(filters.merge({maxResults: max_results, pageToken: next_page_token}), opts)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def search(q={})
|
26
|
+
if q.is_a? String
|
27
|
+
all({q: q})
|
28
|
+
else
|
29
|
+
query = ""
|
30
|
+
[:from, :to, :subject].each do |prop|
|
31
|
+
query += "#{prop.to_s}:(#{q[prop].downcase}) " unless q[prop].nil?
|
32
|
+
q.delete(prop)
|
33
|
+
end
|
34
|
+
[:in, :before, :after].each do |prop|
|
35
|
+
query += "#{prop.to_s}:#{q[prop]} " unless q[prop].nil?
|
36
|
+
q.delete(prop)
|
37
|
+
end
|
38
|
+
|
39
|
+
query += "#{q[:has_words]} " unless q[:has_words].nil?
|
40
|
+
query += "-{#{q[:has_not_words]}}" unless q[:has_not_words].nil?
|
41
|
+
q.delete(:has_words)
|
42
|
+
q.delete(:has_not_words)
|
43
|
+
|
44
|
+
all(q.merge({q: query}))
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.included(base)
|
50
|
+
base.extend(ClassMethods)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Gmail
|
2
|
+
module Base
|
3
|
+
module Modify
|
4
|
+
def modify!(addLabelIds=[], removeLabelIds=[])
|
5
|
+
response = Gmail.request(self.class.base_method.send("modify"),{id: id}, {addLabelIds: addLabelIds, removeLabelIds: removeLabelIds})
|
6
|
+
d = Util.convert_to_gmail_object(response, self.class.class_name.downcase)
|
7
|
+
@values = d.values
|
8
|
+
self
|
9
|
+
end
|
10
|
+
|
11
|
+
def modify(addLabelIds=[], removeLabelIds=[])
|
12
|
+
response = Gmail.request(self.class.base_method.send("modify"),{id: id}, {addLabelIds: addLabelIds, removeLabelIds: removeLabelIds})
|
13
|
+
d = Util.convert_to_gmail_object(response, self.class.class_name.downcase)
|
14
|
+
end
|
15
|
+
|
16
|
+
def archive
|
17
|
+
modify([], ["INBOX"])
|
18
|
+
end
|
19
|
+
|
20
|
+
def archive!
|
21
|
+
modify!([], ["INBOX"])
|
22
|
+
end
|
23
|
+
|
24
|
+
def unarchive
|
25
|
+
modify(["INBOX"], [] )
|
26
|
+
end
|
27
|
+
|
28
|
+
def unarchive!
|
29
|
+
modify!(["INBOX"], [] )
|
30
|
+
end
|
31
|
+
|
32
|
+
def star
|
33
|
+
modify(["STARRED"], [] )
|
34
|
+
end
|
35
|
+
|
36
|
+
def star!
|
37
|
+
modify!(["STARRED"], [] )
|
38
|
+
end
|
39
|
+
|
40
|
+
def unstar
|
41
|
+
modify([],["STARRED"] )
|
42
|
+
end
|
43
|
+
|
44
|
+
def unstar!
|
45
|
+
modify!([],["STARRED"] )
|
46
|
+
end
|
47
|
+
|
48
|
+
def mark_as_read
|
49
|
+
modify([],["UNREAD"] )
|
50
|
+
end
|
51
|
+
|
52
|
+
def mark_as_read!
|
53
|
+
modify!([],["UNREAD"] )
|
54
|
+
end
|
55
|
+
|
56
|
+
def mark_as_unread
|
57
|
+
modify(["UNREAD"],[] )
|
58
|
+
end
|
59
|
+
|
60
|
+
def mark_as_unread!
|
61
|
+
modify!(["UNREAD"],[] )
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Gmail
|
2
|
+
module Base
|
3
|
+
module Trash
|
4
|
+
def trash(opts={})
|
5
|
+
response = Gmail.request(self.class.base_method.send("trash"),{id: id})
|
6
|
+
Util.convert_to_gmail_object(response, self.class.class_name.downcase)
|
7
|
+
end
|
8
|
+
|
9
|
+
def untrash(opts={})
|
10
|
+
response = Gmail.request(self.class.base_method.send("untrash"),{id: id})
|
11
|
+
Util.convert_to_gmail_object(response, self.class.class_name.downcase)
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
def trash(id, opts={})
|
16
|
+
response = Gmail.request(base_method.send("trash"),{id: id})
|
17
|
+
Util.convert_to_gmail_object(response, class_name.downcase)
|
18
|
+
end
|
19
|
+
|
20
|
+
def untrash(id, opts={})
|
21
|
+
response = Gmail.request(base_method.send("untrash"),{id: id})
|
22
|
+
Util.convert_to_gmail_object(response, class_name.downcase)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.included(base)
|
27
|
+
base.extend(ClassMethods)
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|