mailhandler 1.0.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 +7 -0
- data/.gitignore +5 -0
- data/lib/mailhandler.rb +91 -0
- data/lib/mailhandler/receiver.rb +86 -0
- data/lib/mailhandler/receiving/checker.rb +60 -0
- data/lib/mailhandler/receiving/checker_folder.rb +126 -0
- data/lib/mailhandler/receiving/checker_imap.rb +90 -0
- data/lib/mailhandler/receiving/filter.rb +161 -0
- data/lib/mailhandler/receiving/notification/console.rb +44 -0
- data/lib/mailhandler/receiving/notification/email.rb +74 -0
- data/lib/mailhandler/receiving/notification/email/content.rb +43 -0
- data/lib/mailhandler/receiving/notification/email/states.rb +113 -0
- data/lib/mailhandler/receiving/observer.rb +37 -0
- data/lib/mailhandler/sender.rb +56 -0
- data/lib/mailhandler/sending/sender.rb +13 -0
- data/lib/mailhandler/sending/sender_api.rb +45 -0
- data/lib/mailhandler/sending/sender_api_batch.rb +20 -0
- data/lib/mailhandler/sending/sender_smtp.rb +76 -0
- data/lib/mailhandler/version.rb +5 -0
- data/mailhandler.gemspec +28 -0
- data/readme.md +151 -0
- metadata +98 -0
@@ -0,0 +1,161 @@
|
|
1
|
+
module Filter
|
2
|
+
|
3
|
+
class Base
|
4
|
+
|
5
|
+
def Base.sort(files)
|
6
|
+
|
7
|
+
swapped = true
|
8
|
+
j = 0
|
9
|
+
|
10
|
+
while swapped do
|
11
|
+
|
12
|
+
swapped = false
|
13
|
+
j+=1
|
14
|
+
|
15
|
+
(files.size - j).times do |i|
|
16
|
+
|
17
|
+
if File.new(files[i]).ctime < File.new(files[i + 1]).ctime
|
18
|
+
tmp = files[i]
|
19
|
+
files[i] = files[i + 1]
|
20
|
+
files[i + 1] = tmp
|
21
|
+
swapped = true
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
files
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
def get(pattern)
|
34
|
+
|
35
|
+
files = []
|
36
|
+
Dir.glob(pattern).each { |file| files << File.absolute_path(file) }
|
37
|
+
files
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
class ByFilename < Base
|
44
|
+
|
45
|
+
def get(pattern)
|
46
|
+
|
47
|
+
super(pattern)
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
class ByContent < Base
|
54
|
+
|
55
|
+
def initialize(content)
|
56
|
+
|
57
|
+
@content = content
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
def get(pattern)
|
62
|
+
|
63
|
+
files = super(pattern)
|
64
|
+
|
65
|
+
matched_files = []
|
66
|
+
|
67
|
+
files.each do |file|
|
68
|
+
|
69
|
+
begin
|
70
|
+
|
71
|
+
content = File.read(file)
|
72
|
+
matched_files << file if content.include? @content
|
73
|
+
|
74
|
+
rescue
|
75
|
+
|
76
|
+
# skip file reading if file is not there anymore
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
matched_files
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
class ByDate < Base
|
89
|
+
|
90
|
+
def initialize(date)
|
91
|
+
|
92
|
+
@date = date
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
def get(pattern)
|
97
|
+
|
98
|
+
files = super(pattern)
|
99
|
+
files.select { |filename|
|
100
|
+
|
101
|
+
file = nil
|
102
|
+
begin
|
103
|
+
|
104
|
+
file = File.new(filename)
|
105
|
+
|
106
|
+
rescue
|
107
|
+
|
108
|
+
# skip file reading if file is not there anymore
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
file.ctime > @date if (file != nil)
|
113
|
+
|
114
|
+
}
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
module Email
|
121
|
+
|
122
|
+
class ByRecipient < Base
|
123
|
+
|
124
|
+
def initialize(recipient)
|
125
|
+
|
126
|
+
@recipient = recipient
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
def get(pattern)
|
131
|
+
|
132
|
+
files = super(pattern)
|
133
|
+
|
134
|
+
matched_files = []
|
135
|
+
|
136
|
+
files.each do |file|
|
137
|
+
|
138
|
+
begin
|
139
|
+
|
140
|
+
content = File.read(file)
|
141
|
+
|
142
|
+
email = Mail.read_from_string(content)
|
143
|
+
matched_files << file if email[@recipient.keys.first].to_s.include? @recipient.values.first
|
144
|
+
|
145
|
+
rescue
|
146
|
+
|
147
|
+
# skip file reading if file is not there anymore
|
148
|
+
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
matched_files
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module MailHandler
|
2
|
+
|
3
|
+
module Receiving
|
4
|
+
|
5
|
+
module Notification
|
6
|
+
|
7
|
+
class Console
|
8
|
+
|
9
|
+
def notify(search)
|
10
|
+
|
11
|
+
output_delay Time.now - search.started_at
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
module Seconds
|
18
|
+
|
19
|
+
TO_SHOW = 10
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
# print to screen delay length
|
24
|
+
def output_delay(delay)
|
25
|
+
|
26
|
+
delay_seconds = delay.to_i
|
27
|
+
output(delay_seconds) if [0,1].include? (delay_seconds % Seconds::TO_SHOW)
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
# print to screen delay length
|
32
|
+
def output(delay)
|
33
|
+
|
34
|
+
puts " email delay: #{'%03d' % delay} seconds"
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require_relative 'email/content'
|
2
|
+
require_relative 'email/states'
|
3
|
+
|
4
|
+
module MailHandler
|
5
|
+
|
6
|
+
module Receiving
|
7
|
+
|
8
|
+
module Notification
|
9
|
+
|
10
|
+
class Email
|
11
|
+
|
12
|
+
attr_reader :sender,
|
13
|
+
:contacts,
|
14
|
+
:min_time_to_notify,
|
15
|
+
:max_time_to_notify
|
16
|
+
|
17
|
+
def initialize(sender, contacts, min_time_to_notify = 60)
|
18
|
+
|
19
|
+
@min_time_to_notify = min_time_to_notify
|
20
|
+
|
21
|
+
@sender = sender
|
22
|
+
@contacts = contacts
|
23
|
+
init_state
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
def notify(search)
|
28
|
+
|
29
|
+
@max_time_to_notify = search.max_duration
|
30
|
+
init_state if Time.now - search.started_at < min_time_to_notify
|
31
|
+
@current_state.notify(search)
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
def set_state(state)
|
36
|
+
|
37
|
+
@current_state = state
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
def send_email(type, search)
|
42
|
+
|
43
|
+
verify_email_type(type)
|
44
|
+
content = EmailContent.send("email_#{type}",search.options, Time.now - search.started_at, sender.dispatcher.username, contacts)
|
45
|
+
sender.send_email content
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def init_state
|
52
|
+
|
53
|
+
@current_state = Notification::NoDelay.new(self)
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
EMAIL_TYPES = [:delayed, :received]
|
58
|
+
|
59
|
+
def verify_email_type(type)
|
60
|
+
|
61
|
+
raise StandardError, "Incorrect type: #{type}, allowed types: #{EMAIL_TYPES}" unless EMAIL_TYPES.include? type
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'mail'
|
2
|
+
|
3
|
+
module MailHandler
|
4
|
+
|
5
|
+
module Receiving
|
6
|
+
|
7
|
+
module Notification
|
8
|
+
|
9
|
+
class EmailContent
|
10
|
+
|
11
|
+
def self.email_received(options, delay, from, to)
|
12
|
+
|
13
|
+
Mail.new do
|
14
|
+
|
15
|
+
from from
|
16
|
+
subject "Received - delay was #{(delay.to_f/60).round(2)} minutes"
|
17
|
+
body "Received - delay was #{(delay.to_f/60).round(2)} minutes - search by #{options}"
|
18
|
+
to to
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.email_delayed(options, delay, from, to)
|
25
|
+
|
26
|
+
Mail.new do
|
27
|
+
|
28
|
+
from from
|
29
|
+
subject "Over #{(delay.to_f/60).round(2)} minutes delay"
|
30
|
+
body "Over #{(delay.to_f/60).round(2)} minutes delay - search by #{options}"
|
31
|
+
to to
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require_relative '../email'
|
2
|
+
|
3
|
+
module MailHandler
|
4
|
+
|
5
|
+
module Receiving
|
6
|
+
|
7
|
+
module Notification
|
8
|
+
|
9
|
+
class DelayState
|
10
|
+
|
11
|
+
attr_accessor :context,
|
12
|
+
:notified
|
13
|
+
|
14
|
+
def initialize(context)
|
15
|
+
|
16
|
+
@context = context
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
def notification_fired
|
21
|
+
|
22
|
+
@notified = true
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
def notify(search)
|
27
|
+
|
28
|
+
raise StandardError, 'notify(search) interface has to be implemented'
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
def send_notification_email(type, search)
|
35
|
+
|
36
|
+
unless notified
|
37
|
+
|
38
|
+
context.send_email(type, search)
|
39
|
+
notification_fired
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
class NoDelay < DelayState
|
48
|
+
|
49
|
+
def notify(search)
|
50
|
+
|
51
|
+
if Time.now - search.started_at >= context.min_time_to_notify
|
52
|
+
|
53
|
+
context.set_state(Delay.new(context))
|
54
|
+
context.notify(search)
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
class Delay < DelayState
|
63
|
+
|
64
|
+
def notify(search)
|
65
|
+
|
66
|
+
if search.result
|
67
|
+
|
68
|
+
context.set_state(Received.new(context))
|
69
|
+
context.notify(search)
|
70
|
+
|
71
|
+
elsif Time.now - search.started_at >= context.max_time_to_notify
|
72
|
+
|
73
|
+
context.set_state(MaxDelay.new(context))
|
74
|
+
context.notify(search)
|
75
|
+
|
76
|
+
else
|
77
|
+
|
78
|
+
send_notification_email(:delayed, search)
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
class MaxDelay < DelayState
|
87
|
+
|
88
|
+
def notify(search)
|
89
|
+
|
90
|
+
send_notification_email(:delayed, search)
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
class Received < DelayState
|
97
|
+
|
98
|
+
def notify(search)
|
99
|
+
|
100
|
+
send_notification_email(:received, search)
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module MailHandler
|
2
|
+
|
3
|
+
module Receiving
|
4
|
+
|
5
|
+
module Observer
|
6
|
+
|
7
|
+
def init_observer
|
8
|
+
|
9
|
+
@observers = Array.new
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_observer(observer)
|
14
|
+
|
15
|
+
@observers ||= Array.new
|
16
|
+
@observers << observer
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
def delete_observer(observer)
|
21
|
+
|
22
|
+
@observers.delete(observer) if @observers
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
def notify_observers(search)
|
27
|
+
|
28
|
+
@observers.each { |observer| observer.notify(search) } if @observers
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|