nuntius 0.1.1 → 1.0.27

Sign up to get free protection for your applications and to get access to all the features.
Files changed (169) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +199 -63
  4. data/Rakefile +62 -1
  5. data/app/controllers/nuntius/admin/campaigns_controller.rb +56 -0
  6. data/app/controllers/nuntius/admin/layouts/attachments_controller.rb +32 -0
  7. data/app/controllers/nuntius/admin/layouts_controller.rb +50 -0
  8. data/app/controllers/nuntius/admin/lists/subscribers_controller.rb +50 -0
  9. data/app/controllers/nuntius/admin/lists_controller.rb +46 -0
  10. data/app/controllers/nuntius/admin/locales_controller.rb +46 -0
  11. data/app/controllers/nuntius/admin/messages_controller.rb +24 -0
  12. data/app/controllers/nuntius/admin/templates_controller.rb +56 -0
  13. data/app/controllers/nuntius/api/events_controller.rb +16 -0
  14. data/app/controllers/nuntius/application_admin_controller.rb +11 -0
  15. data/app/controllers/nuntius/application_controller.rb +12 -0
  16. data/app/controllers/nuntius/callbacks_controller.rb +21 -0
  17. data/app/controllers/nuntius/dashboard_controller.rb +12 -0
  18. data/app/controllers/nuntius/feedback_controller.rb +17 -0
  19. data/app/controllers/nuntius/inbound_messages/twilio_inbound_smses_controller.rb +56 -0
  20. data/app/controllers/nuntius/messages_controller.rb +13 -0
  21. data/app/drops/nuntius/application_drop.rb +17 -0
  22. data/app/drops/nuntius/campaign_drop.rb +7 -0
  23. data/app/drops/nuntius/layout_drop.rb +11 -0
  24. data/app/drops/nuntius/message_drop.rb +11 -0
  25. data/app/drops/nuntius/template_drop.rb +7 -0
  26. data/app/exceptions/nuntius/base_exception.rb +4 -0
  27. data/app/exceptions/nuntius/missing_messenger_exception.rb +4 -0
  28. data/app/helpers/nuntius/application_helper.rb +78 -0
  29. data/app/jobs/nuntius/application_job.rb +9 -0
  30. data/app/jobs/nuntius/deliver_inbound_message_job.rb +10 -0
  31. data/app/jobs/nuntius/messenger_job.rb +17 -0
  32. data/app/jobs/nuntius/process_inbound_message_job.rb +10 -0
  33. data/app/jobs/nuntius/purge_message_job.rb +13 -0
  34. data/app/jobs/nuntius/retrieve_mail_job.rb +18 -0
  35. data/app/jobs/nuntius/transport_delivery_job.rb +32 -0
  36. data/app/jobs/nuntius/transport_refresh_job.rb +22 -0
  37. data/app/message_boxes/nuntius/base_message_box.rb +75 -0
  38. data/app/messengers/nuntius/base_messenger.rb +193 -0
  39. data/app/messengers/nuntius/custom_messenger.rb +6 -0
  40. data/app/models/nuntius/application_record.rb +11 -0
  41. data/app/models/nuntius/attachment.rb +13 -0
  42. data/app/models/nuntius/campaign.rb +75 -0
  43. data/app/models/nuntius/concerns/metadata_scoped.rb +31 -0
  44. data/app/models/nuntius/concerns/yamlify.rb +23 -0
  45. data/app/models/nuntius/inbound_message.rb +14 -0
  46. data/app/models/nuntius/layout.rb +13 -0
  47. data/app/models/nuntius/list.rb +10 -0
  48. data/app/models/nuntius/locale.rb +11 -0
  49. data/app/models/nuntius/message.rb +148 -0
  50. data/app/models/nuntius/subscriber.rb +12 -0
  51. data/app/models/nuntius/template.rb +102 -0
  52. data/app/presenters/application_presenter.rb +28 -0
  53. data/app/presenters/template_presenter.rb +17 -0
  54. data/app/providers/nuntius/apnotic_push_provider.rb +44 -0
  55. data/app/providers/nuntius/base_provider.rb +72 -0
  56. data/app/providers/nuntius/firebase_push_provider.rb +26 -0
  57. data/app/providers/nuntius/houston_push_provider.rb +40 -0
  58. data/app/providers/nuntius/message_bird_sms_provider.rb +39 -0
  59. data/app/providers/nuntius/slack_slack_provider.rb +36 -0
  60. data/app/providers/nuntius/smtp_mail_provider.rb +80 -0
  61. data/app/providers/nuntius/twilio_sms_provider.rb +37 -0
  62. data/app/providers/nuntius/twilio_voice_provider.rb +70 -0
  63. data/app/reportlets/nuntius/application_reportlet.rb +35 -0
  64. data/app/reportlets/nuntius/daily_messages_per_template_reportlet.rb +50 -0
  65. data/app/runners/nuntius/application_runner.rb +6 -0
  66. data/app/runners/nuntius/basic_application_runner.rb +15 -0
  67. data/app/runners/nuntius/timebased_events_runner.rb +15 -0
  68. data/app/services/nuntius/application_service.rb +7 -0
  69. data/app/services/nuntius/aws_sns_processor_service.rb +81 -0
  70. data/app/services/nuntius/deliver_inbound_message_service.rb +14 -0
  71. data/app/services/nuntius/retrieve_inbound_mail_service.rb +36 -0
  72. data/app/tables/nuntius_campaigns_table.rb +21 -0
  73. data/app/tables/nuntius_layouts_table.rb +24 -0
  74. data/app/tables/nuntius_lists_table.rb +19 -0
  75. data/app/tables/nuntius_locales_table.rb +18 -0
  76. data/app/tables/nuntius_messages_table.rb +36 -0
  77. data/app/tables/nuntius_subscribers_table.rb +19 -0
  78. data/app/tables/nuntius_templates_table.rb +30 -0
  79. data/app/transports/nuntius/base_transport.rb +36 -0
  80. data/app/transports/nuntius/mail_transport.rb +35 -0
  81. data/app/transports/nuntius/push_transport.rb +15 -0
  82. data/app/transports/nuntius/slack_transport.rb +6 -0
  83. data/app/transports/nuntius/sms_transport.rb +6 -0
  84. data/app/transports/nuntius/voice_transport.rb +6 -0
  85. data/app/validators/liquid_validator.rb +11 -0
  86. data/app/views/nuntius/admin/campaigns/edit.html.slim +49 -0
  87. data/app/views/nuntius/admin/campaigns/index.html.slim +2 -0
  88. data/app/views/nuntius/admin/layouts/attachments/_attachments.html.slim +28 -0
  89. data/app/views/nuntius/admin/layouts/attachments/_index.html.slim +2 -0
  90. data/app/views/nuntius/admin/layouts/attachments/create.json.jbuilder +5 -0
  91. data/app/views/nuntius/admin/layouts/edit.html.slim +24 -0
  92. data/app/views/nuntius/admin/layouts/index.html.slim +2 -0
  93. data/app/views/nuntius/admin/lists/edit.html.slim +14 -0
  94. data/app/views/nuntius/admin/lists/index.html.slim +2 -0
  95. data/app/views/nuntius/admin/lists/subscribers/edit.html.slim +14 -0
  96. data/app/views/nuntius/admin/locales/edit.html.slim +14 -0
  97. data/app/views/nuntius/admin/locales/index.html.slim +2 -0
  98. data/app/views/nuntius/admin/messages/index.html.slim +2 -0
  99. data/app/views/nuntius/admin/messages/show.html.slim +56 -0
  100. data/app/views/nuntius/admin/templates/edit.html.slim +81 -0
  101. data/app/views/nuntius/admin/templates/index.html.slim +2 -0
  102. data/app/views/nuntius/dashboard/show.html.slim +7 -0
  103. data/app/views/nuntius/messages/show.html.slim +7 -0
  104. data/config/locales/en.yml +22 -0
  105. data/config/locales/nl.yml +22 -0
  106. data/config/routes.rb +38 -0
  107. data/config/webpack/development.js +5 -0
  108. data/config/webpack/environment.js +4 -0
  109. data/config/webpack/production.js +5 -0
  110. data/config/webpack/test.js +5 -0
  111. data/config/webpacker.yml +119 -0
  112. data/db/migrate/20190301201541_create_nuntius_templates.rb +27 -0
  113. data/db/migrate/20190301202436_create_nuntius_messages.rb +30 -0
  114. data/db/migrate/20190322112815_create_nuntius_lists.rb +12 -0
  115. data/db/migrate/20190322114340_create_nuntius_campaigns.rb +18 -0
  116. data/db/migrate/20190322121338_create_nuntius_subscribers.rb +17 -0
  117. data/db/migrate/20190327123535_add_metadata_to_models.rb +8 -0
  118. data/db/migrate/20190327124407_create_nuntius_layouts.rb +24 -0
  119. data/db/migrate/20190327143921_add_campaign_to_message.rb +7 -0
  120. data/db/migrate/20190327155112_add_state_to_campaign.rb +7 -0
  121. data/db/migrate/20190412103335_add_enabled_to_templates.rb +7 -0
  122. data/db/migrate/20190417125153_change_message_status_default.rb +9 -0
  123. data/db/migrate/20190417144554_add_nuntiable_to_subscriber.rb +7 -0
  124. data/db/migrate/20190521135011_add_payload_to_nuntius_messages.rb +7 -0
  125. data/db/migrate/20190522122657_add_metadata_to_nuntius_message.rb +7 -0
  126. data/db/migrate/20190825080757_update_nuntius_message_sending_to_sent.rb +11 -0
  127. data/db/migrate/20200220154927_change_nuntius_templates_payload_type.rb +7 -0
  128. data/db/migrate/20200224132337_create_nuntius_locales.rb +13 -0
  129. data/db/migrate/20200318095339_create_nuntius_attachments.rb +12 -0
  130. data/db/migrate/20200407050646_add_interval_to_nuntius_template.rb +5 -0
  131. data/db/migrate/20200430131219_make_html_processing_optional.rb +6 -0
  132. data/db/migrate/20200430154032_make_html_processing_optional_revert.rb +6 -0
  133. data/db/migrate/20201121185718_create_nuntius_inbound_messages.rb +21 -0
  134. data/db/migrate/20220412114148_add_last_sent_to_message.rb +5 -0
  135. data/lib/nuntius/active_record_helpers.rb +28 -0
  136. data/lib/nuntius/active_storage_helpers.rb +8 -0
  137. data/lib/nuntius/configuration.rb +97 -0
  138. data/lib/nuntius/deprecator.rb +8 -0
  139. data/lib/nuntius/devise.rb +19 -0
  140. data/lib/nuntius/engine.rb +46 -0
  141. data/lib/nuntius/i18n_store.rb +100 -0
  142. data/lib/nuntius/liquid/tags/attach_tag.rb +44 -0
  143. data/lib/nuntius/mail_allow_list.rb +23 -0
  144. data/lib/nuntius/nuntiable.rb +24 -0
  145. data/lib/nuntius/state_machine.rb +19 -0
  146. data/lib/nuntius/transactio.rb +30 -0
  147. data/lib/nuntius/version.rb +3 -1
  148. data/lib/nuntius.rb +74 -8
  149. data/lib/preamble.rb +87 -0
  150. data/lib/tasks/nuntius_tasks.rake +27 -0
  151. metadata +532 -67
  152. data/.gitignore +0 -4
  153. data/Gemfile +0 -4
  154. data/lib/nuntius/encodings/url_safe_base64.rb +0 -26
  155. data/lib/nuntius/encodings.rb +0 -9
  156. data/lib/nuntius/envelope.rb +0 -45
  157. data/lib/nuntius/key.rb +0 -47
  158. data/lib/nuntius/messenger.rb +0 -37
  159. data/nuntius.gemspec +0 -23
  160. data/spec/keys/alice.pem +0 -27
  161. data/spec/keys/alice.pub +0 -9
  162. data/spec/keys/bob.pem +0 -27
  163. data/spec/keys/bob.pub +0 -9
  164. data/spec/nuntius/encodings/url_safe_base64_spec.rb +0 -36
  165. data/spec/nuntius/envelope_spec.rb +0 -6
  166. data/spec/nuntius/key_spec.rb +0 -12
  167. data/spec/nuntius/messenger_spec.rb +0 -26
  168. data/spec/spec_helper.rb +0 -9
  169. data/spec/support/keys.rb +0 -3
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ ###
4
+ # Process feedback from the amazon mail delivery service. Feedback looks like this:
5
+ # {"Type"=>"Notification", "MessageId"=>"a00966b6-51a5-53f7-a966-35d0e8f878ce", "TopicArn"=>"arn:aws:sns:eu-west-1:218893591873:localexpress-email-feedback",
6
+ # "Message"=>"{\"notificationType\":\"Delivery\",\"mail\":{\"timestamp\":\"2018-05-04T19:40:12.637Z\",\"source\":\"do_not_reply@localexpress.nl\",\
7
+ # "sourceArn\":\"arn:aws:ses:eu-west-1:218893591873:identity/localexpress.nl\",\"sourceIp\":\"52.59.100.253\",\"sendingAccountId\":\"218893591873\",
8
+ # \"messageId\":\"010201632cab47dd-924f80f8-4ae2-4ba0-9c8b-050db003711a-000000\",\"destination\":[\"mercedes.tuin@gmail.com\"],
9
+ # \"headersTruncated\":false,\"headers\":[{\"name\":\"Received\",\"value\":\"from localhost.localdomain (ec2-52-59-100-253.eu-central-1.compute.amazonaws.com [52.59.100.253]) by email-smtp.amazonaws.com with SMTP (SimpleEmailService-2762311919) id 4AtiQr5QXc6CZ3uJYW4D for mercedes.tuin@gmail.com; Fri, 04 May 2018 19:40:12 +0000 (UTC)\"},{\"nam
10
+ #
11
+ #
12
+
13
+ # See: https://docs.aws.amazon.com/ses/latest/dg/notification-examples.html
14
+ module Nuntius
15
+ class AwsSnsProcessorService < ApplicationService
16
+ context do
17
+ attribute :notification, typecaster: lambda { |value|
18
+ value.with_indifferent_access
19
+ }
20
+ end
21
+
22
+ def perform
23
+ if context.notification['Type'] == 'SubscriptionConfirmation'
24
+ Nuntius.config.logger.info('Confirming SNS subscription')
25
+ HTTPClient.get(body['SubscribeURL'])
26
+ return
27
+ end
28
+
29
+ type = context.notification['notificationType']
30
+
31
+ unless message_id
32
+ Nuntius.config.logger.warn("SNS / SES message could not determine message id: #{context.notification}")
33
+ return false
34
+ end
35
+ unless message
36
+ Nuntius.config.logger.warn("SNS / SES message for unknown message with message id: #{message_id}")
37
+ return false
38
+ end
39
+ Nuntius.config.logger.info("SNS /SES updating message #{message.id} for #{type}")
40
+
41
+ case type
42
+ when 'Delivery'
43
+ process_delivery
44
+ when 'Bounce'
45
+ process_bounce
46
+ when 'Complaint'
47
+ process_complaint
48
+ else
49
+ false
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def process_delivery
56
+ message.status = :delivered
57
+ message.metadata[:feedback] = { 'type': 'delivery', 'info': context.notification['delivery'] }
58
+ message.save!
59
+ end
60
+
61
+ def process_bounce
62
+ message.status = :bounced
63
+ message.metadata[:feedback] = { 'type': 'bounce', 'info': context.notification['bounce'] }
64
+ message.save!
65
+ end
66
+
67
+ def process_complaint
68
+ message.status = :complaint
69
+ message.metadata[:feedback] = { 'type': 'complaint', 'info': context.notification['complaint'] }
70
+ message.save!
71
+ end
72
+
73
+ def message_id
74
+ @message_id ||= context.notification.dig('mail', 'commonHeaders', 'messageId')&.[](1...-1)
75
+ end
76
+
77
+ def message
78
+ @message ||= Nuntius::Message.find_by(provider_id: message_id)
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nuntius
4
+ class DeliverInboundMessageService < ApplicationService
5
+ context do
6
+ attribute :inbound_message
7
+ end
8
+
9
+ def perform
10
+ context.inbound_message.update(status: 'delivered')
11
+ Nuntius::BaseMessageBox.deliver(context.inbound_message)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nuntius
4
+ class RetrieveInboundMailService < ApplicationService
5
+ context do
6
+ attribute :settings
7
+ end
8
+
9
+ def perform
10
+ Mail::IMAP.new(context.settings).all do |message, imap, uid|
11
+ inbound_message = Nuntius::InboundMessage.find_or_create_by!(transport: 'mail', provider: 'imap', provider_id: message.message_id,
12
+ digest: Digest::SHA256.hexdigest(message.to_s), status: 'pending')
13
+ inbound_message.from = message.from
14
+ inbound_message.to = message.to
15
+ inbound_message.text = message.body
16
+ # inbound_message.metadata = params
17
+ inbound_message.save!
18
+
19
+ if inbound_message.raw_message.attached?
20
+ if Digest::SHA256.hexdigest(message.to_s) == Digest::SHA256.hexdigest(inbound_message.raw_message.download)
21
+ # Only if we have an attachment and it's digest is the same as we find in the mailbox, delete!
22
+ # This never happens on the first round of fetching it.
23
+ imap.store(uid, '+FLAGS', [Net::IMAP::DELETED])
24
+ imap.expunge
25
+ end
26
+ else
27
+ si = StringIO.new
28
+ si.write(message.to_s)
29
+ si.rewind
30
+ inbound_message.raw_message.attach(io: si, filename: message.message_id)
31
+ Nuntius::ProcessInboundMessageJob.perform_later(inbound_message)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ class NuntiusCampaignsTable < ActionTable::ActionTable
4
+ model Nuntius::Campaign
5
+
6
+ column(:name)
7
+ column(:metadata) { |campaign| Nuntius.config.metadata_humanize(campaign.metadata) }
8
+ column(:transport)
9
+ column(:state)
10
+ column(:list) { |campaign| campaign.list.name }
11
+
12
+ initial_order :name, :asc
13
+
14
+ row_link { |campaign| nuntius.edit_admin_campaign_path(campaign) }
15
+
16
+ private
17
+
18
+ def scope
19
+ @scope = Nuntius::Campaign.visible
20
+ end
21
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ class NuntiusLayoutsTable < ActionTable::ActionTable
4
+ model Nuntius::Layout
5
+
6
+ column(:name) { |layout| layout.name }
7
+ column(:metadata) { |layout| Nuntius.config.metadata_humanize(layout.metadata) }
8
+
9
+ column :actions, title: '', sortable: false do |layout|
10
+ content_tag(:span, class: 'btn-group btn-group-xs') do
11
+ concat link_to(content_tag(:i, nil, class: 'fa fa-trash'), nuntius.admin_layout_url(layout), data: { turbo_confirm: 'Are you sure you want to delete the layout?', turbo_method: :delete }, class: 'btn btn-xs btn-danger')
12
+ end
13
+ end
14
+
15
+ initial_order :name, :asc
16
+
17
+ row_link { |layout| nuntius.edit_admin_layout_path(layout) }
18
+
19
+ private
20
+
21
+ def scope
22
+ @scope = Nuntius::Layout.visible
23
+ end
24
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class NuntiusListsTable < ActionTable::ActionTable
4
+ model Nuntius::List
5
+
6
+ column(:name)
7
+ column(:metadata) { |list| Nuntius.config.metadata_humanize(list.metadata) }
8
+ column(:subscribers) { |list| list.subscribers.count || '-' }
9
+
10
+ initial_order :name, :asc
11
+
12
+ row_link { |list| nuntius.edit_admin_list_path(list) }
13
+
14
+ private
15
+
16
+ def scope
17
+ @scope = Nuntius::List.visible
18
+ end
19
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ class NuntiusLocalesTable < ActionTable::ActionTable
4
+ model Nuntius::Locale
5
+
6
+ column(:key)
7
+ column(:metadata) { |locale| Nuntius.config.metadata_humanize(locale.metadata) }
8
+
9
+ initial_order :mkey, :asc
10
+
11
+ row_link { |locale| nuntius.edit_admin_locale_path(locale) }
12
+
13
+ private
14
+
15
+ def scope
16
+ @scope = Nuntius::Locale.visible
17
+ end
18
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ class NuntiusMessagesTable < ActionTable::ActionTable
4
+ model Nuntius::Message
5
+
6
+ column(:to)
7
+ column(:created_at) { |message| ln(message.created_at) }
8
+ column(:last_sent_at) { |message| ln(message.last_sent_at) }
9
+ column(:origin) do |message|
10
+ link_to message.campaign&.name, nuntius.edit_admin_campaign_path(message.campaign) if message.campaign
11
+ link_to message.template&.description, nuntius.edit_admin_template_path(message.template) if message.template
12
+ end
13
+ column(:subject) do |message|
14
+ if message.nuntiable
15
+ link_to "#{message.nuntiable_type} [#{message.nuntiable}]", begin
16
+ url_for(message.nuntiable)
17
+ rescue StandardError
18
+ '#'
19
+ end
20
+ end
21
+ end
22
+ column(:status)
23
+
24
+ initial_order :created_at, :desc
25
+
26
+ row_link { |message| nuntius.admin_message_path(message) }
27
+
28
+ private
29
+
30
+ def scope
31
+ @scope = Nuntius::Message.visible
32
+ @scope = @scope.where(nuntiable_id: params[:nuntiable_id], nuntiable_type: params[:nuntiable_type]) if params[:nuntiable_id]
33
+ @scope = @scope.where(template_id: params[:template_id]) if params[:template_id]
34
+ @scope
35
+ end
36
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class NuntiusSubscribersTable < ActionTable::ActionTable
4
+ model Nuntius::Subscriber
5
+
6
+ column(:name, sortable: false) { |subscriber| subscriber.name }
7
+ column(:email)
8
+ column(:phonenumber)
9
+
10
+ initial_order :name, :asc
11
+
12
+ row_link { |subscriber| nuntius.edit_admin_list_subscriber_path(params[:list_id], subscriber) }
13
+
14
+ private
15
+
16
+ def scope
17
+ @scope = Nuntius::Subscriber.where(list_id: params[:list_id])
18
+ end
19
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ class NuntiusTemplatesTable < ActionTable::ActionTable
4
+ model Nuntius::Template
5
+
6
+ column(:description)
7
+ column(:enabled, as: :boolean)
8
+ column(:klass)
9
+ column(:event)
10
+ column(:"# messages") { |template| link_to template.messages.count, nuntius.admin_messages_path(template_id: template.id) }
11
+
12
+ column(:metadata) { |template| Nuntius.config.metadata_humanize(template.metadata) }
13
+ column(:created_at) { |flow| ln(flow.created_at) }
14
+
15
+ column :actions, title: '', sortable: false do |template|
16
+ content_tag(:span, class: 'btn-group btn-group-xs') do
17
+ concat link_to(content_tag(:i, nil, class: 'fa fa-trash'), nuntius.admin_template_path(template), data: { turbo_confirm: 'Are you sure you want to delete the template?', turbo_method: :delete }, class: 'btn btn-xs btn-danger')
18
+ end
19
+ end
20
+
21
+ initial_order :description, :asc
22
+
23
+ row_link { |template| nuntius.edit_admin_template_path(template) }
24
+
25
+ private
26
+
27
+ def scope
28
+ @scope = Nuntius::Template.visible
29
+ end
30
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nuntius
4
+ class BaseTransport
5
+ def kind
6
+ self.class.name.demodulize.gsub(/Transport$/, '').underscore.to_sym
7
+ end
8
+
9
+ def deliver(message)
10
+ priority = 1
11
+ wait_time = 0
12
+ message.update(transport: kind.to_s) if message.transport.blank?
13
+ while (providers_for_priority = providers(priority)).present?
14
+ time_out = 0
15
+ providers_for_priority.each do |hash|
16
+ message.update(provider: hash[:provider].to_s) if message.provider.blank?
17
+
18
+ Nuntius::TransportDeliveryJob.set(wait: !Rails.env.development? && wait_time).perform_later(hash[:provider].to_s, message)
19
+ time_out += hash[:timeout].seconds if hash[:timeout].positive?
20
+ end
21
+ wait_time += time_out
22
+ priority += 1
23
+ end
24
+ end
25
+
26
+ def providers(priority = nil)
27
+ results = Nuntius.config.providers[kind].to_a
28
+ results = results.select { |provider| provider[:priority] == priority } if priority
29
+ results
30
+ end
31
+
32
+ def self.class_from_name(name)
33
+ Nuntius.const_get("#{name}_transport".camelize)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nuntius
4
+ class MailTransport < BaseTransport
5
+ # We split per email address, to allow easy resends
6
+ def deliver(message)
7
+ message.html = Inky::Core.new.release_the_kraken(message.html)
8
+
9
+ premailer = Premailer.new(message.html, with_html_string: true)
10
+ message.html = premailer.to_inline_css
11
+ message.text = premailer.to_plain_text
12
+
13
+ message.request_id = SecureRandom.uuid
14
+
15
+ tos = message.to.split(/[\s;,]+/)
16
+
17
+ messages = []
18
+ message.to = tos.first
19
+ messages << message
20
+
21
+ tos[1..-1].each do |to|
22
+ # FIXME: Sadly this also duplicates the attachments
23
+ new_message = message.deep_dup
24
+ new_message.to = to
25
+ new_message.attachments = message.attachments if message.attachments.present?
26
+
27
+ messages << new_message
28
+ end
29
+
30
+ messages.each { |m| super(m) }
31
+
32
+ messages.first
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nuntius
4
+ class PushTransport < BaseTransport
5
+ # We split per email address, to allow easy resends
6
+ def deliver(message)
7
+ message.request_id = SecureRandom.uuid
8
+ message.to.split(',').each do |to|
9
+ new_message = message.dup
10
+ new_message.to = to
11
+ super(new_message)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nuntius
4
+ class SlackTransport < BaseTransport
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nuntius
4
+ class SmsTransport < BaseTransport
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nuntius
4
+ class VoiceTransport < BaseTransport
5
+ end
6
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class LiquidValidator < ActiveModel::EachValidator
4
+ def validate_each(record, attribute, value)
5
+ return if value.blank?
6
+
7
+ Liquid::Template.parse(value)
8
+ rescue Liquid::SyntaxError => e
9
+ record.errors[attribute] << (options[:message] || "is not valid liquid: #{e.message}")
10
+ end
11
+ end
@@ -0,0 +1,49 @@
1
+ - if @campaign.draft?
2
+ = sts.form_for [:admin, @campaign] do |f|
3
+ = sts.card title: t('.campaign'), icon: 'fal fa-megaphone' do |card|
4
+ - card.action
5
+ = f.submit
6
+
7
+ .grid.grid-cols-12.gap-4 data-controller="toggle"
8
+ .col-span-12
9
+ = f.input :name
10
+
11
+ .col-span-4
12
+ = f.input :transport, collection: Nuntius.config.transports, include_blank: false, input_html: {data: { 'toggle-target' => 'input'}}
13
+ .col-span-4
14
+ = f.association :layout, collection: @layouts
15
+ .col-span-4
16
+ = f.association :list, collection: @lists
17
+
18
+ .col-span-12
19
+ = f.input :from, hint: 'You can leave this blank, only fill this in if you want to override the default'
20
+
21
+ .col-span-12
22
+ .grid.grid-cols-12.gap-4 data-toggle-target="insertion"
23
+
24
+ template data-toggle-target='toggleable' data-toggle-value='mail'
25
+ .col-span-12
26
+ = f.input :subject
27
+ .col-span-12
28
+ = f.input :html, as: :hidden
29
+ trix-editor input="campaign_html" style="background-color: #fff; height: 400px; margin-bottom: 20px;"
30
+
31
+ template data-toggle-target='toggleable' data-toggle-value='voice'
32
+ .col-span-12
33
+ = f.input :text, as: :editor, mode: 'text/plain'
34
+
35
+
36
+ - else
37
+ h2 = @campaign.name
38
+ p We're sending this campaign, you can no longer make any changes. This is the audience we're sending it to:
39
+
40
+ table.table
41
+ thead
42
+ tr
43
+ th Status
44
+ th To
45
+ tbody
46
+ - for message in @messages
47
+ tr
48
+ td = link_to message.status, admin_message_path(message)
49
+ td = message.to
@@ -0,0 +1,2 @@
1
+ = sts.card title: t('.campaigns'), icon: 'fad fa-megaphone', content_padding: false, menu: nuntius_campaigns_menu do |card|
2
+ = sts.table :nuntius_campaigns
@@ -0,0 +1,28 @@
1
+ - attachments.each do |attachment|
2
+ li.relative
3
+ /! Current: "ring-2 ring-offset-2 ring-indigo-500", Default: "focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-offset-gray-100 focus-within:ring-indigo-500"
4
+ .group.block.w-full.aspect-w-10.aspect-h-7.rounded-lg.bg-gray-100.overflow-hidden
5
+ /! Current: "", Default: "group-hover:opacity-75"
6
+ - if attachment.previewable?
7
+ = image_tag attachment.preview(resize_to_limit: [500, 500]).url, class: 'object-cover pointer-events-none'
8
+ - elsif attachment.image?
9
+ = image_tag main_app.url_for(attachment.variant(resize_to_limit: [500, 500])), class: 'object-cover pointer-events-none'
10
+ p.mt-2.block.text-sm.font-medium.text-gray-900.truncate.pointer-events-none = attachment.filename
11
+ p.block.text-sm.font-medium.text-gray-500
12
+ = (attachment.blob.byte_size / 1048576.0).round(2)
13
+ ' MB
14
+ = link_to admin_layout_attachment_path(@layout, attachment.id), data: { controller: 'attachment-delete', 'action': 'attachment-delete#delete' } do
15
+ i.fal.fa-xmark
16
+ =< link_to(main_app.rails_blob_path(attachment, disposition: 'attachment'),
17
+ title: attachment.filename)
18
+ i.fal.fa-download
19
+
20
+ / This controller comes from papyrus
21
+ li.relative.attachment-upload data-controller="attachment-upload" data-attachment-upload-url="#{upload_url}" data-attachment-upload-param-name="attachments[]" data-attachment-upload-extra-data='{}'
22
+ .group.block.w-full.aspect-w-10.aspect-h-7.rounded-lg.bg-gray-100.overflow-hidden.text-center.align-middle style="height: 200px;"
23
+ span.icon.upload
24
+ i.fal.fa-4x.fa-upload.pt-16
25
+ span.icon.uploading
26
+ i.fal.fa-4x.fa-circle-notch.fa-spin.pt-16
27
+
28
+ p.mt-2.block.text-sm.font-medium.text-gray-500.truncate.pointer-events-none Drag or click to attach files
@@ -0,0 +1,2 @@
1
+ ul.attachments.grid.grid-cols-2.gap-x-4.gap-y-8.sm:grid-cols-3.sm:gap-x-6.md:grid-cols-4.lg:grid-cols-3.xl:grid-cols-4.xl:gap-x-8 role="list"
2
+ = render partial: 'nuntius/admin/layouts/attachments/attachments', locals: { attachments: @layout.attachments, upload_url: admin_layout_attachments_path(@layout) }
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ json.selector 'ul.attachments'
4
+ json.html render partial: 'nuntius/admin/layouts/attachments/attachments', layout: false, formats: [:html],
5
+ locals: { attachments: @layout.attachments, upload_url: admin_layout_attachments_path(@layout) }
@@ -0,0 +1,24 @@
1
+ = sts.form_for([:admin, @layout]) do |f|
2
+ = sts.card title: t('.layout'), icon: 'fal fa-table-layout' do |card|
3
+ - card.action
4
+ = f.submit
5
+
6
+ = sts.tabs do |tabs|
7
+ - tabs.tab :edit
8
+ .grid.grid-cols-12.gap-4
9
+ .col-span-12
10
+ = f.input :name
11
+ .col-span-12
12
+ = f.input :data, as: :editor, mode: 'text/html', height: '400px'
13
+ - tabs.tab :metadata
14
+ .grid.grid-cols-12.gap-4
15
+ .col-span-12
16
+ = f.input :metadata_yaml, as: :editor, mode: 'application/yaml', label: t('.metadata')
17
+
18
+ - if @layout.persisted?
19
+ - tabs.tab :media
20
+ = render partial: 'nuntius/admin/layouts/attachments/index', locals: { attachments: @layout.attachments, upload_url: nuntius.admin_layout_attachments_path(@layout) }
21
+
22
+ - if @layout.templates.size.positive?
23
+ - tabs.tab :templates
24
+ = sts.table :nuntius_templates, parameters: { layout_id: @layout.id }
@@ -0,0 +1,2 @@
1
+ = sts.card title: t('.layouts'), icon: 'fad fa-table-layout', content_padding: false, menu: nuntius_layouts_menu do |card|
2
+ = sts.table :nuntius_layouts
@@ -0,0 +1,14 @@
1
+ = sts.form_for([:admin, @list]) do |f|
2
+ = sts.card title: t('.list'), icon: 'fad fa-address-book', menu: nuntius_list_menu do |card|
3
+ - card.action
4
+ = f.continue
5
+ - card.action
6
+ = f.submit
7
+
8
+ .grid.grid-cols-12.gap-4
9
+ .col-span-12
10
+ = f.input :name
11
+
12
+ br
13
+ - if @list.subscribers.present?
14
+ = sts.table :nuntius_subscribers, parameters: { list_id: @list.id }
@@ -0,0 +1,2 @@
1
+ = sts.card title: t('.lists'), icon: 'fad fa-address-book', content_padding: false, menu: nuntius_lists_menu do |card|
2
+ = sts.table :nuntius_lists
@@ -0,0 +1,14 @@
1
+ = sts.form_for(@subscriber, url: @subscriber.new_record? ? admin_list_subscribers_path(@list) : admin_list_subscriber_path(@list, @subscriber), html: {multipart: true}) do |f|
2
+ = sts.card title: t('.subscribers'), icon: 'fad fa-address-card' do |card|
3
+ - card.action
4
+ = f.submit
5
+
6
+ .grid.grid-cols-12.gap-4
7
+ .col-span-6
8
+ = f.input :first_name
9
+ .col-span-6
10
+ = f.input :last_name
11
+ .col-span-12
12
+ = f.input :phone_number
13
+ .col-span-12
14
+ = f.input :email
@@ -0,0 +1,14 @@
1
+ = sts.form_for([:admin, @locale]) do |f|
2
+ = sts.card title: t('.locale'), icon: 'fal fa-language' do |card|
3
+ - card.action
4
+ = f.continue
5
+ - card.action
6
+ = f.submit
7
+
8
+ .grid.grid-cols-12.gap-4
9
+ .col-span-12
10
+ = f.input :key
11
+ .col-span-12
12
+ = f.input :data_yaml, as: :editor, mode: 'application/yaml', label: t('.data')
13
+ .col-span-12
14
+ = f.input :metadata_yaml, as: :editor, mode: 'application/yaml', label: t('.metadata')
@@ -0,0 +1,2 @@
1
+ = sts.card title: t('.locales'), icon: 'fad fa-language', content_padding: false, menu: nuntius_locales_menu do |card|
2
+ = sts.table :nuntius_locales
@@ -0,0 +1,2 @@
1
+ = sts.card title: t('.messages'), icon: 'fad fa-envelope', content_padding: false, menu: nuntius_messages_menu do |card|
2
+ = sts.table :nuntius_messages, parameters: { template_id: params[:template_id] }