tolliver 1.0.0 → 2.0.0
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 +4 -4
- data/README.md +21 -11
- data/app/models/tolliver/notification.rb +0 -1
- data/app/models/tolliver/notification_attachment.rb +16 -0
- data/app/models/tolliver/notification_delivery.rb +1 -3
- data/app/models/tolliver/notification_receiver.rb +0 -2
- data/app/models/tolliver/notification_template.rb +0 -1
- data/app/views/{notification_mailer → tolliver/notification_mailer}/notify.html.erb +0 -0
- data/app/views/tolliver/notification_mailer/notify.text.erb +1 -0
- data/config/locales/en.yml +1 -1
- data/db/migrate/20160121093817_create_notifications.rb +1 -12
- data/db/migrate/20160121094838_create_notification_receivers.rb +6 -4
- data/db/migrate/20160509144238_create_notification_templates.rb +4 -4
- data/db/migrate/20170630190600_create_notification_deliveries.rb +10 -3
- data/db/migrate/20201027150000_create_notification_attachments.rb +17 -0
- data/lib/tolliver.rb +44 -47
- data/lib/tolliver/errors/bad_request.rb +17 -0
- data/lib/tolliver/errors/not_found.rb +17 -0
- data/lib/tolliver/errors/standard_error.rb +17 -0
- data/lib/tolliver/mailers/notification_mailer.rb +10 -13
- data/lib/tolliver/models/notification.rb +11 -64
- data/lib/tolliver/models/notification_attachment.rb +35 -0
- data/lib/tolliver/models/notification_delivery.rb +32 -40
- data/lib/tolliver/models/notification_receiver.rb +3 -25
- data/lib/tolliver/models/notification_template.rb +6 -10
- data/lib/tolliver/services/delivery.rb +52 -8
- data/lib/tolliver/services/methods/email.rb +42 -0
- data/lib/tolliver/services/methods/sms.rb +51 -0
- data/lib/tolliver/services/methods/sms/plivo.rb +51 -0
- data/lib/tolliver/services/notification.rb +109 -178
- data/lib/tolliver/services/policies/batch.rb +90 -0
- data/lib/tolliver/services/policies/instantly.rb +50 -0
- metadata +20 -14
- data/app/views/notification_mailer/notify.text.erb +0 -1
- data/lib/tolliver/models/notification_delivery/batch.rb +0 -77
- data/lib/tolliver/models/notification_delivery/instantly.rb +0 -55
- data/lib/tolliver/models/notification_receiver/email.rb +0 -46
- data/lib/tolliver/models/notification_receiver/sms.rb +0 -45
- data/lib/tolliver/services/sms/plivo.rb +0 -49
@@ -0,0 +1,42 @@
|
|
1
|
+
# *****************************************************************************
|
2
|
+
# * Copyright (c) 2019 Matěj Outlý
|
3
|
+
# *****************************************************************************
|
4
|
+
# *
|
5
|
+
# * Notification method - E-mail
|
6
|
+
# *
|
7
|
+
# * Author: Matěj Outlý
|
8
|
+
# * Date : 21. 1. 2016
|
9
|
+
# *
|
10
|
+
# *****************************************************************************
|
11
|
+
|
12
|
+
module Tolliver
|
13
|
+
module Services
|
14
|
+
module Methods
|
15
|
+
class Email
|
16
|
+
|
17
|
+
def deliver(notification_receiver)
|
18
|
+
notification = notification_receiver.notification_delivery.notification
|
19
|
+
|
20
|
+
# Send email
|
21
|
+
begin
|
22
|
+
Tolliver::NotificationMailer.notify(notification, notification_receiver).deliver_now
|
23
|
+
notification_receiver.status = 'sent'
|
24
|
+
#rescue Net::SMTPFatalError, Net::SMTPSyntaxError
|
25
|
+
rescue StandardError => e
|
26
|
+
notification_receiver.status = 'error'
|
27
|
+
notification_receiver.error_message = e.message
|
28
|
+
end
|
29
|
+
|
30
|
+
# Mark as sent
|
31
|
+
notification_receiver.sent_at = Time.current
|
32
|
+
|
33
|
+
# Save
|
34
|
+
notification_receiver.save
|
35
|
+
|
36
|
+
true
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# *****************************************************************************
|
2
|
+
# * Copyright (c) 2019 Matěj Outlý
|
3
|
+
# *****************************************************************************
|
4
|
+
# *
|
5
|
+
# * Notification receiver
|
6
|
+
# *
|
7
|
+
# * Author: Matěj Outlý
|
8
|
+
# * Date : 21. 1. 2016
|
9
|
+
# *
|
10
|
+
# *****************************************************************************
|
11
|
+
|
12
|
+
module Tolliver
|
13
|
+
module Services
|
14
|
+
module Methods
|
15
|
+
class Sms
|
16
|
+
|
17
|
+
def deliver(notification_receiver)
|
18
|
+
notification = notification_receiver.notification_delivery.notification
|
19
|
+
|
20
|
+
# Send SMS
|
21
|
+
begin
|
22
|
+
provider.deliver(notification_receiver.receiver_contact, notification.message.strip_tags)
|
23
|
+
notification_receiver.status = 'sent'
|
24
|
+
rescue StandardError => e
|
25
|
+
notification_receiver.status = 'error'
|
26
|
+
notification_receiver.error_message = e.message
|
27
|
+
end
|
28
|
+
|
29
|
+
# Mark as sent
|
30
|
+
notification_receiver.sent_at = Time.current
|
31
|
+
|
32
|
+
# Save
|
33
|
+
notification_receiver.save
|
34
|
+
|
35
|
+
true
|
36
|
+
end
|
37
|
+
|
38
|
+
protected
|
39
|
+
|
40
|
+
def provider
|
41
|
+
if @provider.nil?
|
42
|
+
provider_class_name = "Tolliver::Services::Methods::Sms::#{Tolliver.sms_provider.to_s.camelize}"
|
43
|
+
@provider = provider_class_name.constantize.new(Tolliver.sms_provider_params)
|
44
|
+
end
|
45
|
+
@provider
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# *****************************************************************************
|
2
|
+
# * Copyright (c) 2019 Matěj Outlý
|
3
|
+
# *****************************************************************************
|
4
|
+
# *
|
5
|
+
# * Plivo SMS provider
|
6
|
+
# *
|
7
|
+
# * Author: Matěj Outlý
|
8
|
+
# * Date : 1. 12. 2017
|
9
|
+
# *
|
10
|
+
# *****************************************************************************
|
11
|
+
|
12
|
+
require 'plivo'
|
13
|
+
|
14
|
+
module Tolliver
|
15
|
+
module Services
|
16
|
+
module Methods
|
17
|
+
module Sms
|
18
|
+
class Plivo
|
19
|
+
|
20
|
+
def initialize(params = {})
|
21
|
+
if params[:auth_id].blank? || params[:auth_token].blank?
|
22
|
+
raise Tolliver::Errors::StandardError.new('Please provide Auth ID and Auth Token in provider params.')
|
23
|
+
end
|
24
|
+
@auth_id = params[:auth_id]
|
25
|
+
@auth_token = params[:auth_token]
|
26
|
+
@api = ::Plivo::RestAPI.new(@auth_id, @auth_token)
|
27
|
+
end
|
28
|
+
|
29
|
+
def deliver(receiver, message)
|
30
|
+
|
31
|
+
# Check message length.
|
32
|
+
if message.bytesize > 200
|
33
|
+
raise 'Message too long.'
|
34
|
+
end
|
35
|
+
|
36
|
+
# Request API
|
37
|
+
response = @api.send_message({
|
38
|
+
'src' => Tolliver.sms_sender, # TODO: This should be improved to take sender from number pool and remember number / message mapping
|
39
|
+
'dst' => receiver.to_s,
|
40
|
+
'text' => message.to_s,
|
41
|
+
'method' => 'POST'
|
42
|
+
})
|
43
|
+
|
44
|
+
true
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -9,226 +9,157 @@
|
|
9
9
|
# *
|
10
10
|
# *****************************************************************************
|
11
11
|
|
12
|
+
require 'singleton'
|
13
|
+
|
12
14
|
module Tolliver
|
13
15
|
module Services
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
module ClassMethods
|
16
|
+
class Notification
|
17
|
+
include Singleton
|
18
18
|
|
19
|
-
|
19
|
+
def notify(options)
|
20
|
+
options = options.deep_symbolize_keys
|
20
21
|
|
21
|
-
|
22
|
-
|
22
|
+
# Object
|
23
|
+
notification = Tolliver.notification_model.new
|
23
24
|
|
24
|
-
|
25
|
+
Tolliver.notification_model.transaction do
|
25
26
|
|
26
|
-
|
27
|
-
|
27
|
+
# Get notification template object
|
28
|
+
template = options[:template]
|
29
|
+
raise Tolliver::Errors::BadRequest.new('Missing template.') if template.blank?
|
30
|
+
notification_template = Tolliver.notification_template_model.where(ref: template).first
|
31
|
+
notification_template = Tolliver.notification_template_model.where(id: template).first if notification_template.nil?
|
32
|
+
raise Tolliver::Errors::NotFound.new("Template #{template.to_s} not found.") if notification_template.nil?
|
28
33
|
|
29
|
-
|
34
|
+
if !notification_template.is_disabled && !notification_template.message.blank?
|
30
35
|
|
31
|
-
|
32
|
-
|
33
|
-
|
36
|
+
# Interpret params and store it in DB
|
37
|
+
params = map_params(options[:params] || {})
|
38
|
+
notification.message = interpret_named_params(notification_template.message, params)
|
39
|
+
notification.subject = interpret_named_params(notification_template.subject, params)
|
34
40
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
# Kind
|
39
|
-
if options[:kind]
|
40
|
-
notification.kind = options[:kind]
|
41
|
-
end
|
41
|
+
# Notification template
|
42
|
+
notification.notification_template = notification_template
|
42
43
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
44
|
+
# Signature
|
45
|
+
unless options[:signature].blank?
|
46
|
+
notification.message += options[:signature].to_s
|
47
|
+
end
|
47
48
|
|
48
|
-
|
49
|
-
|
50
|
-
|
49
|
+
# Save to DB
|
50
|
+
notification.save
|
51
|
+
|
52
|
+
# Attachments
|
53
|
+
unless options[:attachments].blank?
|
54
|
+
attachments = options[:attachments]
|
55
|
+
attachments = [attachments] unless attachments.is_a?(Array)
|
56
|
+
attachments.each do |attachment|
|
57
|
+
raise Tolliver::Errors::BadRequest.new('Missing attachment name.') if attachment[:name].blank?
|
58
|
+
raise Tolliver::Errors::BadRequest.new('Missing attachment data.') if attachment[:attachment].blank?
|
59
|
+
notification.notification_attachments.create(name: attachment[:name], attachment: attachment[:attachment])
|
51
60
|
end
|
61
|
+
end
|
52
62
|
|
53
|
-
|
54
|
-
if options[:attachment]
|
55
|
-
notification.attachment = options[:attachment]
|
56
|
-
end
|
63
|
+
unless notification_template.is_dry
|
57
64
|
|
58
|
-
#
|
59
|
-
if
|
60
|
-
|
65
|
+
# Delivery methods
|
66
|
+
if options[:methods].blank?
|
67
|
+
methods = Tolliver.delivery_methods
|
68
|
+
else
|
69
|
+
# Select only wanted delivery methods, but valid according to module config
|
70
|
+
methods = options[:methods]
|
71
|
+
methods = [methods] unless methods.is_a?(Array)
|
72
|
+
methods = methods.delete_if { |method| !Tolliver.delivery_methods.include?(method.to_sym) }
|
61
73
|
end
|
62
74
|
|
63
|
-
|
64
|
-
notification.save
|
75
|
+
methods.each do |method|
|
65
76
|
|
66
|
-
|
77
|
+
# Delivery object
|
78
|
+
notification_delivery = notification.notification_deliveries.create(method: method)
|
67
79
|
|
68
|
-
#
|
69
|
-
if options[:
|
70
|
-
|
71
|
-
delivery_kinds = delivery_kinds.delete_if { |delivery_kind| !Tolliver.delivery_kinds.include?(delivery_kind.to_sym) }
|
72
|
-
else
|
73
|
-
delivery_kinds = Tolliver.delivery_kinds
|
80
|
+
# Is postponed
|
81
|
+
if options[:is_postponed]
|
82
|
+
notification_delivery.is_postponed = options[:is_postponed]
|
74
83
|
end
|
75
84
|
|
76
|
-
#
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
# Filter out receivers not valid for this delivery kind
|
85
|
-
if delivery_kind == :email
|
86
|
-
filtered_receivers = receivers.delete_if { |receiver| !receiver.respond_to?(:email) }
|
87
|
-
elsif delivery_kind == :sms
|
88
|
-
filtered_receivers = receivers.delete_if { |receiver| !receiver.respond_to?(:phone) }
|
89
|
-
else
|
90
|
-
filtered_receivers = receivers.dup
|
91
|
-
end
|
92
|
-
|
93
|
-
# Save to DB
|
94
|
-
filtered_receivers.each do |receiver|
|
95
|
-
notification_delivery.notification_receivers.create(receiver: receiver)
|
96
|
-
end
|
97
|
-
notification_delivery.sent_count = 0
|
98
|
-
notification_delivery.receivers_count = filtered_receivers.size
|
99
|
-
notification_delivery.save
|
85
|
+
# Sender
|
86
|
+
unless options[:sender].blank?
|
87
|
+
raise Tolliver::Errors::BadRequest.new('Missing sender ref.') if options[:sender][:ref].blank?
|
88
|
+
raise Tolliver::Errors::BadRequest.new('Missing sender contact.') if options[:sender][:contact].blank?
|
89
|
+
notification_delivery.sender_ref = options[:sender][:ref]
|
90
|
+
notification_delivery.sender_contact = options[:sender][:contact]
|
91
|
+
end
|
100
92
|
|
93
|
+
# Receivers
|
94
|
+
receivers = options[:receivers] || []
|
95
|
+
raise Tolliver::Errors::BadRequest.new('Missing receivers.') if receivers.blank? || receivers.empty?
|
96
|
+
receivers = [receivers] unless receivers.is_a?(Array)
|
97
|
+
filtered_receivers = receivers.dup # TODO contact validation based on method and filter
|
98
|
+
|
99
|
+
# Save to DB
|
100
|
+
filtered_receivers.each do |receiver|
|
101
|
+
raise Tolliver::Errors::BadRequest.new('Missing sender ref.') if receiver[:ref].blank?
|
102
|
+
raise Tolliver::Errors::BadRequest.new('Missing sender contact.') if receiver[:contact].blank?
|
103
|
+
notification_delivery.notification_receivers.create(receiver_ref: receiver[:ref], receiver_contact: receiver[:contact])
|
101
104
|
end
|
105
|
+
notification_delivery.sent_count = 0
|
106
|
+
notification_delivery.receivers_count = filtered_receivers.size
|
107
|
+
notification_delivery.save
|
102
108
|
|
103
109
|
end
|
104
110
|
|
105
|
-
else
|
106
|
-
notification = nil # Do not create notification with empty message
|
107
111
|
end
|
108
112
|
|
113
|
+
else
|
114
|
+
notification = nil # Do not create notification with empty message
|
109
115
|
end
|
110
116
|
|
111
|
-
# Enqueue for delivery
|
112
|
-
notification.enqueue_for_delivery if !notification.nil?
|
113
|
-
|
114
|
-
return notification
|
115
117
|
end
|
116
118
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
content = [content]
|
122
|
-
end
|
123
|
-
|
124
|
-
# Check for empty
|
125
|
-
if content.length == 0
|
126
|
-
raise "Notification is incorrectly defined."
|
127
|
-
end
|
128
|
-
|
129
|
-
# Extract content definition and params
|
130
|
-
content_def = content.shift
|
131
|
-
|
132
|
-
# Preset
|
133
|
-
params = content
|
134
|
-
subject = nil
|
135
|
-
message = nil
|
136
|
-
notification_template = nil
|
137
|
-
|
138
|
-
if content_def.is_a?(Symbol)
|
139
|
-
|
140
|
-
notification_template = Tolliver.notification_template_model.where(ref: content_def.to_s).first
|
141
|
-
if notification_template.nil?
|
142
|
-
raise "Notification template #{content_def.to_s} not found."
|
143
|
-
end
|
144
|
-
if notification_template.disabled != true
|
145
|
-
message = notification_template.message
|
146
|
-
subject = notification_template.subject
|
147
|
-
end
|
148
|
-
|
149
|
-
elsif content_def.is_a?(Hash) # Defined by hash containing subject and message
|
150
|
-
|
151
|
-
subject = content_def[:subject]
|
152
|
-
message = content_def[:message]
|
153
|
-
|
154
|
-
elsif content_def.is_a?(String)
|
155
|
-
|
156
|
-
message = content_def
|
119
|
+
# Enqueue for delivery
|
120
|
+
if !notification.nil? && !options[:is_postponed]
|
121
|
+
notification.enqueue_for_delivery
|
122
|
+
end
|
157
123
|
|
158
|
-
|
159
|
-
|
160
|
-
end
|
124
|
+
notification
|
125
|
+
end
|
161
126
|
|
162
|
-
|
163
|
-
params.unshift(message)
|
127
|
+
protected
|
164
128
|
|
165
|
-
|
129
|
+
def map_params(params)
|
130
|
+
result = {}
|
131
|
+
params.each do |param|
|
132
|
+
raise Tolliver::Errors::BadRequest.new('Missing param key.') if param[:key].blank?
|
133
|
+
raise Tolliver::Errors::BadRequest.new('Missing param value.') if param[:value].nil?
|
134
|
+
result[param[:key].to_s] = param[:value]
|
166
135
|
end
|
136
|
+
result
|
137
|
+
end
|
167
138
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
if
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
# Automatic receivers defined by string
|
176
|
-
new_receivers = []
|
177
|
-
receivers.each do |receiver|
|
178
|
-
if receiver.is_a?(String) || receiver.is_a?(Symbol)
|
179
|
-
if Tolliver.people_selector_model && Tolliver.people_selector_model.respond_to?(:decode_value) && Tolliver.people_selector_model.respond_to?(:people)
|
180
|
-
ref, params = Tolliver.people_selector_model.decode_value(receiver.to_s)
|
181
|
-
new_receivers.concat(Tolliver.people_selector_model.people(ref, params).to_a) # Use people selector to generate receivers
|
182
|
-
end
|
183
|
-
else
|
184
|
-
new_receivers << receiver
|
185
|
-
end
|
186
|
-
end
|
187
|
-
receivers = new_receivers
|
188
|
-
|
189
|
-
# Receivers responding to email or users
|
190
|
-
new_receivers = []
|
191
|
-
receivers.each do |receiver|
|
192
|
-
if receiver.respond_to?(:email)
|
193
|
-
new_receivers << receiver
|
194
|
-
else
|
195
|
-
if receiver.respond_to?(:user)
|
196
|
-
new_receivers << receiver.user
|
197
|
-
else
|
198
|
-
if receiver.respond_to?(:users)
|
199
|
-
new_receivers.concat(receiver.users.to_a)
|
200
|
-
end
|
201
|
-
end
|
202
|
-
end
|
139
|
+
def interpret_named_params(text, params)
|
140
|
+
text.gsub(/%{[^{}]+}/) do |match|
|
141
|
+
key = match[2..-2].to_s.strip
|
142
|
+
if params.has_key?(key)
|
143
|
+
params[key] # return param value if key found
|
144
|
+
else
|
145
|
+
match # return entire match if key not found
|
203
146
|
end
|
204
|
-
receivers = new_receivers
|
205
|
-
|
206
|
-
return receivers
|
207
147
|
end
|
148
|
+
end
|
208
149
|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
template_to_eval = match[2..-2].gsub(/%([0-9]+)/, "params[\\1]")
|
217
|
-
|
218
|
-
# Evaluate match
|
219
|
-
begin
|
220
|
-
evaluated_match = eval(template_to_eval)
|
221
|
-
rescue
|
222
|
-
evaluated_match = ""
|
223
|
-
end
|
224
|
-
|
225
|
-
# Result
|
226
|
-
evaluated_match
|
150
|
+
def interpret_positional_params(text, params)
|
151
|
+
text.gsub(/%{[^{}]+}/) do |match|
|
152
|
+
template_to_eval = match[2..-2].gsub(/%([0-9]+)/, "params[\\1]") # substitute all %1, %2, %3, ... to a form which can be evaluated
|
153
|
+
begin
|
154
|
+
evaluated_match = eval(template_to_eval) # evaluate match against params
|
155
|
+
rescue
|
156
|
+
evaluated_match = ""
|
227
157
|
end
|
158
|
+
evaluated_match
|
228
159
|
end
|
229
|
-
|
230
160
|
end
|
231
161
|
|
232
162
|
end
|
163
|
+
|
233
164
|
end
|
234
|
-
end
|
165
|
+
end
|