nuntius 1.4.7 → 1.4.9

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: 60ba6bdae28e285f54ae11261866c96857711e32dc2d8f066a39f01acfff444f
4
- data.tar.gz: f0044e290dc4e32eee2eb7ce024e74325aa437846b304920f2d31d1f211e43d5
3
+ metadata.gz: 8ccc43faba64c7face2decf37a6c33b775ea9d6c13dd36cc08ec86da58da0bbd
4
+ data.tar.gz: f43c4cf84c238bd62956151a468a0adc62c1e798589e02b70ffe85be899a1ad8
5
5
  SHA512:
6
- metadata.gz: 05ba146b1ee291c41819bef8041ed961c6c93c52ffa0474e9522e9e2e5a37657427d61aca8bb387233c6c1c3f641189113dcdd986bfa8f0a1fe137e4266be607
7
- data.tar.gz: d23e9470ad3bc65cc2b1a0f3134d5f45367afcabd152d3e936998142edf67e91a22122fa8b712b3fc49710e609804cc8ce62c7e96d0e99d2f4c2db71acdfe12d
6
+ metadata.gz: db7b5cd54668c544ad6ca9494b350ec2585e3f504bdeb68aa4ce2b8f56a344ee36d2d09b9802d8d3dbd8ecc9df5f8e2d59648ee30f577b65e64e14170a84e21a
7
+ data.tar.gz: 96dbae515949cf5c24d9936712ac8d0068283fce076314943eacb6439b98e0f36329a8e0a5a95f75e2b19322a4c9891117100d2dda90b9186f10e991391abb84
data/README.md CHANGED
@@ -227,6 +227,9 @@ Outbound mail is handled through SMTP. We've only exposed the HTML of mail, we c
227
227
 
228
228
  In the layout you can add `<a href="{{message_url}}">Link to mail</a>` to provide a link to the online version of the message.
229
229
 
230
+ Nuntius supports email tracking, to enable this, you need to enable tracking for a template.
231
+ Nuntius will then inject a tracking pixel and track clicks on links.
232
+
230
233
  #### AWS SES
231
234
 
232
235
  In case you use AWS SES, you can use the SNS Feedback Notifications to automatically mark messages as read, or deal with complaints and bounces. Create a AWS SNS topic, with a HTTPS subscription with the following URL (pattern):
@@ -30,7 +30,7 @@ module Nuntius
30
30
  def update
31
31
  @campaign.update(campaign_params)
32
32
 
33
- respond_with :admin, @campaign
33
+ respond_with :admin, @campaign, action: :edit
34
34
  end
35
35
 
36
36
  def publish
@@ -51,7 +51,7 @@ module Nuntius
51
51
  def campaign_params
52
52
  return unless params[:campaign]
53
53
 
54
- params.require(:campaign).permit(:name, :transport, :layout_id, :list_id, :from, :subject, :text, :html, :metadata_yaml)
54
+ params.require(:campaign).permit(:name, :transport, :layout_id, :list_id, :from, :subject, :text, :html, :metadata_yaml, :open_tracking, :link_tracking)
55
55
  end
56
56
  end
57
57
  end
@@ -5,6 +5,8 @@ require_dependency "nuntius/application_admin_controller"
5
5
  module Nuntius
6
6
  module Admin
7
7
  class ListsController < ApplicationAdminController
8
+ before_action :set_objects, except: [:index]
9
+
8
10
  def index
9
11
  @lists = Nuntius::List.visible.order(:name)
10
12
  end
@@ -16,7 +18,7 @@ module Nuntius
16
18
 
17
19
  def create
18
20
  @list = Nuntius::List.create(list_params)
19
- respond_with :admin, @list
21
+ respond_with :admin, @list, action: :edit
20
22
  end
21
23
 
22
24
  def show
@@ -24,18 +26,18 @@ module Nuntius
24
26
  end
25
27
 
26
28
  def edit
27
- @list = Nuntius::List.visible.find(params[:id])
28
29
  end
29
30
 
30
31
  def update
31
- @list = Nuntius::List.visible.find(params[:id])
32
32
  @list.update(list_params)
33
- respond_with :admin, @list
33
+ respond_with :admin, @list, action: :edit
34
34
  end
