@@ -11,7 +11,9 @@ module Agents |
||
| 11 | 11 |
description <<-MD |
| 12 | 12 |
|
| 13 | 13 |
The ImapFolderAgent checks an IMAP server in specified folders |
| 14 |
- and creates Events based on new unread mails. |
|
| 14 |
+ and creates Events based on new mails found since the last run. |
|
| 15 |
+ In the first visit to a foler, this agent only checks for the |
|
| 16 |
+ initial status and does not create events. |
|
| 15 | 17 |
|
| 16 | 18 |
Specify an IMAP server to connect with `host`, and set `ssl` to |
| 17 | 19 |
true if the server supports IMAP over SSL. Specify `port` if |
@@ -65,6 +67,13 @@ module Agents |
||
| 65 | 67 |
body. The default value is `['text/plain', 'text/enriched', |
| 66 | 68 |
'text/html']`. |
| 67 | 69 |
|
| 70 |
+ - "is_unread" |
|
| 71 |
+ |
|
| 72 |
+ Setting this to true or false means only mails that is |
|
| 73 |
+ marked as unread or read respectively, are selected. |
|
| 74 |
+ |
|
| 75 |
+ If this key is unspecified or set to null, it is ignored. |
|
| 76 |
+ |
|
| 68 | 77 |
- "has_attachment" |
| 69 | 78 |
|
| 70 | 79 |
Setting this to true or false means only mails that does or does |
@@ -77,11 +86,13 @@ module Agents |
||
| 77 | 86 |
Each agent instance memorizes the highest UID of mails that are |
| 78 | 87 |
found in the last run for each watched folder, so even if you |
| 79 | 88 |
change a set of conditions so that it matches mails that are |
| 80 |
- missed previously, or if you unmark already seen mails as read, |
|
| 81 |
- they will not show up as new events. Also, in order to avoid |
|
| 82 |
- duplicated notification it keeps a list of Message-Id's of 100 |
|
| 83 |
- most recent mails, so if multiple mails of the same Message-Id |
|
| 84 |
- are found, you will only see one event out of them. |
|
| 89 |
+ missed previously, or if you alter the flag status of already |
|
| 90 |
+ found mails, they will not show up as new events. |
|
| 91 |
+ |
|
| 92 |
+ Also, in order to avoid duplicated notification it keeps a list |
|
| 93 |
+ of Message-Id's of 100 most recent mails, so if multiple mails |
|
| 94 |
+ of the same Message-Id are found, you will only see one event |
|
| 95 |
+ out of them. |
|
| 85 | 96 |
MD |
| 86 | 97 |
|
| 87 | 98 |
event_description <<-MD |
@@ -200,7 +211,7 @@ module Agents |
||
| 200 | 211 |
errors.add(:base, 'conditions.%s contains a non-string object' % key) |
| 201 | 212 |
end |
| 202 | 213 |
} |
| 203 |
- when 'has_attachment' |
|
| 214 |
+ when 'is_unread', 'has_attachment' |
|
| 204 | 215 |
case boolify(value) |
| 205 | 216 |
when true, false |
| 206 | 217 |
else |
@@ -258,6 +269,8 @@ module Agents |
||
| 258 | 269 |
} |
| 259 | 270 |
when 'has_attachment' |
| 260 | 271 |
boolify(value) == mail.has_attachment? |
| 272 |
+ when 'is_unread' |
|
| 273 |
+ true # already filtered out by each_unread_mail |
|
| 261 | 274 |
else |
| 262 | 275 |
log 'Unknown condition key ignored: %s' % key |
| 263 | 276 |
true |
@@ -321,25 +334,47 @@ module Agents |
||
| 321 | 334 |
imap.select(folder) |
| 322 | 335 |
uidvalidity = imap.uidvalidity |
| 323 | 336 |
|
| 324 |
- if lastseenuid = lastseen[uidvalidity] |
|
| 325 |
- seen[uidvalidity] = lastseenuid |
|
| 326 |
- uids = imap.uid_fetch((lastseenuid + 1)..-1, 'FLAGS'). |
|
| 327 |
- each_with_object([]) { |data, ret|
|
|
| 328 |
- uid, flags = data.attr.values_at('UID', 'FLAGS')
|
|
| 329 |
- seen[uidvalidity] = uid |
|
| 330 |
- next if uid <= lastseenuid || flags.include?(:Seen) |
|
| 331 |
- ret << uid |
|
| 332 |
- } |
|
| 333 |
- else |
|
| 334 |
- uids = imap.uid_search('UNSEEN')
|
|
| 335 |
- seen[uidvalidity] = uids.max unless uids.empty? |
|
| 336 |
- end |
|
| 337 |
+ lastseenuid = lastseen[uidvalidity] |
|
| 338 |
+ |
|
| 339 |
+ if lastseenuid.nil? |
|
| 340 |
+ maxseq = imap.responses['EXISTS'].last |
|
| 341 |
+ |
|
| 342 |
+ log "Recording the initial status: %s" % pluralize(maxseq, 'existing mail') |
|
| 343 |
+ |
|
| 344 |
+ if maxseq > 0 |
|
| 345 |
+ seen[uidvalidity] = imap.fetch(maxseq, 'UID').last.attr['UID'] |
|
| 346 |
+ end |
|
| 337 | 347 |
|
| 338 |
- if uids.empty? |
|
| 339 |
- log "No unread mails" |
|
| 340 | 348 |
next |
| 341 | 349 |
end |
| 342 | 350 |
|
| 351 |
+ seen[uidvalidity] = lastseenuid |
|
| 352 |
+ is_unread = boolify(interpolated['conditions']['is_unread']) |
|
| 353 |
+ |
|
| 354 |
+ uids = imap.uid_fetch((lastseenuid + 1)..-1, 'FLAGS'). |
|
| 355 |
+ each_with_object([]) { |data, ret|
|
|
| 356 |
+ uid, flags = data.attr.values_at('UID', 'FLAGS')
|
|
| 357 |
+ seen[uidvalidity] = uid |
|
| 358 |
+ next if uid <= lastseenuid |
|
| 359 |
+ |
|
| 360 |
+ case is_unread |
|
| 361 |
+ when nil, !flags.include?(:Seen) |
|
| 362 |
+ ret << uid |
|
| 363 |
+ end |
|
| 364 |
+ } |
|
| 365 |
+ |
|
| 366 |
+ log pluralize(uids.size, |
|
| 367 |
+ case is_unread |
|
| 368 |
+ when true |
|
| 369 |
+ 'new unread mail' |
|
| 370 |
+ when false |
|
| 371 |
+ 'new read mail' |
|
| 372 |
+ else |
|
| 373 |
+ 'new mail' |
|
| 374 |
+ end) |
|
| 375 |
+ |
|
| 376 |
+ next if uids.empty? |
|
| 377 |
+ |
|
| 343 | 378 |
imap.uid_fetch_mails(uids).each { |mail|
|
| 344 | 379 |
yield mail, notified |
| 345 | 380 |
} |
@@ -391,6 +426,10 @@ module Agents |
||
| 391 | 426 |
File.fnmatch?(pattern, value, FNM_FLAGS) |
| 392 | 427 |
end |
| 393 | 428 |
|
| 429 |
+ def pluralize(count, noun) |
|
| 430 |
+ "%d %s" % [count, noun.pluralize(count)] |
|
| 431 |
+ end |
|
| 432 |
+ |
|
| 394 | 433 |
class Client < ::Net::IMAP |
| 395 | 434 |
class << self |
| 396 | 435 |
def open(host, port, ssl) |