gloo-email 1.0 → 1.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 738fcacc7475ef8b8404861a76e59b6711fdd54a7423429d442f79bddf29316e
4
- data.tar.gz: d6cfda322d190c5ebede7d8d3d811e30b89b752dafeef32f526b2a61503aa362
3
+ metadata.gz: 5df7f906bcf10ea57f6483fdd78b300acf1ef5490478be8f86f1a096ea9eadf3
4
+ data.tar.gz: 8ae3eb79d025496bb017691222b417f3ada5bd991048cbf339cb856e0606e655
5
5
  SHA512:
6
- metadata.gz: cf5c545dad112ea64891fed3fd235a657c95b2a6dfa365aadf375279613089a9f1b78fb072145f2ee30a00debd8ea4a90e2d94fd32c773e05b4c18730fc1f2ee
7
- data.tar.gz: 8f46e2fd256810f41cb87fdb026e218062920b510bc5ee8c574589b3e273b7ffc1ab0a5f62a01086bfde0c72034e08912a787ede5446b7c270fc59324a59118e
6
+ metadata.gz: c4495eca105012199f7ae429d1bfdf17e307a7d9f5321e598a6083b729eb5f4e8e2df066df4f964426736eac437c7b2b522c48b0b3ddf22cb152faec3afa9d54
7
+ data.tar.gz: 79b2d878722f5acdc9d00aacc8ed07806e07023b63a3dde849e5a08583da1c392e554dc963f3513e8a6748599233f7edbc444f596510bf42ec009acbacf6b40d
data/lib/email_imap.rb CHANGED
@@ -103,45 +103,70 @@ class EmailImap < Gloo::Core::Obj
103
103
  end
104
104
 
105
105
  #
106
- # Fetch emails from the IMAP server.
106
+ # Fetch the unseen emails from the IMAP server into messages.
107
+ # It is true if it could fetch them (even if there were none),
108
+ # false otherwise.
107
109
  #
108
110
  def msg_fetch
109
- # Connect to the email server
110
- imap = Net::IMAP.new(server, port, true)
111
- imap.login(username, password)
112
- imap.select(mailbox)
111
+ mails = fetch_unseen
112
+ if mails
113
+ @engine.log.info "No new messages." if mails.empty?
114
+ mails.each { |mail| process_message( mail ) }
115
+ end
116
+
117
+ @engine.heap.it.set_to( mails ? true : false )
118
+ end
119
+
120
+ #
121
+ # Get the unseen messages from the server, as Mail messages.
122
+ # If it can't (an unreachable server, a refused login, no such
123
+ # mailbox), report a readable error and return nil. The connection
124
+ # is always closed.
125
+ #
126
+ def fetch_unseen
127
+ imap = Net::IMAP.new( server, port, true )
128
+ imap.login( username, password )
129
+ imap.select( mailbox )
113
130
 
114
131
  # Search for messages matching filter
115
- ids = imap.search([SEARCH])
116
-
117
- if ids.empty?
118
- puts "No new messages."
119
- else
120
- ids.each do |msg_id|
121
- raw_message = imap.fetch( msg_id, "RFC822" )[0].attr["RFC822"]
122
- mail = Mail.read_from_string( raw_message )
123
- process_message( mail )
124
- end
132
+ ids = imap.search( [ SEARCH ] )
133
+
134
+ return ids.map do |msg_id|
135
+ raw_message = imap.fetch( msg_id, "RFC822" )[0].attr["RFC822"]
136
+ Mail.read_from_string( raw_message )
125
137
  end
138
+ rescue => e
139
+ @engine.err "Could not fetch email from #{server}: #{e.message}"
140
+ return nil
141
+ ensure
142
+ close_imap( imap )
143
+ end
144
+
145
+ #
146
+ # Log out and disconnect, if connected.
147
+ #
148
+ def close_imap( imap )
149
+ return unless imap
126
150
 
127
- # -----------------------------
128
- # CLEAN UP
129
- # -----------------------------
130
151
  imap.logout
131
- imap.disconnect
152
+ imap.disconnect
153
+ rescue StandardError
154
+ # The connection already failed or was closed; nothing to do.
155
+ nil
132
156
  end
133
157
 
134
158
  #
135
159
  # Process a single email message
136
160
  #
137
161
  def process_message( mail )
138
- from = mail.from.join(', ')
139
- to = mail.to.join(', ')
162
+ # A message may have no From: or To: (eg. when we were Bcc'd).
163
+ from = Array( mail.from ).join(', ')
164
+ to = Array( mail.to ).join(', ')
140
165
  subject = mail.subject
