kapso-client-ruby 1.0.1 → 1.0.2

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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +81 -81
  3. data/CHANGELOG.md +262 -91
  4. data/Gemfile +20 -20
  5. data/RAILS_INTEGRATION.md +477 -477
  6. data/README.md +1053 -752
  7. data/Rakefile +40 -40
  8. data/TEMPLATE_TOOLS_GUIDE.md +120 -120
  9. data/WHATSAPP_24_HOUR_GUIDE.md +133 -133
  10. data/examples/advanced_features.rb +352 -349
  11. data/examples/advanced_messaging.rb +241 -0
  12. data/examples/basic_messaging.rb +139 -136
  13. data/examples/enhanced_interactive.rb +400 -0
  14. data/examples/flows_usage.rb +307 -0
  15. data/examples/interactive_messages.rb +343 -0
  16. data/examples/media_management.rb +256 -253
  17. data/examples/rails/jobs.rb +387 -387
  18. data/examples/rails/models.rb +239 -239
  19. data/examples/rails/notifications_controller.rb +226 -226
  20. data/examples/template_management.rb +393 -390
  21. data/kapso-ruby-logo.jpg +0 -0
  22. data/lib/kapso_client_ruby/client.rb +321 -316
  23. data/lib/kapso_client_ruby/errors.rb +348 -329
  24. data/lib/kapso_client_ruby/rails/generators/install_generator.rb +75 -75
  25. data/lib/kapso_client_ruby/rails/generators/templates/env.erb +20 -20
  26. data/lib/kapso_client_ruby/rails/generators/templates/initializer.rb.erb +32 -32
  27. data/lib/kapso_client_ruby/rails/generators/templates/message_service.rb.erb +137 -137
  28. data/lib/kapso_client_ruby/rails/generators/templates/webhook_controller.rb.erb +61 -61
  29. data/lib/kapso_client_ruby/rails/railtie.rb +54 -54
  30. data/lib/kapso_client_ruby/rails/service.rb +188 -188
  31. data/lib/kapso_client_ruby/rails/tasks.rake +166 -166
  32. data/lib/kapso_client_ruby/resources/calls.rb +172 -172
  33. data/lib/kapso_client_ruby/resources/contacts.rb +190 -190
  34. data/lib/kapso_client_ruby/resources/conversations.rb +103 -103
  35. data/lib/kapso_client_ruby/resources/flows.rb +382 -0
  36. data/lib/kapso_client_ruby/resources/media.rb +205 -205
  37. data/lib/kapso_client_ruby/resources/messages.rb +760 -380
  38. data/lib/kapso_client_ruby/resources/phone_numbers.rb +85 -85
  39. data/lib/kapso_client_ruby/resources/templates.rb +283 -283
  40. data/lib/kapso_client_ruby/types.rb +348 -262
  41. data/lib/kapso_client_ruby/version.rb +5 -5
  42. data/lib/kapso_client_ruby.rb +75 -74
  43. data/scripts/.env.example +17 -17
  44. data/scripts/kapso_template_finder.rb +91 -91
  45. data/scripts/sdk_setup.rb +404 -404
  46. data/scripts/test.rb +60 -60
  47. metadata +12 -3
