kapso-client-ruby 1.0.0 → 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 +478 -0
- data/README.md +1053 -734
- 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 +388 -0
- data/examples/rails/models.rb +240 -0
- data/examples/rails/notifications_controller.rb +227 -0
- 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 +76 -0
- data/lib/kapso_client_ruby/rails/generators/templates/env.erb +21 -0
- data/lib/kapso_client_ruby/rails/generators/templates/initializer.rb.erb +33 -0
- data/lib/kapso_client_ruby/rails/generators/templates/message_service.rb.erb +138 -0
- data/lib/kapso_client_ruby/rails/generators/templates/webhook_controller.rb.erb +62 -0
- data/lib/kapso_client_ruby/rails/railtie.rb +55 -0
- data/lib/kapso_client_ruby/rails/service.rb +189 -0
- data/lib/kapso_client_ruby/rails/tasks.rake +167 -0
- 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 -68
- 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 +24 -3
|
@@ -1,350 +1,353 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require '
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
puts "
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
puts "
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
puts "
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
#
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
puts "
|
|
191
|
-
puts "
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
#
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
puts "
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
end
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
# Test
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
end
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
#
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
#
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'kapso-client-ruby'
|
|
4
|
+
require 'dotenv'
|
|
5
|
+
|
|
6
|
+
Dotenv.load
|
|
7
|
+
|
|
8
|
+
puts "=== Advanced Features with Kapso Proxy ==="
|
|
9
|
+
|
|
10
|
+
# Initialize Kapso client
|
|
11
|
+
kapso_client = KapsoClientRuby::Client.new(
|
|
12
|
+
kapso_api_key: ENV['KAPSO_API_KEY'],
|
|
13
|
+
base_url: 'https://app.kapso.ai/api/meta',
|
|
14
|
+
debug: true
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
phone_number_id = ENV['PHONE_NUMBER_ID']
|
|
18
|
+
|
|
19
|
+
# Example 1: Message History and Analytics
|
|
20
|
+
puts "\n--- Message History ---"
|
|
21
|
+
|
|
22
|
+
begin
|
|
23
|
+
# Query message history
|
|
24
|
+
messages = kapso_client.messages.query(
|
|
25
|
+
phone_number_id: phone_number_id,
|
|
26
|
+
direction: 'inbound',
|
|
27
|
+
since: '2024-01-01T00:00:00Z',
|
|
28
|
+
limit: 10
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
puts "Found #{messages.data.length} messages:"
|
|
32
|
+
messages.data.each do |message|
|
|
33
|
+
puts "- #{message['id']}: #{message['type']} from #{message['from']}"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Get messages by conversation
|
|
37
|
+
if messages.data.any? && messages.data.first['conversation_id']
|
|
38
|
+
conv_messages = kapso_client.messages.list_by_conversation(
|
|
39
|
+
phone_number_id: phone_number_id,
|
|
40
|
+
conversation_id: messages.data.first['conversation_id'],
|
|
41
|
+
limit: 5
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
puts "\nConversation messages: #{conv_messages.data.length}"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
rescue KapsoClientRuby::Errors::KapsoProxyRequiredError => e
|
|
48
|
+
puts "Kapso Proxy required: #{e.message}"
|
|
49
|
+
rescue KapsoClientRuby::Errors::GraphApiError => e
|
|
50
|
+
puts "Message history error: #{e.message}"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Example 2: Conversation Management
|
|
54
|
+
puts "\n--- Conversation Management ---"
|
|
55
|
+
|
|
56
|
+
begin
|
|
57
|
+
# List active conversations
|
|
58
|
+
conversations = kapso_client.conversations.list(
|
|
59
|
+
phone_number_id: phone_number_id,
|
|
60
|
+
status: 'active',
|
|
61
|
+
limit: 10
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
puts "Active conversations: #{conversations.data.length}"
|
|
65
|
+
|
|
66
|
+
conversations.data.each do |conv|
|
|
67
|
+
puts "Conversation #{conv.id}:"
|
|
68
|
+
puts " Phone: #{conv.phone_number}"
|
|
69
|
+
puts " Status: #{conv.status}"
|
|
70
|
+
puts " Last Active: #{conv.last_active_at}"
|
|
71
|
+
|
|
72
|
+
if conv.kapso
|
|
73
|
+
puts " Contact Name: #{conv.kapso['contact_name']}"
|
|
74
|
+
puts " Messages Count: #{conv.kapso['messages_count']}"
|
|
75
|
+
puts " Last Message: #{conv.kapso['last_message_text']}"
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Get specific conversation details
|
|
80
|
+
if conversations.data.any?
|
|
81
|
+
conversation_id = conversations.data.first.id
|
|
82
|
+
|
|
83
|
+
conv_details = kapso_client.conversations.get(
|
|
84
|
+
conversation_id: conversation_id
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
puts "\nDetailed conversation info:"
|
|
88
|
+
puts "ID: #{conv_details.id}"
|
|
89
|
+
puts "Status: #{conv_details.status}"
|
|
90
|
+
puts "Metadata: #{conv_details.metadata}"
|
|
91
|
+
|
|
92
|
+
# Update conversation status
|
|
93
|
+
kapso_client.conversations.update_status(
|
|
94
|
+
conversation_id: conversation_id,
|
|
95
|
+
status: 'archived'
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
puts "Conversation archived successfully"
|
|
99
|
+
|
|
100
|
+
# Unarchive it
|
|
101
|
+
kapso_client.conversations.unarchive(conversation_id: conversation_id)
|
|
102
|
+
puts "Conversation unarchived"
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
rescue KapsoClientRuby::Errors::GraphApiError => e
|
|
106
|
+
puts "Conversation management error: #{e.message}"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Example 3: Contact Management
|
|
110
|
+
puts "\n--- Contact Management ---"
|
|
111
|
+
|
|
112
|
+
begin
|
|
113
|
+
# List contacts
|
|
114
|
+
contacts = kapso_client.contacts.list(
|
|
115
|
+
phone_number_id: phone_number_id,
|
|
116
|
+
limit: 10
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
puts "Found #{contacts.data.length} contacts:"
|
|
120
|
+
|
|
121
|
+
contacts.data.each do |contact|
|
|
122
|
+
puts "Contact #{contact.wa_id}:"
|
|
123
|
+
puts " Phone: #{contact.phone_number}"
|
|
124
|
+
puts " Profile Name: #{contact.profile_name}"
|
|
125
|
+
puts " Metadata: #{contact.metadata}"
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Get specific contact
|
|
129
|
+
if contacts.data.any?
|
|
130
|
+
wa_id = contacts.data.first.wa_id
|
|
131
|
+
|
|
132
|
+
contact_details = kapso_client.contacts.get(
|
|
133
|
+
phone_number_id: phone_number_id,
|
|
134
|
+
wa_id: wa_id
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
puts "\nContact details for #{wa_id}:"
|
|
138
|
+
puts "Profile Name: #{contact_details.profile_name}"
|
|
139
|
+
|
|
140
|
+
# Update contact metadata
|
|
141
|
+
kapso_client.contacts.update(
|
|
142
|
+
phone_number_id: phone_number_id,
|
|
143
|
+
wa_id: wa_id,
|
|
144
|
+
metadata: {
|
|
145
|
+
tags: ['ruby_sdk', 'test_contact'],
|
|
146
|
+
source: 'api_example',
|
|
147
|
+
notes: 'Updated via Ruby SDK'
|
|
148
|
+
}
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
puts "Contact metadata updated"
|
|
152
|
+
|
|
153
|
+
# Add tags
|
|
154
|
+
kapso_client.contacts.add_tags(
|
|
155
|
+
phone_number_id: phone_number_id,
|
|
156
|
+
wa_id: wa_id,
|
|
157
|
+
tags: ['premium_customer']
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
puts "Tags added to contact"
|
|
161
|
+
|
|
162
|
+
# Search contacts
|
|
163
|
+
search_results = kapso_client.contacts.search(
|
|
164
|
+
phone_number_id: phone_number_id,
|
|
165
|
+
query: 'john',
|
|
166
|
+
search_in: ['profile_name', 'phone_number']
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
puts "Search results: #{search_results.data.length} contacts"
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
rescue KapsoClientRuby::Errors::GraphApiError => e
|
|
173
|
+
puts "Contact management error: #{e.message}"
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Example 4: Call Management
|
|
177
|
+
puts "\n--- Call Management ---"
|
|
178
|
+
|
|
179
|
+
begin
|
|
180
|
+
# List recent calls
|
|
181
|
+
calls = kapso_client.calls.list(
|
|
182
|
+
phone_number_id: phone_number_id,
|
|
183
|
+
direction: 'INBOUND',
|
|
184
|
+
limit: 5
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
puts "Recent calls: #{calls.data.length}"
|
|
188
|
+
|
|
189
|
+
calls.data.each do |call|
|
|
190
|
+
puts "Call #{call.id}:"
|
|
191
|
+
puts " Direction: #{call.direction}"
|
|
192
|
+
puts " Status: #{call.status}"
|
|
193
|
+
puts " Duration: #{call.duration_seconds} seconds"
|
|
194
|
+
puts " Started: #{call.started_at}"
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Initiate a call (example - requires proper setup)
|
|
198
|
+
begin
|
|
199
|
+
call_response = kapso_client.calls.connect(
|
|
200
|
+
phone_number_id: phone_number_id,
|
|
201
|
+
to: '+1234567890',
|
|
202
|
+
session: {
|
|
203
|
+
sdp_type: 'offer',
|
|
204
|
+
sdp: 'v=0\r\no=- 123456789 123456789 IN IP4 127.0.0.1\r\n...'
|
|
205
|
+
}
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
puts "Call initiated: #{call_response.calls.first['id']}"
|
|
209
|
+
rescue KapsoClientRuby::Errors::GraphApiError => e
|
|
210
|
+
puts "Call initiation error (expected in example): #{e.message}"
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
rescue KapsoClientRuby::Errors::GraphApiError => e
|
|
214
|
+
puts "Call management error: #{e.message}"
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# Example 5: Advanced Error Handling and Monitoring
|
|
218
|
+
puts "\n--- Advanced Error Handling ---"
|
|
219
|
+
|
|
220
|
+
class WhatsAppMonitor
|
|
221
|
+
def initialize(client)
|
|
222
|
+
@client = client
|
|
223
|
+
@error_counts = Hash.new(0)
|
|
224
|
+
@last_errors = []
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def send_with_monitoring(method, *args, **kwargs)
|
|
228
|
+
start_time = Time.now
|
|
229
|
+
|
|
230
|
+
begin
|
|
231
|
+
result = @client.messages.public_send(method, **kwargs)
|
|
232
|
+
|
|
233
|
+
duration = Time.now - start_time
|
|
234
|
+
puts "✓ #{method} succeeded in #{duration.round(2)}s"
|
|
235
|
+
|
|
236
|
+
result
|
|
237
|
+
rescue KapsoClientRuby::Errors::GraphApiError => e
|
|
238
|
+
duration = Time.now - start_time
|
|
239
|
+
@error_counts[e.category] += 1
|
|
240
|
+
@last_errors << {
|
|
241
|
+
timestamp: Time.now,
|
|
242
|
+
method: method,
|
|
243
|
+
error: e,
|
|
244
|
+
duration: duration
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
puts "✗ #{method} failed in #{duration.round(2)}s"
|
|
248
|
+
puts " Category: #{e.category}"
|
|
249
|
+
puts " Message: #{e.message}"
|
|
250
|
+
puts " Retry: #{e.retry_hint[:action]}"
|
|
251
|
+
|
|
252
|
+
# Automatic retry logic
|
|
253
|
+
case e.retry_hint[:action]
|
|
254
|
+
when :retry
|
|
255
|
+
if kwargs[:_retry_count].to_i < 3
|
|
256
|
+
retry_count = kwargs[:_retry_count].to_i + 1
|
|
257
|
+
puts " Auto-retrying (#{retry_count}/3)..."
|
|
258
|
+
sleep(retry_count)
|
|
259
|
+
return send_with_monitoring(method, **kwargs.merge(_retry_count: retry_count))
|
|
260
|
+
end
|
|
261
|
+
when :retry_after
|
|
262
|
+
if e.retry_hint[:retry_after_ms] && e.retry_hint[:retry_after_ms] < 30000
|
|
263
|
+
delay = e.retry_hint[:retry_after_ms] / 1000.0
|
|
264
|
+
puts " Waiting #{delay}s for rate limit..."
|
|
265
|
+
sleep(delay)
|
|
266
|
+
return send_with_monitoring(method, **kwargs)
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
raise
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def print_statistics
|
|
275
|
+
puts "\n--- Error Statistics ---"
|
|
276
|
+
puts "Total error categories: #{@error_counts.keys.length}"
|
|
277
|
+
@error_counts.each do |category, count|
|
|
278
|
+
puts " #{category}: #{count} errors"
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
if @last_errors.any?
|
|
282
|
+
puts "\nRecent errors:"
|
|
283
|
+
@last_errors.last(3).each do |error_info|
|
|
284
|
+
puts " #{error_info[:timestamp]}: #{error_info[:method]} -> #{error_info[:error].category}"
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
# Test the monitoring system
|
|
291
|
+
monitor = WhatsAppMonitor.new(kapso_client)
|
|
292
|
+
|
|
293
|
+
# Test various operations with monitoring
|
|
294
|
+
test_operations = [
|
|
295
|
+
[:send_text, {
|
|
296
|
+
phone_number_id: phone_number_id,
|
|
297
|
+
to: '+1234567890',
|
|
298
|
+
body: 'Test message from monitoring system'
|
|
299
|
+
}],
|
|
300
|
+
[:send_template, {
|
|
301
|
+
phone_number_id: phone_number_id,
|
|
302
|
+
to: '+1234567890',
|
|
303
|
+
name: 'nonexistent_template',
|
|
304
|
+
language: 'en_US'
|
|
305
|
+
}],
|
|
306
|
+
[:send_image, {
|
|
307
|
+
phone_number_id: phone_number_id,
|
|
308
|
+
to: '+1234567890',
|
|
309
|
+
image: { link: 'https://invalid-url.example/image.jpg' }
|
|
310
|
+
}]
|
|
311
|
+
]
|
|
312
|
+
|
|
313
|
+
test_operations.each do |method, kwargs|
|
|
314
|
+
begin
|
|
315
|
+
monitor.send_with_monitoring(method, **kwargs)
|
|
316
|
+
rescue => e
|
|
317
|
+
puts "Final error for #{method}: #{e.message}"
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
sleep(1) # Rate limiting prevention
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
monitor.print_statistics
|
|
324
|
+
|
|
325
|
+
# Example 6: Webhook Signature Verification (helper function)
|
|
326
|
+
puts "\n--- Webhook Signature Verification ---"
|
|
327
|
+
|
|
328
|
+
def verify_webhook_signature(payload, signature, app_secret)
|
|
329
|
+
require 'openssl'
|
|
330
|
+
|
|
331
|
+
# Extract signature from header (format: "sha256=...")
|
|
332
|
+
sig_hash = signature.sub('sha256=', '')
|
|
333
|
+
|
|
334
|
+
# Calculate expected signature
|
|
335
|
+
expected_sig = OpenSSL::HMAC.hexdigest('sha256', app_secret, payload)
|
|
336
|
+
|
|
337
|
+
# Secure comparison
|
|
338
|
+
sig_hash == expected_sig
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
# Example webhook payload verification
|
|
342
|
+
webhook_payload = '{"object":"whatsapp_business_account","entry":[...]}'
|
|
343
|
+
webhook_signature = 'sha256=abcdef123456...' # From X-Hub-Signature-256 header
|
|
344
|
+
app_secret = ENV['WHATSAPP_APP_SECRET']
|
|
345
|
+
|
|
346
|
+
if app_secret
|
|
347
|
+
is_valid = verify_webhook_signature(webhook_payload, webhook_signature, app_secret)
|
|
348
|
+
puts "Webhook signature valid: #{is_valid}"
|
|
349
|
+
else
|
|
350
|
+
puts "Set WHATSAPP_APP_SECRET to test webhook verification"
|
|
351
|
+
end
|
|
352
|
+
|
|
350
353
|
puts "\n=== Advanced Features Examples Completed ==="
|