35
35
 
36
36
  private
37
37
 
38
38
  def set_objects
39
+ @list = Nuntius::List.visible.find(params[:id]) if params[:id]
40
+ @list ||= Nuntius::List.new
39
41
  end
40
42
 
41
43
  def list_params
@@ -49,7 +49,7 @@ module Nuntius
49
49
  end
50
50
 
51
51
  def template_params
52
- params.require(:template).permit(:enabled, :klass, :event, :interval, :transport, :description, :metadata_yaml, :payload, :from, :to, :subject, :layout_id, :html, :text, :payload)
52
+ params.require(:template).permit(:enabled, :klass, :event, :interval, :transport, :description, :metadata_yaml, :payload, :from, :to, :subject, :layout_id, :html, :text, :payload, :link_tracking, :open_tracking)
53
53
  end
54
54
  end
55
55
  end
@@ -5,8 +5,55 @@ require_dependency "nuntius/application_controller"
5
5
  module Nuntius
6
6
  class MessagesController < ApplicationController
7
7
  layout false
8
+ skip_before_action :verify_authenticity_token
9
+ before_action :set_objects
8
10
 
9
11
  def show
12
+ end
13
+
14
+ # GET /messages/:message_id/pixel.gif
15
+ def pixel
16
+ return if request.env["HTTP_REFERER"].to_s.include?(Nuntius.config.host(@message))
17
+
18
+ if @message&.open_tracking_enabled?
19
+ # Record the first open time
20
+ @message.opened_at ||= Time.current
21
+ @message.open_count = (@message.open_count || 0) + 1
22
+ @message.save
23
+ end
24
+
25
+ # Return a 1x1 transparent GIF pixel
26
+ send_data(Base64.decode64("R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"), type: "image/gif", disposition: "inline")
27
+ end
28
+
29
+ # GET /messages/:message_id/link?url=...
30
+ def link
31
+ return if request.env["HTTP_REFERER"].to_s.include?(Nuntius.config.host(@message))
32
+
33
+ url = params[:url]
34
+
35
+ if @message&.link_tracking_enabled? && url.present?
36
+ # Record the first click time
37
+ @message.clicked_at ||= Time.current
38
+ @message.click_count = (@message.click_count || 0) + 1
39
+ @message.save
40
+
41
+ tracking = @message.message_trackings.find_or_create_by(url: url)
42
+ tracking.count = (tracking.count || 0) + 1
43
+ tracking.save!
44
+ end
45
+
46
+ # Redirect to the original URL
47
+ if url.present?
48
+ redirect_to url, allow_other_host: true
49
+ else
50
+ head :not_found
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def set_objects
10
57
  @message = Nuntius::Message.find(params[:id])
11
58
  end
12
59
  end
@@ -5,6 +5,7 @@ require_dependency "nuntius/application_controller"
5
5
  module Nuntius
6
6
  class SubscribersController < ApplicationController
7
7
  # skip_before_action :authenticate_user!, only: %i[show destroy]
8
+ layout "empty"
8
9
 
9
10
  def show
10
11
  @subscriber = Nuntius::Subscriber.find_by(id: params[:id])
@@ -19,14 +20,15 @@ module Nuntius
19
20
  def subscribe
20
21
  @subscriber = Nuntius::Subscriber.find(params[:id])
21
22
  @subscriber.update(unsubscribed_at: nil)
22
- flash[:notice] = "Subscription has been restored."
23
+ Signum.success(request.session.id, text: "Subscription has been restored.")
24
+
23
25
  redirect_to nuntius.subscriber_path(@subscriber), status: :see_other
24
26
  end
25
27
 
26
28
  def unsubscribe
27
29
  @subscriber = Nuntius::Subscriber.find(params[:id])
28
30
  @subscriber.touch(:unsubscribed_at)
29
- flash[:notice] = "Subscription has been removed."
31
+ Signum.success(request.session.id, text: "Subscription has been removed.")
30
32
  redirect_to nuntius.subscriber_path(@subscriber), status: :see_other
31
33
  end
32
34
  end
@@ -19,6 +19,8 @@ module Nuntius
19
19
  belongs_to :template, optional: true