141
166
  dt = mail.date
142
167
  body = mail.body.decoded
143
168
 
144
- msg_can = find_child MESSAGES
169
+ msg_can = find_child( MESSAGES ) || @engine.factory.create_can( MESSAGES, self )
145
170
 
146
171
  msg = msg_can.find_add_child( msg_can.children.length.to_s, 'email' )
147
172
  o = msg.find_add_child( 'from', 'string' )
@@ -178,7 +203,7 @@ class EmailImap < Gloo::Core::Obj
178
203
  'messages (container) — Populated by fetch: one child per unseen message found, each an email object with from/to/subject/date/body.'
179
204
  ],
180
205
  :messages => [
181
- 'fetch — Connect to the IMAP server, select the mailbox, and fetch all unseen messages, adding each as a child of messages. Logs out and disconnects when done.'
206
+ 'fetch — Connect to the IMAP server, select the mailbox, and fetch all unseen messages, adding each as a child of messages (created if missing). Logs out and disconnects when done. It is true if it could fetch (even if there were no new messages), false otherwise (eg. a refused login or an unreachable server, reported as an error).'
182
207
  ],
183
208
  :notes => 'No vault documentation exists for this object type — ' \
184
209
  'this was authored directly from the code.',
data/lib/email_msg.rb CHANGED
@@ -1,8 +1,12 @@
1
1
  #
2
2
  # An email message object
3
3
  #
4
+ require 'email_param'
5
+
4
6
  class EmailMsg < Gloo::Core::Obj
5
7
 
8
+ include EmailParam
9
+
6
10
  KEYWORD = 'email'.freeze
7
11
  SUBJECT = 'subject'.freeze
8
12
  BODY = 'body'.freeze
@@ -96,24 +100,13 @@ class EmailMsg < Gloo::Core::Obj
96
100
  end
97
101
 
98
102
  #
99
- # Send an email.
103
+ # Send this message with the email_smtp given as the parameter.
104
+ # It is true if it was sent, false otherwise.
100
105
  #
101
106
  def msg_send
102
- if @params&.token_count&.positive?
103
- smtp_pn = Gloo::Core::Pn.new( @engine, @params.tokens.first )
104
- unless smtp_pn&.exists?
105
- @engine.err 'SMTP server does not exist'
106
- return
107
- end
108
- else
109
- @engine.err 'Email Message is required'
110
- return
111
- end
112
- config = smtp_pn.resolve.get_config
113
- msg = get_msg
114
-
115
- smtp = Smtp.new( @engine, config )
116
- smtp.send msg
107
+ smtp = param_obj( EmailSmtp, 'email_smtp object',
108
+ "tell #{name} to send (my.smtp)" )
109
+ send_msg( smtp && get_msg, smtp&.get_config )
117
110
  end
118
111
 
119
112
  # ---------------------------------------------------------------------
@@ -136,7 +129,7 @@ class EmailMsg < Gloo::Core::Obj
136
129
  'body (text) — The email body.'
137
130
  ],
138
131
  :messages => [
139
- 'send ({smtp.path}) — Send this message using the given SMTP configuration object. A parameter is required: the path to an email_smtp object.'
132
+ 'send ({smtp.path}) — Send this message using the given SMTP configuration object. A parameter is required: the path to an email_smtp object. It is true if the message was sent, false otherwise (eg. a refused login or an unreachable server, reported as an error).'
140
133
  ],
141
134
  :notes => 'No vault documentation exists for this object type — ' \
142
135
  'this was authored directly from the code.',
@@ -0,0 +1,46 @@
1
+ #
2
+ # Shared by the send messages on email_smtp and email: each takes the
3
+ # path to the other object as its parameter.
4
+ #
5
+
6
+ module EmailParam
7
+
8
+ #
9
+ # The object named by the message's parameter, if it is of the given
10
+ # class. Otherwise report it (a syntax error if there's no parameter)
11
+ # and return nil.
12
+ # type - the class the object must be
13
+ # kind - how to name that kind of object in a message
14
+ # example - an example of the message, for the syntax error
15
+ #
16
+ def param_obj( type, kind, example )
17
+ unless @params&.token_count&.positive?
18
+ @engine.syntax_err "Missing the #{kind}! eg. #{example}"
19
+ return nil
20
+ end
21
+
22
+ pn = @params.first
23
+ obj = Gloo::Core::Pn.new( @engine, pn ).resolve
24
+ if obj.nil?
25
+ @engine.err Gloo::Core::NotFound.object( pn )
26
+ return nil
27
+ end
28
+
29
+ unless obj.is_a?( type )
30
+ @engine.err "'#{pn}' is not an #{kind}."
31
+ return nil
32
+ end
33
+
34
+ return obj
35
+ end
36
+
37
+ #
38
+ # Send the message with the SMTP configuration; it is true if it was
39
+ # sent, false otherwise (with the error reported).
40
+ #
41
+ def send_msg( msg, config )
42
+ sent = msg && config && Smtp.new( @engine, config ).deliver( msg )
43
+ @engine.heap.it.set_to( sent ? true : false )
44
+ end
45
+
46
+ end
data/lib/email_smtp.rb CHANGED
@@ -4,9 +4,12 @@
4
4
  require 'config'