@@ -1,240 +1,240 @@
1
- # frozen_string_literal: true
2
-
3
- # Example Rails model integration showing WhatsApp messaging hooks
4
- class User < ApplicationRecord
5
- has_many :orders, dependent: :destroy
6
- has_many :whatsapp_messages, dependent: :destroy
7
-
8
- # Validations
9
- validates :email, presence: true, uniqueness: true
10
- validates :phone_number, format: { with: /\A\+\d{10,15}\z/, message: "must be in E.164 format" }, allow_blank: true
11
- validates :preferred_language, inclusion: { in: %w[en es fr], message: "must be a supported language" }
12
-
13
- # Callbacks for WhatsApp integration
14
- after_create :send_welcome_message, if: :phone_number?
15
- after_update :send_phone_verification, if: :phone_number_changed?
16
-
17
- # Scopes
18
- scope :with_phone_number, -> { where.not(phone_number: nil) }
19
- scope :opted_in_for_notifications, -> { where(notifications_enabled: true) }
20
-
21
- # Instance methods
22
-
23
- def send_welcome_message
24
- SendWelcomeMessageJob.perform_later(self)
25
- end
26
-
27
- def send_phone_verification
28
- return unless phone_number.present?
29
-
30
- SendPhoneVerificationJob.perform_later(self)
31
- end
32
-
33
- def send_notification(message, type: 'general')
34
- return unless phone_number.present? && notifications_enabled?
35
-
36
- SendNotificationJob.perform_later(self, message, type)
37
- end
38
-
39
- def send_order_confirmation(order)
40
- return unless phone_number.present?
41
-
42
- SendOrderConfirmationJob.perform_later(self, order)
43
- end
44
-
45
- # Format phone number for WhatsApp (ensure E.164 format)
46
- def formatted_phone_number
47
- return nil unless phone_number.present?
48
-
49
- # Remove any non-digit characters except the leading +
50
- cleaned = phone_number.gsub(/[^\d+]/, '')
51
-
52
- # Ensure it starts with +
53
- cleaned.start_with?('+') ? cleaned : "+#{cleaned}"
54
- end
55
-
56
- # Check if user can receive WhatsApp messages
57
- def can_receive_whatsapp?
58
- phone_number.present? && notifications_enabled?
59
- end
60
-
61
- # Get recent WhatsApp messages
62
- def recent_whatsapp_messages(limit = 10)
63
- whatsapp_messages.order(created_at: :desc).limit(limit)
64
- end
65
-
66
- # Check if user has received a specific message type recently
67
- def received_message_type_recently?(message_type, within: 24.hours)
68
- whatsapp_messages
69
- .where(message_type: message_type)
70
- .where('created_at > ?', within.ago)
71
- .exists?
72
- end
73
-
74
- private
75
-
76
- def phone_number_changed?
77
- saved_change_to_phone_number? && phone_number.present?
78
- end
79
- end
80
-
81
- # Example Order model with WhatsApp integration
82
- class Order < ApplicationRecord
83
- belongs_to :user
84
- has_many :order_items, dependent: :destroy
85
- has_many :whatsapp_messages, through: :user
86
-
87
- # Order statuses
88
- enum status: {
89
- pending: 0,
90
- confirmed: 1,
91
- processing: 2,
92
- shipped: 3,
93
- delivered: 4,
94
- cancelled: 5
95
- }
96
-
97
- # Callbacks for WhatsApp notifications
98
- after_create :send_order_confirmation
99
- after_update :send_status_update, if: :saved_change_to_status?
100
-
101
- # Instance methods
102
-
103
- def send_order_confirmation
104
- return unless user.can_receive_whatsapp?
105
-
106
- SendOrderConfirmationJob.perform_later(user, self)
107
- end
108
-
109
- def send_status_update
110
- return unless user.can_receive_whatsapp?
111
-
112
- # Don't send updates for pending status (already sent confirmation)
113
- return if status == 'pending'
114
-
115
- SendOrderStatusUpdateJob.perform_later(user, self)
116
- end
117
-
118
- def total_amount_in_cents
119
- (total_amount * 100).to_i
120
- end
121
-
122
- def formatted_total
123
- "$#{'%.2f' % total_amount}"
124
- end
125
-
126
- def estimated_delivery_date
127
- return nil unless shipped?
128
-
129
- shipped_at + 3.days # Example: 3 days for delivery
130
- end
131
- end
132
-
133
- # Example WhatsAppMessage model for tracking sent messages
134
- class WhatsappMessage < ApplicationRecord
135
- belongs_to :user
136
- belongs_to :messageable, polymorphic: true, optional: true # Could be Order, User, etc.
137
-
138
- # Message types
139
- enum message_type: {
140
- welcome: 0,
141
- order_confirmation: 1,
142
- order_status_update: 2,
143
- phone_verification: 3,
144
- general_notification: 4,
145
- marketing: 5,
146
- support: 6
147
- }
148
-
149
- # Message status from WhatsApp API
150
- enum status: {
151
- sent: 0,
152
- delivered: 1,
153
- read: 2,
154
- failed: 3
155
- }
156
-
157
- # Validations
158
- validates :message_id, presence: true, uniqueness: true
159
- validates :phone_number, presence: true
160
- validates :message_type, presence: true
161
-
162
- # Scopes
163
- scope :recent, -> { order(created_at: :desc) }
164
- scope :successful, -> { where(status: [:sent, :delivered, :read]) }
165
- scope :failed, -> { where(status: :failed) }
166
-
167
- # Class methods
168
-
169
- def self.track_message(user:, message_id:, message_type:, phone_number:, messageable: nil)
170
- create!(
171
- user: user,
172
- message_id: message_id,
173
- message_type: message_type,
174
- phone_number: phone_number,
175
- messageable: messageable,
176
- status: :sent,
177
- sent_at: Time.current
178
- )
179
- end
180
-
181
- def self.update_status_from_webhook(message_id, new_status, timestamp = nil)
182
- message = find_by(message_id: message_id)
183
- return unless message
184
-
185
- status_mapping = {
186
- 'sent' => :sent,
187
- 'delivered' => :delivered,
188
- 'read' => :read,
189
- 'failed' => :failed
190
- }
191
-
192
- mapped_status = status_mapping[new_status.to_s.downcase]
193
- return unless mapped_status
194
-
195
- message.update!(
196
- status: mapped_status,
197
- status_updated_at: timestamp ? Time.at(timestamp) : Time.current
198
- )
199
- end
200
-
201
- # Instance methods
202
-
203
- def delivered?
204
- %w[delivered read].include?(status)
205
- end
206
-
207
- def failed?
208
- status == 'failed'
209
- end
210
-
211
- def delivery_time
212
- return nil unless delivered? && sent_at.present? && status_updated_at.present?
213
-
214
- status_updated_at - sent_at
215
- end
216
- end
217
-
218
- # Example migration for WhatsApp messages tracking
219
- #
220
- # class CreateWhatsappMessages < ActiveRecord::Migration[8.0]
221
- # def change
222
- # create_table :whatsapp_messages do |t|
223
- # t.references :user, null: false, foreign_key: true
224
- # t.references :messageable, polymorphic: true, null: true
225
- # t.string :message_id, null: false, index: { unique: true }
226
- # t.string :phone_number, null: false
227
- # t.integer :message_type, null: false
228
- # t.integer :status, default: 0
229
- # t.datetime :sent_at
230
- # t.datetime :status_updated_at
231
- # t.text :error_message
232
- # t.json :metadata # Store additional message data
233
- #
234
- # t.timestamps
235
- # end
236
- #
237
- # add_index :whatsapp_messages, [:user_id, :message_type]
238
- # add_index :whatsapp_messages, [:message_type, :status]
239
- # end
1
+ # frozen_string_literal: true
2
+
3
+ # Example Rails model integration showing WhatsApp messaging hooks
4
+ class User < ApplicationRecord
5
+ has_many :orders, dependent: :destroy
6
+ has_many :whatsapp_messages, dependent: :destroy
7
+
8
+ # Validations
9
+ validates :email, presence: true, uniqueness: true
10
+ validates :phone_number, format: { with: /\A\+\d{10,15}\z/, message: "must be in E.164 format" }, allow_blank: true
11
+ validates :preferred_language, inclusion: { in: %w[en es fr], message: "must be a supported language" }
12
+
13
+ # Callbacks for WhatsApp integration
14
+ after_create :send_welcome_message, if: :phone_number?
15
+ after_update :send_phone_verification, if: :phone_number_changed?
16
+
17
+ # Scopes
18
+ scope :with_phone_number, -> { where.not(phone_number: nil) }
19
+ scope :opted_in_for_notifications, -> { where(notifications_enabled: true) }
20
+
21
+ # Instance methods
22
+
23
+ def send_welcome_message
24
+ SendWelcomeMessageJob.perform_later(self)
25
+ end
26
+
27
+ def send_phone_verification
28
+ return unless phone_number.present?
29
+
30
+ SendPhoneVerificationJob.perform_later(self)
31
+ end
32
+
33
+ def send_notification(message, type: 'general')
34
+ return unless phone_number.present? && notifications_enabled?
35
+
36
+ SendNotificationJob.perform_later(self, message, type)
37
+ end
38
+
39
+ def send_order_confirmation(order)
40
+ return unless phone_number.present?
41
+
42
+ SendOrderConfirmationJob.perform_later(self, order)
43
+ end
44
+
45
+ # Format phone number for WhatsApp (ensure E.164 format)
46
+ def formatted_phone_number
47
+ return nil unless phone_number.present?
48
+
49
+ # Remove any non-digit characters except the leading +
50
+ cleaned = phone_number.gsub(/[^\d+]/, '')
51
+
52
+ # Ensure it starts with +
53
+ cleaned.start_with?('+') ? cleaned : "+#{cleaned}"
54
+ end
55
+
56
+ # Check if user can receive WhatsApp messages
57
+ def can_receive_whatsapp?
58
+ phone_number.present? && notifications_enabled?
59
+ end
60
+
61
+ # Get recent WhatsApp messages
62
+ def recent_whatsapp_messages(limit = 10)
63
+ whatsapp_messages.order(created_at: :desc).limit(limit)
64
+ end
65
+
66
+ # Check if user has received a specific message type recently
67
+ def received_message_type_recently?(message_type, within: 24.hours)
68
+ whatsapp_messages
69
+ .where(message_type: message_type)
70
+ .where('created_at > ?', within.ago)
71
+ .exists?
72
+ end
73
+
74
+ private
75
+
76
+ def phone_number_changed?
77
+ saved_change_to_phone_number? && phone_number.present?
78
+ end
79
+ end
80
+
81
+ # Example Order model with WhatsApp integration
82
+ class Order < ApplicationRecord
83
+ belongs_to :user
84
+ has_many :order_items, dependent: :destroy
85
+ has_many :whatsapp_messages, through: :user
86
+
87
+ # Order statuses
88
+ enum status: {
89
+ pending: 0,
90
+ confirmed: 1,
91
+ processing: 2,
92
+ shipped: 3,
93
+ delivered: 4,
94
+ cancelled: 5
95
+ }
96
+
97
+ # Callbacks for WhatsApp notifications
98
+ after_create :send_order_confirmation
99
+ after_update :send_status_update, if: :saved_change_to_status?
100
+
101
+ # Instance methods
102
+
103
+ def send_order_confirmation
104
+ return unless user.can_receive_whatsapp?
105
+
106
+ SendOrderConfirmationJob.perform_later(user, self)
107
+ end
108
+
109
+ def send_status_update
110
+ return unless user.can_receive_whatsapp?
111
+
112
+ # Don't send updates for pending status (already sent confirmation)
113
+ return if status == 'pending'
114
+
115
+ SendOrderStatusUpdateJob.perform_later(user, self)
116
+ end
117
+
118
+ def total_amount_in_cents
119
+ (total_amount * 100).to_i
120
+ end
121
+
122
+ def formatted_total
123
+ "$#{'%.2f' % total_amount}"
124
+ end
125
+
126
+ def estimated_delivery_date
127
+ return nil unless shipped?
128
+
129
+ shipped_at + 3.days # Example: 3 days for delivery
130
+ end
131
+ end
132
+
133
+ # Example WhatsAppMessage model for tracking sent messages
134
+ class WhatsappMessage < ApplicationRecord
135
+ belongs_to :user
136
+ belongs_to :messageable, polymorphic: true, optional: true # Could be Order, User, etc.
137
+
138
+ # Message types
139
+ enum message_type: {
140
+ welcome: 0,
141
+ order_confirmation: 1,
142
+ order_status_update: 2,
143
+ phone_verification: 3,
144
+ general_notification: 4,
145
+ marketing: 5,
146
+ support: 6
147
+ }
148
+
149
+ # Message status from WhatsApp API
150
+ enum status: {
151
+ sent: 0,
152
+ delivered: 1,
153
+ read: 2,
154
+ failed: 3
155
+ }
156
+
157
+ # Validations
158
+ validates :message_id, presence: true, uniqueness: true
159
+ validates :phone_number, presence: true
160
+ validates :message_type, presence: true
161
+
162
+ # Scopes
163
+ scope :recent, -> { order(created_at: :desc) }
164
+ scope :successful, -> { where(status: [:sent, :delivered, :read]) }
165
+ scope :failed, -> { where(status: :failed) }
166
+
167
+ # Class methods
168
+
169
+ def self.track_message(user:, message_id:, message_type:, phone_number:, messageable: nil)
170
+ create!(
171
+ user: user,
172
+ message_id: message_id,
173
+ message_type: message_type,
174
+ phone_number: phone_number,
175
+ messageable: messageable,
176
+ status: :sent,
177
+ sent_at: Time.current
178
+ )
179
+ end
180
+
181
+ def self.update_status_from_webhook(message_id, new_status, timestamp = nil)
182
+ message = find_by(message_id: message_id)
183
+ return unless message
184
+
185
+ status_mapping = {
186
+ 'sent' => :sent,
187
+ 'delivered' => :delivered,
188
+ 'read' => :read,
189
+ 'failed' => :failed
190
+ }
191
+
192
+ mapped_status = status_mapping[new_status.to_s.downcase]
193
+ return unless mapped_status
194
+
195
+ message.update!(
196
+ status: mapped_status,
197
+ status_updated_at: timestamp ? Time.at(timestamp) : Time.current
198
+ )
199
+ end
200
+
201
+ # Instance methods
202
+
203
+ def delivered?
204
+ %w[delivered read].include?(status)
205
+ end
206
+
207
+ def failed?
208
+ status == 'failed'
209
+ end
210
+
211
+ def delivery_time
212
+ return nil unless delivered? && sent_at.present? && status_updated_at.present?
213
+
214
+ status_updated_at - sent_at
215
+ end
216
+ end
217
+
218
+ # Example migration for WhatsApp messages tracking
219
+ #
220
+ # class CreateWhatsappMessages < ActiveRecord::Migration[8.0]
221
+ # def change
222
+ # create_table :whatsapp_messages do |t|
223
+ # t.references :user, null: false, foreign_key: true
224
+ # t.references :messageable, polymorphic: true, null: true
225
+ # t.string :message_id, null: false, index: { unique: true }
226
+ # t.string :phone_number, null: false
227
+ # t.integer :message_type, null: false
228
+ # t.integer :status, default: 0
229
+ # t.datetime :sent_at
230
+ # t.datetime :status_updated_at
231
+ # t.text :error_message
232
+ # t.json :metadata # Store additional message data
233
+ #
234
+ # t.timestamps
235
+ # end
236
+ #
237
+ # add_index :whatsapp_messages, [:user_id, :message_type]
238
+ # add_index :whatsapp_messages, [:message_type, :status]
239
+ # end
240
240
  # end