20
20
  belongs_to :parent_message, class_name: "Message", optional: true
21
21
  has_many :child_messages, class_name: "Message", foreign_key: "parent_message_id", dependent: :destroy
22
+ has_many :message_trackings, class_name: "MessageTracking", dependent: :destroy
23
+
22
24
  belongs_to :nuntiable, polymorphic: true, optional: true
23
25
 
24
26
  validates :transport, presence: true
@@ -55,6 +57,14 @@ module Nuntius
55
57
  status == "undelivered"
56
58
  end
57
59
 
60
+ def opened?
61
+ opened_at.present?
62
+ end
63
+
64
+ def clicked?
65
+ clicked_at.present?
66
+ end
67
+
58
68
  # Removes only pending child messages
59
69
  def cleanup!
60
70
  Nuntius::Message.where(status: "pending").where(parent_message: self).destroy_all
@@ -148,5 +158,13 @@ module Nuntius
148
158
  klass.deliver(self)
149
159
  self
150
160
  end
161
+
162
+ def link_tracking_enabled?
163
+ template&.link_tracking? || campaign&.link_tracking?
164
+ end
165
+
166
+ def open_tracking_enabled?
167
+ template&.open_tracking? || campaign&.open_tracking?
168
+ end
151
169
  end
152
170
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nuntius
4
+ class MessageTracking < ApplicationRecord
5
+ belongs_to :message, class_name: "Nuntius::Message"
6
+ end
7
+ end
@@ -33,10 +33,10 @@ module Nuntius
33
33
 
34
34
  options = {registers: {"template" => self, "message" => message}}
35
35
 
36
- message.to = render(:to, assigns, locale, options).split(',').map(&:strip).reject{ |to| to == 'Liquid error internal' }.reject(&:empty?).join(',')
36
+ message.to = render(:to, assigns, locale, options).split(",").map(&:strip).reject { |to| to == "Liquid error internal" }.reject(&:empty?).join(",")
37
37
  return unless message.to.present?
38
38
 
39
- message.from = render(:from, assigns, locale, options).split(',').reject(&:empty?).join(',')
39
+ message.from = render(:from, assigns, locale, options).split(",").reject(&:empty?).join(",")
40
40
  message.subject = render(:subject, assigns, locale, options)
41
41
  message.html = render(:html, assigns, locale, options.merge(layout: layout&.data))
42
42
  message.text = render(:text, assigns, locale, options)
@@ -45,7 +45,11 @@ module Nuntius
45
45
  charset: "UTF-8"
46
46
  )
47
47
  if message.html.present?
48
+ # Apply email tracking (tracking pixel and link wrapping)
49
+ Nuntius::EmailTrackingService.perform(message: message)
50
+
48
51
  message.html = message.html.gsub("{{message_url}}") { message_url(message) }
