-
Notifications
You must be signed in to change notification settings - Fork 120
Expand file tree
/
Copy pathgmail.rb
More file actions
245 lines (223 loc) · 5.63 KB
/
gmail.rb
File metadata and controls
245 lines (223 loc) · 5.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
require 'net/imap'
class Gmail
VERSION = '0.0.9'
class NoLabel < RuntimeError; end
##################################
# Gmail.new(username, password)
##################################
def initialize(username, password)
# This is to hide the username and password, not like it REALLY needs hiding, but ... you know.
# Could be helpful when demoing the gem in irb, these bits won't show up that way.
class << self
class << self
attr_accessor :username, :password
end
end
meta.username = username =~ /@/ ? username : username + '@gmail.com'
meta.password = password
@imap = Net::IMAP.new('imap.gmail.com',993,true,nil,false)
if block_given?
login # This is here intentionally. Normally, we get auto logged-in when first needed.
yield self
logout
end
end
###########################
# READING EMAILS
#
# gmail.inbox
# gmail.label('News')
#
###########################
def create_label(name)
@xlist_result = nil
imap.create(name)
end
# List the available labels
def labels
labels = []
prefixes = ['']
done = []
until prefixes.empty?
prefix = prefixes.shift
done << prefix
(imap.list(prefix, "%")||[]).each { |e|
if e[:attr].include?(:Haschildren)
unless done.include?(e[:name]+"/") or e[:name].empty?
prefixes << e[:name]+"/"
end
end
unless e[:attr].include?(:Noselect)
labels << e[:name]
end
}
end
labels
end
def imap_xlist
unless @imap.respond_to?(:xlist)
def @imap.xlist(refname, mailbox)
handler = proc do |resp|
if resp.kind_of?(Net::IMAP::UntaggedResponse) and resp.name == "XLIST" && resp.raw_data.nil?
list_resp = Net::IMAP::ResponseParser.new.instance_eval {
@str, @pos, @token = "#{resp.name} " + resp.data, 0, nil
@lex_state = Net::IMAP::ResponseParser::EXPR_BEG
list_response
}
if @responses['XLIST'].last == resp.data
@responses['XLIST'].pop
@responses['XLIST'].push(list_resp.data)
end
end
end
synchronize do
add_response_handler(handler)
send_command('XLIST', '', '*')
remove_response_handler(handler)
return @responses.delete('XLIST')
end
end
end
@xlist_result ||= @imap.xlist('', '*')
end
def self.gmail_label_types
[:Inbox, :Allmail, :Spam, :Trash, :Drafts, :Important, :Starred, :Sent]
end
def gmail_label_types
self.class.gmail_label_types
end
def normal_labels
imap_xlist.reject { |label|
label.attr.include?(:Noselect) or label.attr.any? { |flag| gmail_label_types.include?(flag) }
}.map { |label|
label.name
}
end
def imap_xlist!
@xlist_result = nil
imap_xlist
end
def label_of_type(type)
info = imap_xlist.find { |l| l.attr.include?(type) }
info && info.name || nil
end
gmail_label_types.each do |label|
module_eval <<-EOL
def #{label.to_s.downcase} &block
in_label(#{label.to_s.downcase}_label, &block)
end
def #{label.to_s.downcase}_label
label_of_type(#{label.inspect})
end
EOL
end
# gmail.label(name)
def label(name)
mailboxes[name] ||= Mailbox.new(self, name)
end
alias :mailbox :label
###########################
# MAKING EMAILS
#
# gmail.generate_message do
# ...inside Mail context...
# end
#
# gmail.deliver do ... end
#
# mail = Mail.new...
# gmail.deliver!(mail)
###########################
def generate_message(&block)
require 'net/smtp'
require 'smtp_tls'
require 'mail'
mail = Mail.new(&block)
mail.delivery_method(*smtp_settings)
mail
end
def deliver(mail=nil, &block)
require 'net/smtp'
require 'smtp_tls'
require 'mail'
mail = Mail.new(&block) if block_given?
mail.delivery_method(*smtp_settings)
mail.from = meta.username unless mail.from
mail.deliver!
end
###########################
# LOGIN
###########################
def login
res = @imap.login(meta.username, meta.password)
@logged_in = true if res.name == 'OK'
end
def logged_in?
!!@logged_in
end
# Log out of gmail
def logout
if logged_in?
res = @imap.logout
@logged_in = false if res.name == 'OK'
end
end
def in_mailbox(mailbox, &block)
if block_given?
mailbox_stack << mailbox
unless @selected == mailbox.name
imap.select(mailbox.name)
@selected = mailbox.name
end
value = block.arity == 1 ? block.call(mailbox) : block.call
mailbox_stack.pop
# Select previously selected mailbox if there is one
if mailbox_stack.last
imap.select(mailbox_stack.last.name)
@selected = mailbox.name
end
return value
else
mailboxes[mailbox] ||= Mailbox.new(self, mailbox)
end
end
alias :in_label :in_mailbox
###########################
# Other...
###########################
def inspect
"#<Gmail:#{'0x%x' % (object_id << 1)} (#{meta.username}) #{'dis' if !logged_in?}connected>"
end
# Accessor for @imap, but ensures that it's logged in first.
def imap
unless logged_in?
login
at_exit { logout } # Set up auto-logout for later.
end
@imap
end
private
def mailboxes
@mailboxes ||= {}
end
def mailbox_stack
@mailbox_stack ||= []
end
def meta
class << self; self end
end
def domain
meta.username.split('@')[0]
end
def smtp_settings
[:smtp, {:address => "smtp.gmail.com",
:port => 587,
:domain => domain,
:user_name => meta.username,
:password => meta.password,
:authentication => 'plain',
:enable_starttls_auto => true}]
end
end
require 'gmail/mailbox'
require 'gmail/message'