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.
- checksums.yaml +4 -4
- data/.rubocop.yml +81 -81
- data/CHANGELOG.md +262 -91
- data/Gemfile +20 -20
- data/RAILS_INTEGRATION.md +477 -477
- data/README.md +1053 -752
- data/Rakefile +40 -40
- data/TEMPLATE_TOOLS_GUIDE.md +120 -120
- data/WHATSAPP_24_HOUR_GUIDE.md +133 -133
- data/examples/advanced_features.rb +352 -349
- data/examples/advanced_messaging.rb +241 -0
- data/examples/basic_messaging.rb +139 -136
- data/examples/enhanced_interactive.rb +400 -0
- data/examples/flows_usage.rb +307 -0
- data/examples/interactive_messages.rb +343 -0
- data/examples/media_management.rb +256 -253
- data/examples/rails/jobs.rb +387 -387
- data/examples/rails/models.rb +239 -239
- data/examples/rails/notifications_controller.rb +226 -226
- data/examples/template_management.rb +393 -390
- data/kapso-ruby-logo.jpg +0 -0
- data/lib/kapso_client_ruby/client.rb +321 -316
- data/lib/kapso_client_ruby/errors.rb +348 -329
- data/lib/kapso_client_ruby/rails/generators/install_generator.rb +75 -75
- data/lib/kapso_client_ruby/rails/generators/templates/env.erb +20 -20
- data/lib/kapso_client_ruby/rails/generators/templates/initializer.rb.erb +32 -32
- data/lib/kapso_client_ruby/rails/generators/templates/message_service.rb.erb +137 -137
- data/lib/kapso_client_ruby/rails/generators/templates/webhook_controller.rb.erb +61 -61
- data/lib/kapso_client_ruby/rails/railtie.rb +54 -54
- data/lib/kapso_client_ruby/rails/service.rb +188 -188
- data/lib/kapso_client_ruby/rails/tasks.rake +166 -166
- data/lib/kapso_client_ruby/resources/calls.rb +172 -172
- data/lib/kapso_client_ruby/resources/contacts.rb +190 -190
- data/lib/kapso_client_ruby/resources/conversations.rb +103 -103
- data/lib/kapso_client_ruby/resources/flows.rb +382 -0
- data/lib/kapso_client_ruby/resources/media.rb +205 -205
- data/lib/kapso_client_ruby/resources/messages.rb +760 -380
- data/lib/kapso_client_ruby/resources/phone_numbers.rb +85 -85
- data/lib/kapso_client_ruby/resources/templates.rb +283 -283
- data/lib/kapso_client_ruby/types.rb +348 -262
- data/lib/kapso_client_ruby/version.rb +5 -5
- data/lib/kapso_client_ruby.rb +75 -74
- data/scripts/.env.example +17 -17
- data/scripts/kapso_template_finder.rb +91 -91
- data/scripts/sdk_setup.rb +404 -404
- data/scripts/test.rb +60 -60
- metadata +12 -3
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# WhatsApp Enhanced Interactive Messages Examples
|
|
4
|
+
# Demonstrates updated button headers and list validations
|
|
5
|
+
|
|
6
|
+
require 'kapso-client-ruby'
|
|
7
|
+
require 'dotenv'
|
|
8
|
+
|
|
9
|
+
Dotenv.load
|
|
10
|
+
|
|
11
|
+
# Initialize client
|
|
12
|
+
client = KapsoClientRuby::Client.new(
|
|
13
|
+
access_token: ENV['WHATSAPP_ACCESS_TOKEN']
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
phone_number_id = ENV['PHONE_NUMBER_ID']
|
|
17
|
+
recipient = '+1234567890'
|
|
18
|
+
|
|
19
|
+
puts "=== Enhanced Interactive Messages ===\n\n"
|
|
20
|
+
|
|
21
|
+
# 1. INTERACTIVE BUTTONS - Text Header
|
|
22
|
+
|
|
23
|
+
puts "1. Buttons with text header..."
|
|
24
|
+
|
|
25
|
+
begin
|
|
26
|
+
response = client.messages.send_interactive_buttons(
|
|
27
|
+
phone_number_id: phone_number_id,
|
|
28
|
+
to: recipient,
|
|
29
|
+
header: {
|
|
30
|
+
type: 'text',
|
|
31
|
+
text: 'Appointment Confirmation'
|
|
32
|
+
},
|
|
33
|
+
body_text: 'We have an appointment slot available for you tomorrow at 2:00 PM.',
|
|
34
|
+
buttons: [
|
|
35
|
+
{ type: 'reply', reply: { id: 'confirm', title: 'Confirm' } },
|
|
36
|
+
{ type: 'reply', reply: { id: 'reschedule', title: 'Reschedule' } },
|
|
37
|
+
{ type: 'reply', reply: { id: 'cancel', title: 'Cancel' } }
|
|
38
|
+
],
|
|
39
|
+
footer: 'Please respond within 24 hours'
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
puts "✓ Sent! ID: #{response.messages.first.id}\n\n"
|
|
43
|
+
rescue StandardError => e
|
|
44
|
+
puts "✗ Error: #{e.message}\n\n"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# 2. INTERACTIVE BUTTONS - Image Header (NEW!)
|
|
48
|
+
|
|
49
|
+
puts "2. Buttons with image header (NEW FEATURE)..."
|
|
50
|
+
|
|
51
|
+
begin
|
|
52
|
+
response = client.messages.send_interactive_buttons(
|
|
53
|
+
phone_number_id: phone_number_id,
|
|
54
|
+
to: recipient,
|
|
55
|
+
header: {
|
|
56
|
+
type: 'image',
|
|
57
|
+
image: {
|
|
58
|
+
link: 'https://example.com/product-image.jpg'
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
body_text: 'Interested in this product? Choose an option below:',
|
|
62
|
+
buttons: [
|
|
63
|
+
{ type: 'reply', reply: { id: 'buy_now', title: 'Buy Now' } },
|
|
64
|
+
{ type: 'reply', reply: { id: 'add_cart', title: 'Add to Cart' } },
|
|
65
|
+
{ type: 'reply', reply: { id: 'more_info', title: 'More Info' } }
|
|
66
|
+
],
|
|
67
|
+
footer: '30-day money-back guarantee'
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
puts "✓ Sent! ID: #{response.messages.first.id}\n\n"
|
|
71
|
+
rescue StandardError => e
|
|
72
|
+
puts "✗ Error: #{e.message}\n\n"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# 3. INTERACTIVE BUTTONS - Video Header (NEW!)
|
|
76
|
+
|
|
77
|
+
puts "3. Buttons with video header (NEW FEATURE)..."
|
|
78
|
+
|
|
79
|
+
begin
|
|
80
|
+
response = client.messages.send_interactive_buttons(
|
|
81
|
+
phone_number_id: phone_number_id,
|
|
82
|
+
to: recipient,
|
|
83
|
+
header: {
|
|
84
|
+
type: 'video',
|
|
85
|
+
video: {
|
|
86
|
+
link: 'https://example.com/tutorial.mp4'
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
body_text: 'Watch this quick tutorial. Was it helpful?',
|
|
90
|
+
buttons: [
|
|
91
|
+
{ type: 'reply', reply: { id: 'yes', title: 'Yes, helpful' } },
|
|
92
|
+
{ type: 'reply', reply: { id: 'no', title: 'Not helpful' } }
|
|
93
|
+
]
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
puts "✓ Sent! ID: #{response.messages.first.id}\n\n"
|
|
97
|
+
rescue StandardError => e
|
|
98
|
+
puts "✗ Error: #{e.message}\n\n"
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# 4. INTERACTIVE BUTTONS - Document Header (NEW!)
|
|
102
|
+
|
|
103
|
+
puts "4. Buttons with document header (NEW FEATURE)..."
|
|
104
|
+
|
|
105
|
+
begin
|
|
106
|
+
response = client.messages.send_interactive_buttons(
|
|
107
|
+
phone_number_id: phone_number_id,
|
|
108
|
+
to: recipient,
|
|
109
|
+
header: {
|
|
110
|
+
type: 'document',
|
|
111
|
+
document: {
|
|
112
|
+
link: 'https://example.com/invoice-12345.pdf',
|
|
113
|
+
filename: 'invoice.pdf'
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
body_text: 'Your invoice is ready. Please review and confirm:',
|
|
117
|
+
buttons: [
|
|
118
|
+
{ type: 'reply', reply: { id: 'approve', title: 'Approve' } },
|
|
119
|
+
{ type: 'reply', reply: { id: 'question', title: 'Question' } }
|
|
120
|
+
],
|
|
121
|
+
footer: 'Invoice #12345'
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
puts "✓ Sent! ID: #{response.messages.first.id}\n\n"
|
|
125
|
+
rescue StandardError => e
|
|
126
|
+
puts "✗ Error: #{e.message}\n\n"
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# 5. INTERACTIVE LIST - Extended Body Text
|
|
130
|
+
|
|
131
|
+
puts "5. List with extended body text (4096 chars)..."
|
|
132
|
+
|
|
133
|
+
long_body = "Welcome to our service menu! " + ("Here's what we offer. " * 100)
|
|
134
|
+
long_body = long_body[0...4000] # Keep under 4096
|
|
135
|
+
|
|
136
|
+
begin
|
|
137
|
+
response = client.messages.send_interactive_list(
|
|
138
|
+
phone_number_id: phone_number_id,
|
|
139
|
+
to: recipient,
|
|
140
|
+
header: {
|
|
141
|
+
type: 'text',
|
|
142
|
+
text: 'Our Services'
|
|
143
|
+
},
|
|
144
|
+
body_text: long_body,
|
|
145
|
+
button_text: 'View Services',
|
|
146
|
+
sections: [
|
|
147
|
+
{
|
|
148
|
+
title: 'Popular Services',
|
|
149
|
+
rows: [
|
|
150
|
+
{ id: 'service1', title: 'Web Design', description: 'Custom website design' },
|
|
151
|
+
{ id: 'service2', title: 'Mobile Apps', description: 'iOS and Android apps' },
|
|
152
|
+
{ id: 'service3', title: 'SEO', description: 'Search engine optimization' }
|
|
153
|
+
]
|
|
154
|
+
}
|
|
155
|
+
],
|
|
156
|
+
footer: 'Select a service for details'
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
puts "✓ Sent! ID: #{response.messages.first.id}\n\n"
|
|
160
|
+
rescue StandardError => e
|
|
161
|
+
puts "✗ Error: #{e.message}\n\n"
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# 6. INTERACTIVE LIST - Maximum Rows (10)
|
|
165
|
+
|
|
166
|
+
puts "6. List with maximum rows (10 total)..."
|
|
167
|
+
|
|
168
|
+
begin
|
|
169
|
+
response = client.messages.send_interactive_list(
|
|
170
|
+
phone_number_id: phone_number_id,
|
|
171
|
+
to: recipient,
|
|
172
|
+
body_text: 'Choose from our top 10 products:',
|
|
173
|
+
button_text: 'View Products',
|
|
174
|
+
sections: [
|
|
175
|
+
{
|
|
176
|
+
title: 'Electronics',
|
|
177
|
+
rows: [
|
|
178
|
+
{ id: 'p1', title: 'Laptop', description: 'High-performance laptop' },
|
|
179
|
+
{ id: 'p2', title: 'Phone', description: 'Latest smartphone' },
|
|
180
|
+
{ id: 'p3', title: 'Tablet', description: 'Portable tablet' },
|
|
181
|
+
{ id: 'p4', title: 'Watch', description: 'Smart watch' },
|
|
182
|
+
{ id: 'p5', title: 'Earbuds', description: 'Wireless earbuds' }
|
|
183
|
+
]
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
title: 'Accessories',
|
|
187
|
+
rows: [
|
|
188
|
+
{ id: 'a1', title: 'Case', description: 'Protective case' },
|
|
189
|
+
{ id: 'a2', title: 'Charger', description: 'Fast charger' },
|
|
190
|
+
{ id: 'a3', title: 'Cable', description: 'USB-C cable' },
|
|
191
|
+
{ id: 'a4', title: 'Stand', description: 'Phone stand' },
|
|
192
|
+
{ id: 'a5', title: 'Adapter', description: 'Power adapter' }
|
|
193
|
+
]
|
|
194
|
+
}
|
|
195
|
+
]
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
puts "✓ Sent with 10 rows! ID: #{response.messages.first.id}\n\n"
|
|
199
|
+
rescue StandardError => e
|
|
200
|
+
puts "✗ Error: #{e.message}\n\n"
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# 7. VALIDATION TESTS
|
|
204
|
+
|
|
205
|
+
puts "7. Testing enhanced validations...\n"
|
|
206
|
+
|
|
207
|
+
# Test button count validation (max 3)
|
|
208
|
+
puts " Testing button count (max 3)..."
|
|
209
|
+
begin
|
|
210
|
+
client.messages.send_interactive_buttons(
|
|
211
|
+
phone_number_id: phone_number_id,
|
|
212
|
+
to: recipient,
|
|
213
|
+
body_text: 'Test',
|
|
214
|
+
buttons: [
|
|
215
|
+
{ type: 'reply', reply: { id: '1', title: 'Button 1' } },
|
|
216
|
+
{ type: 'reply', reply: { id: '2', title: 'Button 2' } },
|
|
217
|
+
{ type: 'reply', reply: { id: '3', title: 'Button 3' } },
|
|
218
|
+
{ type: 'reply', reply: { id: '4', title: 'Button 4' } } # Too many!
|
|
219
|
+
]
|
|
220
|
+
)
|
|
221
|
+
puts " ✗ Validation failed\n"
|
|
222
|
+
rescue ArgumentError => e
|
|
223
|
+
puts " ✓ Validation passed: #{e.message}\n"
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# Test list body text limit (4096)
|
|
227
|
+
puts " Testing list body text (max 4096)..."
|
|
228
|
+
begin
|
|
229
|
+
long_text = 'a' * 4097 # Exceeds limit
|
|
230
|
+
client.messages.send_interactive_list(
|
|
231
|
+
phone_number_id: phone_number_id,
|
|
232
|
+
to: recipient,
|
|
233
|
+
body_text: long_text,
|
|
234
|
+
button_text: 'View',
|
|
235
|
+
sections: [{ title: 'Test', rows: [{ id: '1', title: 'Item' }] }]
|
|
236
|
+
)
|
|
237
|
+
puts " ✗ Validation failed\n"
|
|
238
|
+
rescue ArgumentError => e
|
|
239
|
+
puts " ✓ Validation passed: #{e.message}\n"
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# Test list row count (max 10)
|
|
243
|
+
puts " Testing list row count (max 10)..."
|
|
244
|
+
begin
|
|
245
|
+
too_many_rows = (1..11).map { |i| { id: "item#{i}", title: "Item #{i}" } }
|
|
246
|
+
client.messages.send_interactive_list(
|
|
247
|
+
phone_number_id: phone_number_id,
|
|
248
|
+
to: recipient,
|
|
249
|
+
body_text: 'Select',
|
|
250
|
+
button_text: 'View',
|
|
251
|
+
sections: [{ title: 'Items', rows: too_many_rows }]
|
|
252
|
+
)
|
|
253
|
+
puts " ✗ Validation failed\n"
|
|
254
|
+
rescue ArgumentError => e
|
|
255
|
+
puts " ✓ Validation passed: #{e.message}\n"
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# Test list header type (text only)
|
|
259
|
+
puts " Testing list header type (text only)..."
|
|
260
|
+
begin
|
|
261
|
+
client.messages.send_interactive_list(
|
|
262
|
+
phone_number_id: phone_number_id,
|
|
263
|
+
to: recipient,
|
|
264
|
+
header: {
|
|
265
|
+
type: 'image', # Lists only support text headers!
|
|
266
|
+
image: { link: 'https://example.com/image.jpg' }
|
|
267
|
+
},
|
|
268
|
+
body_text: 'Test',
|
|
269
|
+
button_text: 'View',
|
|
270
|
+
sections: [{ title: 'Test', rows: [{ id: '1', title: 'Item' }] }]
|
|
271
|
+
)
|
|
272
|
+
puts " ✗ Validation failed\n"
|
|
273
|
+
rescue ArgumentError => e
|
|
274
|
+
puts " ✓ Validation passed: #{e.message}\n"
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
# Test button header with invalid media
|
|
278
|
+
puts " Testing button header validation..."
|
|
279
|
+
begin
|
|
280
|
+
client.messages.send_interactive_buttons(
|
|
281
|
+
phone_number_id: phone_number_id,
|
|
282
|
+
to: recipient,
|
|
283
|
+
header: {
|
|
284
|
+
type: 'image'
|
|
285
|
+
# Missing image field!
|
|
286
|
+
},
|
|
287
|
+
body_text: 'Test',
|
|
288
|
+
buttons: [{ type: 'reply', reply: { id: '1', title: 'OK' } }]
|
|
289
|
+
)
|
|
290
|
+
puts " ✗ Validation failed\n"
|
|
291
|
+
rescue ArgumentError => e
|
|
292
|
+
puts " ✓ Validation passed: #{e.message}\n"
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
puts "\n=== All enhanced features demonstrated! ===\n"
|
|
296
|
+
|
|
297
|
+
# USE CASES
|
|
298
|
+
|
|
299
|
+
puts "\n=== Real-World Use Cases ===\n"
|
|
300
|
+
|
|
301
|
+
# E-commerce: Product selection with image
|
|
302
|
+
def product_quick_buy(client, phone_number_id, customer_phone)
|
|
303
|
+
client.messages.send_interactive_buttons(
|
|
304
|
+
phone_number_id: phone_number_id,
|
|
305
|
+
to: customer_phone,
|
|
306
|
+
header: {
|
|
307
|
+
type: 'image',
|
|
308
|
+
image: { link: 'https://shop.example.com/featured-product.jpg' }
|
|
309
|
+
},
|
|
310
|
+
body_text: '🔥 Flash Sale! This item is 50% off for the next 2 hours.',
|
|
311
|
+
buttons: [
|
|
312
|
+
{ type: 'reply', reply: { id: 'buy', title: 'Buy Now' } },
|
|
313
|
+
{ type: 'reply', reply: { id: 'cart', title: 'Add to Cart' } },
|
|
314
|
+
{ type: 'reply', reply: { id: 'notify', title: 'Notify Later' } }
|
|
315
|
+
],
|
|
316
|
+
footer: 'Limited stock available'
|
|
317
|
+
)
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
# Customer Support: Video tutorial with feedback
|
|
321
|
+
def send_tutorial_with_feedback(client, phone_number_id, customer_phone)
|
|
322
|
+
client.messages.send_interactive_buttons(
|
|
323
|
+
phone_number_id: phone_number_id,
|
|
324
|
+
to: customer_phone,
|
|
325
|
+
header: {
|
|
326
|
+
type: 'video',
|
|
327
|
+
video: { link: 'https://support.example.com/tutorials/setup.mp4' }
|
|
328
|
+
},
|
|
329
|
+
body_text: '📺 Here\'s a quick video showing how to set up your device. Did this help?',
|
|
330
|
+
buttons: [
|
|
331
|
+
{ type: 'reply', reply: { id: 'solved', title: 'Problem Solved' } },
|
|
332
|
+
{ type: 'reply', reply: { id: 'more_help', title: 'Need More Help' } }
|
|
333
|
+
],
|
|
334
|
+
footer: 'Support Team'
|
|
335
|
+
)
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
# Document approval workflow
|
|
339
|
+
def document_approval(client, phone_number_id, manager_phone)
|
|
340
|
+
client.messages.send_interactive_buttons(
|
|
341
|
+
phone_number_id: phone_number_id,
|
|
342
|
+
to: manager_phone,
|
|
343
|
+
header: {
|
|
344
|
+
type: 'document',
|
|
345
|
+
document: {
|
|
346
|
+
link: 'https://docs.example.com/contracts/2024-Q1.pdf',
|
|
347
|
+
filename: 'Q1-2024-Contract.pdf'
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
body_text: '📄 Contract requires your approval. Please review the attached document.',
|
|
351
|
+
buttons: [
|
|
352
|
+
{ type: 'reply', reply: { id: 'approve', title: 'Approve' } },
|
|
353
|
+
{ type: 'reply', reply: { id: 'reject', title: 'Reject' } },
|
|
354
|
+
{ type: 'reply', reply: { id: 'clarify', title: 'Ask Question' } }
|
|
355
|
+
],
|
|
356
|
+
footer: 'Deadline: Friday 5 PM'
|
|
357
|
+
)
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
# Comprehensive service menu (using max rows)
|
|
361
|
+
def comprehensive_service_menu(client, phone_number_id, customer_phone)
|
|
362
|
+
client.messages.send_interactive_list(
|
|
363
|
+
phone_number_id: phone_number_id,
|
|
364
|
+
to: customer_phone,
|
|
365
|
+
header: {
|
|
366
|
+
type: 'text',
|
|
367
|
+
text: 'Complete Service Catalog'
|
|
368
|
+
},
|
|
369
|
+
body_text: 'Browse our full catalog of services. We offer professional solutions across multiple categories to meet all your business needs.',
|
|
370
|
+
button_text: 'Browse Services',
|
|
371
|
+
sections: [
|
|
372
|
+
{
|
|
373
|
+
title: 'Design Services',
|
|
374
|
+
rows: [
|
|
375
|
+
{ id: 'web_design', title: 'Web Design', description: 'Custom website design' },
|
|
376
|
+
{ id: 'brand_design', title: 'Brand Design', description: 'Logo and branding' },
|
|
377
|
+
{ id: 'ui_design', title: 'UI/UX Design', description: 'User interface design' }
|
|
378
|
+
]
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
title: 'Development Services',
|
|
382
|
+
rows: [
|
|
383
|
+
{ id: 'web_dev', title: 'Web Development', description: 'Full-stack development' },
|
|
384
|
+
{ id: 'mobile_dev', title: 'Mobile Apps', description: 'iOS & Android apps' },
|
|
385
|
+
{ id: 'api_dev', title: 'API Development', description: 'Backend APIs' }
|
|
386
|
+
]
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
title: 'Marketing Services',
|
|
390
|
+
rows: [
|
|
391
|
+
{ id: 'seo', title: 'SEO', description: 'Search optimization' },
|
|
392
|
+
{ id: 'social', title: 'Social Media', description: 'Social media marketing' },
|
|
393
|
+
{ id: 'email', title: 'Email Marketing', description: 'Email campaigns' },
|
|
394
|
+
{ id: 'content', title: 'Content Marketing', description: 'Content creation' }
|
|
395
|
+
]
|
|
396
|
+
}
|
|
397
|
+
],
|
|
398
|
+
footer: 'Get a free consultation'
|
|
399
|
+
)
|
|
400
|
+
end
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# WhatsApp Flows API Examples
|
|
4
|
+
# Demonstrates Flow creation, deployment, messaging, and webhook handling
|
|
5
|
+
|
|
6
|
+
require 'kapso-client-ruby'
|
|
7
|
+
require 'dotenv'
|
|
8
|
+
|
|
9
|
+
Dotenv.load
|
|
10
|
+
|
|
11
|
+
# Initialize client
|
|
12
|
+
client = KapsoClientRuby::Client.new(
|
|
13
|
+
access_token: ENV['WHATSAPP_ACCESS_TOKEN']
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
business_account_id = ENV['WHATSAPP_BUSINESS_ACCOUNT_ID']
|
|
17
|
+
phone_number_id = ENV['PHONE_NUMBER_ID']
|
|
18
|
+
|
|
19
|
+
# 1. CREATE A FLOW
|
|
20
|
+
|
|
21
|
+
puts "Creating a new Flow..."
|
|
22
|
+
|
|
23
|
+
flow = client.flows.create(
|
|
24
|
+
business_account_id: business_account_id,
|
|
25
|
+
name: 'appointment_booking',
|
|
26
|
+
categories: ['APPOINTMENT_BOOKING'],
|
|
27
|
+
endpoint_uri: 'https://your-server.com/whatsapp/flows'
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
puts "Flow created: #{flow['id']}"
|
|
31
|
+
flow_id = flow['id']
|
|
32
|
+
|
|
33
|
+
# 2. UPDATE FLOW ASSET (JSON Definition)
|
|
34
|
+
|
|
35
|
+
puts "Updating Flow JSON..."
|
|
36
|
+
|
|
37
|
+
flow_json = {
|
|
38
|
+
version: '3.0',
|
|
39
|
+
screens: [
|
|
40
|
+
{
|
|
41
|
+
id: 'APPOINTMENT_DETAILS',
|
|
42
|
+
title: 'Book Appointment',
|
|
43
|
+
data: {},
|
|
44
|
+
layout: {
|
|
45
|
+
type: 'SingleColumnLayout',
|
|
46
|
+
children: [
|
|
47
|
+
{
|
|
48
|
+
type: 'Form',
|
|
49
|
+
name: 'appointment_form',
|
|
50
|
+
children: [
|
|
51
|
+
{
|
|
52
|
+
type: 'TextInput',
|
|
53
|
+
name: 'customer_name',
|
|
54
|
+
label: 'Full Name',
|
|
55
|
+
required: true
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
type: 'DatePicker',
|
|
59
|
+
name: 'appointment_date',
|
|
60
|
+
label: 'Preferred Date',
|
|
61
|
+
required: true
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
type: 'Dropdown',
|
|
65
|
+
name: 'service_type',
|
|
66
|
+
label: 'Service',
|
|
67
|
+
required: true,
|
|
68
|
+
data_source: ['Haircut', 'Coloring', 'Styling']
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
type: 'Footer',
|
|
72
|
+
label: 'Continue',
|
|
73
|
+
on_click_action: {
|
|
74
|
+
name: 'complete',
|
|
75
|
+
payload: {
|
|
76
|
+
customer_name: '${form.customer_name}',
|
|
77
|
+
appointment_date: '${form.appointment_date}',
|
|
78
|
+
service_type: '${form.service_type}'
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
asset_response = client.flows.update_asset(
|
|
91
|
+
flow_id: flow_id,
|
|
92
|
+
asset: flow_json
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
if asset_response.valid?
|
|
96
|
+
puts "Flow asset updated successfully!"
|
|
97
|
+
else
|
|
98
|
+
puts "Validation errors:"
|
|
99
|
+
asset_response.errors.each { |err| puts " - #{err}" }
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# 3. PUBLISH FLOW
|
|
103
|
+
|
|
104
|
+
puts "Publishing Flow..."
|
|
105
|
+
|
|
106
|
+
client.flows.publish(flow_id: flow_id)
|
|
107
|
+
puts "Flow published!"
|
|
108
|
+
|
|
109
|
+
# 4. GET PREVIEW URL
|
|
110
|
+
|
|
111
|
+
preview = client.flows.preview(flow_id: flow_id)
|
|
112
|
+
puts "Preview URL: #{preview.preview_url}"
|
|
113
|
+
puts "Expires at: #{preview.expires_at}"
|
|
114
|
+
|
|
115
|
+
# 5. SEND FLOW MESSAGE
|
|
116
|
+
|
|
117
|
+
puts "Sending Flow message..."
|
|
118
|
+
|
|
119
|
+
# Generate unique flow token (use UUID or session ID)
|
|
120
|
+
require 'securerandom'
|
|
121
|
+
flow_token = SecureRandom.uuid
|
|
122
|
+
|
|
123
|
+
message_response = client.messages.send_flow(
|
|
124
|
+
phone_number_id: phone_number_id,
|
|
125
|
+
to: '+1234567890',
|
|
126
|
+
flow_id: flow_id,
|
|
127
|
+
flow_cta: 'Book Now',
|
|
128
|
+
flow_token: flow_token,
|
|
129
|
+
header: {
|
|
130
|
+
type: 'text',
|
|
131
|
+
text: 'Appointment Booking'
|
|
132
|
+
},
|
|
133
|
+
body_text: 'Book your appointment in just a few taps!',
|
|
134
|
+
footer_text: 'Available slots fill up fast'
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
puts "Flow message sent! Message ID: #{message_response.messages.first.id}"
|
|
138
|
+
|
|
139
|
+
# 6. IDEMPOTENT DEPLOYMENT
|
|
140
|
+
|
|
141
|
+
puts "\n\nIdempotent deployment example..."
|
|
142
|
+
|
|
143
|
+
# This will create or update and publish the flow
|
|
144
|
+
deployment = client.flows.deploy(
|
|
145
|
+
business_account_id: business_account_id,
|
|
146
|
+
name: 'feedback_form',
|
|
147
|
+
categories: ['SURVEY'],
|
|
148
|
+
flow_json: {
|
|
149
|
+
version: '3.0',
|
|
150
|
+
screens: [
|
|
151
|
+
{
|
|
152
|
+
id: 'FEEDBACK',
|
|
153
|
+
title: 'Feedback',
|
|
154
|
+
data: {},
|
|
155
|
+
layout: {
|
|
156
|
+
type: 'SingleColumnLayout',
|
|
157
|
+
children: [
|
|
158
|
+
{
|
|
159
|
+
type: 'Form',
|
|
160
|
+
name: 'feedback_form',
|
|
161
|
+
children: [
|
|
162
|
+
{
|
|
163
|
+
type: 'TextArea',
|
|
164
|
+
name: 'comments',
|
|
165
|
+
label: 'Your Feedback',
|
|
166
|
+
required: true
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
type: 'RadioButtonsGroup',
|
|
170
|
+
name: 'rating',
|
|
171
|
+
label: 'Rating',
|
|
172
|
+
required: true,
|
|
173
|
+
data_source: ['1 - Poor', '2 - Fair', '3 - Good', '4 - Very Good', '5 - Excellent']
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
type: 'Footer',
|
|
177
|
+
label: 'Submit',
|
|
178
|
+
on_click_action: {
|
|
179
|
+
name: 'complete',
|
|
180
|
+
payload: {
|
|
181
|
+
comments: '${form.comments}',
|
|
182
|
+
rating: '${form.rating}'
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
]
|
|
187
|
+
}
|
|
188
|
+
]
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
]
|
|
192
|
+
}
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
puts "Deployment complete: #{deployment[:message]}"
|
|
196
|
+
puts "Flow ID: #{deployment[:id]}"
|
|
197
|
+
|
|
198
|
+
# 7. LIST ALL FLOWS
|
|
199
|
+
|
|
200
|
+
puts "\n\nListing all Flows..."
|
|
201
|
+
|
|
202
|
+
flows_list = client.flows.list(business_account_id: business_account_id)
|
|
203
|
+
flows_list['data'].each do |flow|
|
|
204
|
+
puts "- #{flow['name']} (#{flow['id']}) - Status: #{flow['status']}"
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# 8. GET FLOW DETAILS
|
|
208
|
+
|
|
209
|
+
flow_details = client.flows.get(
|
|
210
|
+
flow_id: flow_id,
|
|
211
|
+
fields: ['id', 'name', 'status', 'categories', 'endpoint_uri']
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
puts "\n\nFlow Details:"
|
|
215
|
+
puts " Name: #{flow_details.name}"
|
|
216
|
+
puts " Status: #{flow_details.status}"
|
|
217
|
+
puts " Categories: #{flow_details.categories.join(', ')}"
|
|
218
|
+
puts " Endpoint: #{flow_details.endpoint_uri}"
|
|
219
|
+
|
|
220
|
+
# 9. WEBHOOK HANDLING - RECEIVE FLOW EVENT
|
|
221
|
+
|
|
222
|
+
# This would typically be in your webhook endpoint
|
|
223
|
+
def handle_flow_webhook(encrypted_request, client)
|
|
224
|
+
# Load your private key (the public key should be registered with WhatsApp)
|
|
225
|
+
private_key = OpenSSL::PKey::RSA.new(File.read('path/to/private_key.pem'), 'optional_passphrase')
|
|
226
|
+
|
|
227
|
+
# Decrypt the incoming Flow event
|
|
228
|
+
flow_event = client.flows.receive_flow_event(
|
|
229
|
+
encrypted_request: encrypted_request,
|
|
230
|
+
private_key: private_key
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
puts "Flow Event Received:"
|
|
234
|
+
puts " Version: #{flow_event.version}"
|
|
235
|
+
puts " Screen: #{flow_event.screen}"
|
|
236
|
+
puts " Action: #{flow_event.action}"
|
|
237
|
+
puts " Data: #{flow_event.data.inspect}"
|
|
238
|
+
|
|
239
|
+
# Process the form data
|
|
240
|
+
case flow_event.action
|
|
241
|
+
when 'INIT'
|
|
242
|
+
# Flow initialized - return initial screen data
|
|
243
|
+
response_data = {
|
|
244
|
+
version: flow_event.version,
|
|
245
|
+
screen: 'APPOINTMENT_DETAILS',
|
|
246
|
+
data: {
|
|
247
|
+
available_dates: ['2024-01-15', '2024-01-16', '2024-01-17']
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
when 'data_exchange'
|
|
251
|
+
# User submitted form data - process and respond
|
|
252
|
+
customer_name = flow_event.data['customer_name']
|
|
253
|
+
appointment_date = flow_event.data['appointment_date']
|
|
254
|
+
service_type = flow_event.data['service_type']
|
|
255
|
+
|
|
256
|
+
# Save to database, send confirmation, etc.
|
|
257
|
+
puts "Booking appointment for #{customer_name} on #{appointment_date}"
|
|
258
|
+
|
|
259
|
+
response_data = {
|
|
260
|
+
version: flow_event.version,
|
|
261
|
+
screen: 'SUCCESS',
|
|
262
|
+
data: {
|
|
263
|
+
success: true,
|
|
264
|
+
confirmation_number: "APT#{rand(10000..99999)}"
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
else
|
|
268
|
+
response_data = {
|
|
269
|
+
version: flow_event.version,
|
|
270
|
+
error_message: 'Unknown action'
|
|
271
|
+
}
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
# Encrypt and return response
|
|
275
|
+
encrypted_response = client.flows.respond_to_flow(
|
|
276
|
+
response_data: response_data,
|
|
277
|
+
private_key: private_key
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
encrypted_response
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
# 10. DOWNLOAD FLOW MEDIA
|
|
284
|
+
|
|
285
|
+
# If Flow contains media uploads (images, documents, etc.)
|
|
286
|
+
def download_flow_media_example(media_url, client)
|
|
287
|
+
media_content = client.flows.download_flow_media(
|
|
288
|
+
media_url: media_url
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
# Save to file
|
|
292
|
+
File.write('uploaded_document.pdf', media_content)
|
|
293
|
+
puts "Media downloaded and saved"
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
# 11. UPDATE FLOW
|
|
297
|
+
|
|
298
|
+
puts "\n\nUpdating Flow properties..."
|
|
299
|
+
|
|
300
|
+
client.flows.update(
|
|
301
|
+
flow_id: flow_id,
|
|
302
|
+
categories: ['APPOINTMENT_BOOKING', 'CUSTOMER_SUPPORT'],
|
|
303
|
+
endpoint_uri: 'https://new-server.com/whatsapp/flows'
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
puts "Flow updated!"
|
|
307
|
+
puts "\n\nAll Flow examples completed!"
|