52
+
49
53
  p.html_part = Mail::Part.new(
50
54
  body: message.html,
51
55
  content_type: "text/html",
@@ -70,7 +74,7 @@ module Nuntius
70
74
 
71
75
  message.provider_id = mail.message_id
72
76
  message.status = "undelivered"
73
- message.status = "sent" if Rails.env.test? ? true : response.success?
77
+ message.status = "sent" if Rails.env.test? || response.success?
74
78
  message.last_sent_at = Time.zone.now if message.sent?
75
79
 
76
80
  message
@@ -11,7 +11,7 @@ module Nuntius
11
11
 
12
12
  def perform
13
13
  deliver
14
- # campaign.sent!
14
+ campaign.sent!
15
15
  end
16
16
 
17
17
  def deliver
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Use Nokogiri to parse and modify links
4
+ require "nokogiri"
5
+
6
+ module Nuntius
7
+ class EmailTrackingService < ApplicationService
8
+ context do
9
+ attribute :message
10
+ end
11
+
12
+ def perform
13
+ return context.message.html unless context.message.link_tracking_enabled? || context.message.open_tracking_enabled?
14
+
15
+ tracked_html = context.message.html.dup
16
+ tracked_html = inject_tracking_pixel(tracked_html)
17
+ tracked_html = wrap_links_with_tracking(tracked_html)
18
+
19
+ context.message.html = tracked_html
20
+ end
21
+
22
+ private
23
+
24
+ def inject_tracking_pixel(html)
25
+ return html unless context.message.open_tracking_enabled?
26
+
27
+ tracking_pixel_url = tracking_pixel_url(context.message.id)
28
+ tracking_pixel = %(<img src="#{tracking_pixel_url}" width="1" height="1" alt=""/>)
29
+
30
+ # Try to inject before closing body tag, otherwise append to end
31
+ if html.include?("</body>")
32
+ html.gsub("</body>", "#{tracking_pixel}</body>")
33
+ else
34
+ html + tracking_pixel
35
+ end
36
+ end
37
+
38
+ def wrap_links_with_tracking(html)
39
+ return html unless context.message.link_tracking_enabled?
40
+
41
+ doc = Nokogiri::HTML.fragment(html)
42
+
43
+ doc.css("a[href]").each do |link|
44
+ original_url = link["href"]
45
+
46
+ # Skip if it's a mailto, tel, anchor link or liquid variable
47
+ next if original_url.start_with?("mailto:", "tel:", "#", "{{")
48
+
49
+ # Skip if it's already a tracking link
50
+ next if original_url.include?("/tracking/")
51
+
52
+ # Create tracking URL
53
+ tracking_url = tracking_link_url(context.message.id, url: original_url)
54
+ link["href"] = tracking_url
55
+ end
56
+
57
+ doc.to_html
58
+ end
59
+
60
+ def tracking_pixel_url(message_id)
61
+ Nuntius::Engine.routes.url_helpers.tracking_pixel_message_url(
62
+ message_id,
63
+ host: Nuntius.config.host(context.message)
64
+ )
65
+ end
66
+
67
+ def tracking_link_url(message_id, url:)
68
+ Nuntius::Engine.routes.url_helpers.tracking_link_message_url(
69
+ message_id,
70
+ url: url,
71
+ host: Nuntius.config.host(context.message)
72
+ )
73
+ end
74
+ end
75
+ end
@@ -8,6 +8,15 @@ module Nuntius
8
8
  column(:to)
9
9
  column(:status)
10
10
  column(:created_at)
11
+ column(:last_sent_at)
12
+ action :resend do
13
+ link { |message| nuntius.resend_admin_message_path(message) }
14
+ icon "fal fa-rotate-right"
15
+ link_attributes data: {"turbo-confirm": "Are you sure you want to resend the message?", "turbo-method": :post}
16
+ show ->(message) { true }
17
+ end
18
+ column(:open_count)
19
+ column(:click_count)
11
20
 
12
21
  order created_at: :desc
13
22
 
@@ -17,7 +26,7 @@ module Nuntius
17
26
  private
18
27
 
19
28
  def scope
20
- @scope = Nuntius::Campaign.find_by(params[:campaign_id]).messages
29
+ @scope = Nuntius::Campaign.find(params[:campaign_id]).messages
21
30
  @scope
22
31
  end
23
32
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nuntius
4
+ class MessageTrackingsTable < Nuntius::ApplicationTable
5
+ definition do
6
+ model Nuntius::MessageTracking
7
+
8
+ column(:url)
9
+ column(:count)
10
+ column(:updated_at)
11
+
12
+ order updated_at: :desc
13
+ end
14
+
15
+ private
16
+
17
+ def scope
18
+ @scope = Nuntius::MessageTracking.where(message_id: params[:message_id])
19
+ end
20
+ end
21
+ end
@@ -2,7 +2,6 @@
2
2
 
3
3
  module Nuntius
4
4
  class MessagesTable < Nuntius::ApplicationTable
5
-
6
5
  definition do
7
6
  model Nuntius::Message
8
7
 
@@ -15,7 +14,9 @@ module Nuntius
15
14
  link_attributes data: {"turbo-confirm": "Are you sure you want to resend the message?", "turbo-method": :post}
16
15
  show ->(message) { true }
17
16
  end
18
-
17
+ column(:open_count)
18
+ column(:click_count)
19
+
19
20
  column(:campaign_id) do
20
21
  render do
21
22
  html do |message|
@@ -0,0 +1,21 @@
1
+ head.h-full.bg-white
2
+ title= t(:app_name)
3
+ meta name="viewport" content="width=device-width, initial-scale=1.0"
4
+ meta name="turbo-prefetch" content="false"
5
+ = javascript_include_tag asset_url("fontawesome.js"), defer: true
6
+ = javascript_include_tag asset_url("solid.js"), defer: true
7
+ = javascript_include_tag asset_url("duotone.js"), defer: true
8
+ = javascript_include_tag asset_url("light.js"), defer: true
9
+
10
+ = javascript_importmap_tags "application"
11
+ = stylesheet_link_tag "tailwind", "data-turbo-track": "reload"
12
+ = csrf_meta_tags
13
+ = csp_meta_tag
14
+ - if content_for?(:head)
15
+ = yield(:head)
16
+
17
+ body
18
+ main
19
+ = yield
20
+
21
+ = render(Signum::Notifications::Component.new([request.session.id]))
@@ -18,12 +18,16 @@
18
18
  = f.association :list, collection: @lists
19
19
 
20
20
  .col-span-12
21
- = f.input :from, hint: 'You can leave this blank, only fill this in if you want to override the default'
21
+ = f.input :from, hint: t(".from_hint")
22
22
 
23
23
  .col-span-12
24
24
  .grid.grid-cols-12.gap-4 data-toggle-target="insertion"
25
25
 
26
26
  template data-toggle-target='toggleable' data-toggle-value='mail'
27
+ .col-span-6
28
+ = f.input :link_tracking, as: :switch
29
+ .col-span-6
30
+ = f.input :open_tracking, as: :switch
27
31
  .col-span-12
28
32
  = f.input :subject
29
33
  .col-span-12
@@ -34,3 +34,12 @@
34
34
  = info.with_item :refreshes, content: @message.refreshes, class: "sm:col-span-1"
35
35
  = info.with_item :last_sent_at, content: ln(@message.last_sent_at), class: "sm:col-span-1", icon: 'fal fa-envelope'
36
36
  = info.with_item :created_at, content: ln(@message.created_at), class: "sm:col-span-1", icon: 'fal fa-calendar'
37
+ - if @message.link_tracking_enabled?
38
+ = info.with_item :click_count, content: @message.click_count, class: "sm:col-span-1"
39
+ - if @message.open_tracking_enabled?
40
+ = info.with_item :open_count, content: @message.open_count, class: "sm:col-span-1"
41
+
42
+ - if @message.link_tracking_enabled? || @message.open_tracking_enabled?
43
+ - card.with_tab:tracking
44
+ = sts.table :"nuntius/message_trackings", params: {message_id: @message.id}
45
+
@@ -50,6 +50,10 @@
50
50
  = f.input :metadata_yaml, as: :editor, mode: 'application/yaml', label: t('.metadata')
51
51
 
52
52
  template data-toggle-target='toggleable' data-toggle-value='mail'
53
+ .col-span-6
54
+ = f.input :link_tracking, as: :switch
55
+ .col-span-6
56
+ = f.input :open_tracking, as: :switch
53
57
  .col-span-12
54
58
  = f.input :subject, as: :editor, mode: 'text/plain'
55
59
  .col-span-12
@@ -10,6 +10,7 @@ en:
10
10
  state: Staat
11
11
  subject: Subject
12
12
  transport: Transport
13
+ html: Body
13
14
  nuntius/layout:
14
15
  name: Name
15
16
  nuntius/list:
@@ -55,6 +56,7 @@ en:
55
56
  card:
56
57
  nuntius_admin_campaigns:
57
58
  title: Campaigns
59
+ from_hint: "You can leave this blank, only fill this in if you want to override the default"
58
60
  metadata: Metadata
59
61
  index:
60
62
  card:
@@ -138,12 +140,15 @@ en:
138
140
  subject: Subject
139
141
  to: To
140
142
  transport: Transport
143
+ click_count: Click Count
144
+ open_count: Open Count
141
145
  message: Message
142
146
  tabs:
143
147
  main:
144
148
  tab:
145
149
  details: Details
146
150
  preview: Preview
151
+ tracking: Tracking
147
152
  templates:
148
153
  edit:
149
154
  card:
@@ -10,6 +10,7 @@ nl:
10
10
  state: Staat
11
11
  subject: Onderwerp
12
12
  transport: Transport
13
+ html: Body
13
14
  nuntius/layout:
14
15
  name: Naam
15
16
  nuntius/list:
@@ -56,6 +57,7 @@ nl:
56
57
  nuntius_admin_campaigns:
57
58
  title: Campagne
58
59
  metadata: Metadata
60
+ from_hint: "Je kunt dit leeg laten, vul dit alleen in als je de standaard wilt overschrijven"
59
61
  index:
60
62
  card:
61
63
  nuntius_admin_campaigns:
@@ -140,12 +142,16 @@ nl:
140
142
  subject: Onderwerp
141
143
  to: Aan
142
144
  transport: Transport
145
+ click_count: Click Count
146
+ open_count: Open Count
147
+
143
148
  message: Message
144
149
  tabs:
145
150
  main:
146
151
  tab:
147
152
  details: Details
148
153
  preview: Preview
154
+ tracking: Tracking
149
155
  templates:
150
156
  edit:
151
157
  card:
data/config/routes.rb CHANGED
@@ -10,7 +10,13 @@ Nuntius::Engine.routes.draw do
10
10
 
11
11
  post "feedback/awssns" => "feedback#awssns"
12
12
 
13
- resources :messages
13
+ resources :messages do
14
+ # Email tracking routes
15
+ member do
16
+ get "pixel", to: "messages#pixel", as: :tracking_pixel
17
+ get "link", to: "messages#link", as: :tracking_link
18
+ end
19
+ end
14
20
  resources :campaigns
15
21
  resources :subscribers do
16
22
  member do
@@ -3,7 +3,7 @@ class UpdateNuntiusEventsIndex < ActiveRecord::Migration[7.0]
3
3
  remove_index :nuntius_events, name: :index_nuntius_events_on_transitionable
4
4
 
5
5
  add_index :nuntius_events,
6
- [:transitionable_type, :transitionable_id, :transition_event],
7
- name: :index_nuntius_events_on_type_id_event
6
+ [:transitionable_type, :transitionable_id, :transition_event],
7
+ name: :index_nuntius_events_on_type_id_event
8
8
  end
9
9
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddTrackingToNuntiusMessages < ActiveRecord::Migration[7.0]
4
+ def change
5
+ add_column :nuntius_messages, :opened_at, :datetime
6
+ add_column :nuntius_messages, :clicked_at, :datetime
7
+ add_column :nuntius_messages, :open_count, :integer, default: 0
8
+ add_column :nuntius_messages, :click_count, :integer, default: 0
9
+
10
+ add_column :nuntius_templates, :open_tracking, :boolean, default: false
11
+ add_column :nuntius_templates, :link_tracking, :boolean, default: false
12
+
13
+ create_table :nuntius_message_trackings, id: :uuid do |t|
14
+ t.references :message, type: :uuid, foreign_key: {to_table: :nuntius_messages}
15
+ t.string :url
16
+ t.integer :count, default: 0
17
+
18
+ t.timestamps
19
+ end
20
+
21
+ add_column :nuntius_campaigns, :open_tracking, :boolean, default: false
22
+ add_column :nuntius_campaigns, :link_tracking, :boolean, default: false
23
+ end
24
+ end
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Nuntius::ActiveStorageHelpers
4
4
  extend ActiveSupport::Concern
5
+
5
6
  included do
6
7
  has_one_attached :content, service: Nuntius.config.active_storage_service
7
8
  end
@@ -7,13 +7,13 @@ module Nuntius
7
7
  included do
8
8
  raise "#{name} must be nuntiable" unless nuntiable?
9
9
 
10
- I18n.t('devise.mailer').keys.map(&:to_s).each do |event_name|
10
+ I18n.t("devise.mailer").keys.map(&:to_s).each do |event_name|
11
11
  messenger.send(:define_method, event_name) { |object, options = {}| }
12
12
  end
13
13
 
14
14
  define_method(:send_devise_notification) do |notification, *devise_params|
15
15
  # All notifications have either a token as the first param, or nothing
16
- Nuntius.event(notification, self, { token: devise_params.first })
16
+ Nuntius.event(notification, self, {token: devise_params.first})
17
17
  end
18
18
  end
19
19
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nuntius
4
- VERSION = "1.4.7"
4
+ VERSION = "1.4.9"
5
5
  end
data/lib/nuntius.rb CHANGED
@@ -10,6 +10,7 @@ require "nuntius/mail_allow_list"
10
10
 
11
11
  module Nuntius
12
12
  extend Configurable
13
+
13
14
  ROOT_PATH = Pathname.new(File.join(__dir__, ".."))
14
15
 
15
16
  class Error < StandardError; end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nuntius
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.7
4
+ version: 1.4.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom de Grunt
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-01-23 00:00:00.000000000 Z
11
+ date: 2026-02-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: apnotic
@@ -416,7 +416,9 @@ dependencies:
416
416
  - - "~>"
417
417
  - !ruby/object:Gem::Version
418
418
  version: '1'
419
- description: Messages are defined in editable liquid templates.
419
+ description: |
420
+ Messsages can be sent based on creating, updating, or destroying a record.
421
+ It also uses state-events to send messages based on state changes.
420
422
  email:
421
423
  - tom@degrunt.nl
422
424
  executables: []
@@ -476,6 +478,7 @@ files:
476
478
  - app/models/nuntius/list.rb
477
479
  - app/models/nuntius/locale.rb
478
480
  - app/models/nuntius/message.rb
481
+ - app/models/nuntius/message_tracking.rb
479
482
  - app/models/nuntius/subscriber.rb
480
483
  - app/models/nuntius/template.rb
481
484
  - app/presenters/application_presenter.rb
@@ -494,6 +497,7 @@ files:
494
497
  - app/services/nuntius/aws_sns_processor_service.rb
495
498
  - app/services/nuntius/deliver_campaign_service.rb
496
499
  - app/services/nuntius/deliver_inbound_message_service.rb
500
+ - app/services/nuntius/email_tracking_service.rb
497
501
  - app/services/nuntius/retrieve_inbound_mail_service.rb
498
502
  - app/tables/nuntius/application_table.rb
499
503
  - app/tables/nuntius/campaign_messages_table.rb
@@ -501,6 +505,7 @@ files:
501
505
  - app/tables/nuntius/layouts_table.rb
502
506
  - app/tables/nuntius/lists_table.rb
503
507
  - app/tables/nuntius/locales_table.rb
508
+ - app/tables/nuntius/message_trackings_table.rb
504
509
  - app/tables/nuntius/messages_table.rb
505
510
  - app/tables/nuntius/subscribers_table.rb
506
511
  - app/tables/nuntius/templates_table.rb
@@ -512,6 +517,7 @@ files:
512
517
  - app/transports/nuntius/teams_transport.rb
513
518
  - app/transports/nuntius/voice_transport.rb
514
519
  - app/validators/liquid_validator.rb
520
+ - app/views/layouts/empty.html.slim
515
521
  - app/views/nuntius/admin/campaigns/edit.html.slim
516
522
  - app/views/nuntius/admin/campaigns/index.html.slim
517
523
  - app/views/nuntius/admin/layouts/edit.html.slim
@@ -559,6 +565,7 @@ files:
559
565
  - db/migrate/20250520091916_create_nuntius_events_table.rb
560
566
  - db/migrate/20250521132554_update_nuntius_events_index.rb
561
567
  - db/migrate/20260123160443_change_nuntius_events_id.rb
568
+ - db/migrate/20260210122500_add_tracking_to_nuntius_messages.rb
562
569
  - lib/generators/nuntius/install_generator.rb
563
570
  - lib/generators/nuntius/tailwind_config_generator.rb
564
571
  - lib/generators/nuntius/templates/config/initializers/nuntius.rb
@@ -599,5 +606,6 @@ requirements: []
599
606
  rubygems_version: 3.4.10
600
607
  signing_key:
601
608
  specification_version: 4
602
- summary: Messaging and notification for Ruby on Rails
609
+ summary: Easily send emails, sms messages, teams & slack messages, and push notifications
610
+ from your Rails app.
603
611
  test_files: []