5
5
  require 'msg'
6
6
  require 'smtp'
7
+ require 'email_param'
7
8
 
8
9
  class EmailSmtp < Gloo::Core::Obj
9
10
 
11
+ include EmailParam
12
+
10
13
  KEYWORD = 'email_smtp'.freeze
11
14
  SERVER = 'server'.freeze
12
15
  PORT = 'port'.freeze
@@ -90,23 +93,13 @@ class EmailSmtp < Gloo::Core::Obj
90
93
  end
91
94
 
92
95
  #
93
- # Send an email.
96
+ # Send the email message given as the parameter.
97
+ # It is true if it was sent, false otherwise.
94
98
  #
95
99
  def msg_send
96
- if @params&.token_count&.positive?
97
- msg_pn = Gloo::Core::Pn.new( @engine, @params.tokens.first )
98
- unless msg_pn&.exists?
99
- @engine.err 'Email Message does not exist'
100
- return
101
- end
102
- else
103
- @engine.err 'Email Message is required'
104
- return
105
- end
106
- msg = msg_pn.resolve.get_msg
107
-
108
- smtp = Smtp.new( @engine, get_config )
109
- smtp.send msg
100
+ email = param_obj( EmailMsg, 'email message',
101
+ "tell #{name} to send (my.email)" )
102
+ send_msg( email&.get_msg, email && get_config )
110
103
  end
111
104
 
112
105
  #
@@ -137,7 +130,7 @@ class EmailSmtp < Gloo::Core::Obj
137
130
  "password (string) — The user's password."
138
131
  ],
139
132
  :messages => [
140
- 'send ({message.path}) — Send the given email message object using this SMTP configuration. A parameter is required: the path to an email message object.'
133
+ 'send ({message.path}) — Send the given email message object using this SMTP configuration. A parameter is required: the path to an email message object. It is true if the message was sent, false otherwise (eg. a refused login or an unreachable server, reported as an error).'
141
134
  ],
142
135
  :notes => 'No vault documentation exists for this object type — ' \
143
136
  'this was authored directly from the code.',
data/lib/smtp.rb CHANGED
@@ -18,17 +18,19 @@ class Smtp
18
18
  end
19
19
 
20
20
  #
21
- # Send an email message.
21
+ # Deliver an email message. Returns true if it was sent; otherwise
22
+ # reports a readable error (no backtrace: a refused login, an
23
+ # unreachable server or a bad address are expected) and returns
24
+ # false.
22
25
  #
23
- def send msg
24
- begin
25
- configure
26
- mail = msg.get_mail
27
- mail.deliver!
28
- @engine.log.info "Email sent successfully!"
29
- rescue => ex
30
- @engine.log_exception ex
31
- end
26
+ def deliver msg
27
+ configure
28
+ msg.get_mail.deliver!
29
+ @engine.log.info "Email sent successfully!"
30
+ return true
31
+ rescue => ex
32
+ @engine.err "Could not send email: #{ex.message}"
33
+ return false
32
34
  end
33
35
 
34
36
  #
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gloo-email
3
3
  version: !ruby/object:Gem::Version
4
- version: '1.0'
4
+ version: '1.1'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Crane
@@ -23,6 +23,20 @@ dependencies:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
25
  version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: gloo
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '7.0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '7.0'
26
40
  - !ruby/object:Gem::Dependency
27
41
  name: bundler
28
42
  requirement: !ruby/object:Gem::Requirement
@@ -88,6 +102,7 @@ files:
88
102
  - lib/config.rb
89
103
  - lib/email_imap.rb
90
104
  - lib/email_msg.rb
105
+ - lib/email_param.rb
91
106
  - lib/email_smtp.rb
92
107
  - lib/gloo-email.rb
93
108
  - lib/msg.rb