whatsapp-cloud-api-ruby 1.0.0

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.
data/Rakefile ADDED
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ desc 'Run RSpec tests'
4
+ task :spec do
5
+ sh 'bundle exec rspec'
6
+ end
7
+
8
+ desc 'Run RuboCop'
9
+ task :rubocop do
10
+ sh 'bundle exec rubocop'
11
+ end
12
+
13
+ desc 'Run all linting and tests'
14
+ task test: [:rubocop, :spec]
15
+
16
+ desc 'Generate YARD documentation'
17
+ task :docs do
18
+ sh 'bundle exec yard doc'
19
+ end
20
+
21
+ desc 'Clean up generated files'
22
+ task :clean do
23
+ sh 'rm -rf coverage/ doc/ .yardoc/'
24
+ end
25
+
26
+ desc 'Build the gem'
27
+ task :build do
28
+ sh 'gem build whatsapp-cloud-api-ruby.gemspec'
29
+ end
30
+
31
+ desc 'Install the gem locally'
32
+ task install: :build do
33
+ sh 'gem install whatsapp-cloud-api-ruby-*.gem'
34
+ end
35
+
36
+ desc 'Release the gem'
37
+ task release: [:clean, :test, :build] do
38
+ puts 'Run `gem push whatsapp-cloud-api-ruby-*.gem` to release'
39
+ end
40
+
41
+ task default: :test
@@ -0,0 +1,121 @@
1
+ # 📋 Kapso Template Management Tools
2
+
3
+ ## The 24-Hour Problem & Solution
4
+
5
+ **Problem**: You're getting "24 hours have passed" errors when sending messages.
6
+ **Solution**: Use template messages - they work anytime, no 24-hour limit!
7
+
8
+ ## 🛠️ Template Tools (Simplified)
9
+
10
+ ### 1. `scripts/kapso_template_finder.rb` - **MAIN TOOL** ⭐
11
+ ```bash
12
+ ruby scripts/kapso_template_finder.rb
13
+ ```
14
+ **Purpose**: Complete template discovery and testing
15
+ **Features**:
16
+ - Fetches your actual created templates from Kapso
17
+
18
+ ### 2. `scripts/test.rb` - Basic Message Testing
19
+ ```bash
20
+ ruby scripts/test.rb
21
+ ```
22
+ **Purpose**: Test regular message sending
23
+ **Use when**: Testing within 24-hour window
24
+ **Features**:
25
+ - Test regular messages
26
+ - Shows 24h error if outside window
27
+
28
+ ## 📋 Setup Instructions
29
+
30
+ ### Step 1: Find Your Business Account ID
31
+
32
+ 1. **Login to Kapso Dashboard**: https://app.kapso.ai/
33
+ 2. **Navigate to WhatsApp Business section**
34
+ 3. **Look for**:
35
+ - "Business Account ID"
36
+ - "WABA ID"
37
+ - Long number (15+ digits)
38
+ 4. **Add to `.env` file**:
39
+ ```properties
40
+ BUSINESS_ACCOUNT_ID=your_business_account_id_here
41
+ ```
42
+
43
+ ### Step 2: Discover Your Templates
44
+
45
+ ```bash
46
+ ruby scripts/kapso_template_finder.rb
47
+ ```
48
+
49
+ ✅ **Current Status**: Found 1 template `reply_message (es_MX) - PENDING`
50
+ ⏳ **Waiting for approval** - Template will work once Meta approves it
51
+
52
+ ### Step 3: Test Templates
53
+
54
+ ```bash
55
+ ruby test_specific_template.rb
56
+ # Enter template name when prompted
57
+ ```
58
+
59
+ ## 🎯 Creating Templates in Kapso
60
+
61
+ ### Via Kapso Dashboard:
62
+ 1. Login to https://app.kapso.ai/
63
+ 2. Find "WhatsApp Templates" or "Template Builder"
64
+ 3. Create simple text template:
65
+ - **Name**: `hello_world` or `welcome_message`
66
+ - **Language**: `en_US`
67
+ - **Category**: `UTILITY` (usually fastest approval)
68
+ - **Content**: "Hello {{1}}, welcome to our service!"
69
+
70
+ ### Template Approval:
71
+ - **Pending**: Template submitted, waiting for Meta approval
72
+ - **Approved**: Ready to use anytime
73
+ - **Rejected**: Need to fix and resubmit
74
+ - **Time**: Usually 24-48 hours for approval
75
+
76
+ ## 🚀 Using Templates in Code
77
+
78
+ ```ruby
79
+ # Once your template is approved:
80
+ response = client.messages.send_template(
81
+ phone_number_id: "your_phone_number",
82
+ to: "+56912345678",
83
+ name: "reply_message", # Your actual template name
84
+ language: "es_MX" # Your template language
85
+ )
86
+
87
+ # Templates work 24/7 - no time restrictions!
88
+ ```
89
+
90
+ ## 🔄 Workflow Summary
91
+
92
+ 1. **Check templates**: `ruby scripts/kapso_template_finder.rb`
93
+ 2. ✅ **Template found**: `reply_message (es_MX) - PENDING`
94
+ 3. ⏳ **Wait for approval**: Meta is reviewing your template
95
+ 4. **Once approved**: Use `reply_message` template for 24/7 messaging
96
+
97
+ ## ❓ Troubleshooting
98
+
99
+ ### "Template not found"
100
+ - Template name is wrong
101
+ - Template not approved yet
102
+ - Language code incorrect
103
+
104
+ ### "24 hours error" (with templates)
105
+ - This shouldn't happen with templates
106
+ - Contact Kapso support
107
+
108
+ ### "Business Account ID not found"
109
+ - Check Kapso dashboard for WABA ID
110
+ - Add to .env file
111
+ - Contact Kapso support if unclear
112
+
113
+ ## 📞 Support Resources
114
+
115
+ - **Kapso Dashboard**: https://app.kapso.ai/
116
+ - **Kapso Support**: Contact through dashboard
117
+ - **WhatsApp Template Policies**: Check Meta Business documentation
118
+
119
+ ---
120
+
121
+ **Remember**: Templates solve the 24-hour limitation permanently! Regular messages work within 24h of customer reply, templates work anytime. 🎯
@@ -0,0 +1,134 @@
1
+ # WhatsApp 24-Hour Window Policy - Solutions Guide
2
+
3
+ ## The Problem You're Experiencing
4
+
5
+ **Error Message**: "Meta reported a deli### Long-term**: Design your messaging strategy around the 24h rule
6
+
7
+ ## Kapso.ai Specific Features
8
+
9
+ Since you're using Kapso.ai, you may have access to:
10
+
11
+ - **Template Library**: Pre-built approved templates
12
+ - **Template Builder**: Easy template creation interface
13
+ - **Auto-Approval**: Faster template approval process
14
+ - **Template Analytics**: Usage statistics and delivery rates
15
+ - **Bulk Template Management**: Manage multiple templates
16
+
17
+ Check your Kapso dashboard for:
18
+ 1. **Available Templates** - Already approved and ready to use
19
+ 2. **Template Status** - Pending, approved, or rejected templates
20
+ 3. **Template Builder** - Create new templates without Meta Business Manager
21
+
22
+ ## Why This Existsy error. Re-engagement message Message failed to send because more than 24 hours have passed since the customer last replied to this number."
23
+
24
+ This is **NOT** a bug in your Ruby SDK - it's a WhatsApp Business API policy.
25
+
26
+ ## Understanding the 24-Hour Rule
27
+
28
+ WhatsApp Business API has a **conversation window** policy:
29
+
30
+ ### ✅ Within 24 Hours (Free Messaging Window)
31
+ - **Trigger**: Customer sends you a message
32
+ - **Duration**: 24 hours from their last message
33
+ - **Allowed**: ANY message type (text, images, audio, video, documents)
34
+ - **No restrictions**: Send as many messages as you want
35
+
36
+ ### ❌ After 24 Hours (Template-Only Window)
37
+ - **Trigger**: 24+ hours since customer's last message
38
+ - **Restriction**: ONLY pre-approved template messages allowed
39
+ - **Regular messages**: Will be rejected with your error
40
+
41
+ ## Solutions
42
+
43
+ ### Option 1: Use Template Messages (Recommended)
44
+
45
+ Template messages work anytime and are designed for business communications:
46
+
47
+ ```ruby
48
+ # Send a template message (works 24/7)
49
+ response = client.messages.send_template(
50
+ phone_number_id: phone_number_id,
51
+ to: "+56912345678",
52
+ template_name: "hello_world", # Must be pre-approved
53
+ template_language: "en_US"
54
+ )
55
+ ```
56
+
57
+ ### Option 2: Wait for Customer Reply
58
+
59
+ - Customer sends any message → Opens 24-hour window
60
+ - You can then send regular messages for 24 hours
61
+
62
+ ### Option 3: Create Templates via Kapso.ai
63
+
64
+ ✅ **Current Status**: You already have templates in progress!
65
+
66
+ 1. **Kapso Dashboard**: ✅ Connected (Business Account: `your_business_id_account`)
67
+ 2. **Template Found**: `reply_message (es_MX) - PENDING`
68
+ 3. **Status**: Waiting for Meta approval (usually 24-48 hours)
69
+ 4. **Once Approved**: Use `your_template` template for 24/7 messaging
70
+ 5. **Test Tool**: `ruby scripts/kapso_template_finder.rb`
71
+
72
+ ## Prevention Strategies
73
+
74
+ ### For Customer Service
75
+ - Respond within 24 hours of customer messages
76
+ - Use templates for follow-ups after 24h
77
+ - Set up auto-responses within the window
78
+
79
+ ### For Marketing
80
+ - Always use approved template messages
81
+ - Create templates for different campaigns
82
+ - Schedule template sends anytime
83
+
84
+ ### For Notifications
85
+ - Use templates for order updates, reminders, etc.
86
+ - Create templates for common notifications
87
+ - Test templates before going live
88
+
89
+ ## Template Message Examples
90
+
91
+ ### Basic Template (No Variables)
92
+ ```ruby
93
+ client.messages.send_template(
94
+ phone_number_id: phone_number_id,
95
+ to: phone_number,
96
+ template_name: "hello_world",
97
+ template_language: "en_US"
98
+ )
99
+ ```
100
+
101
+ ### Template with Variables
102
+ ```ruby
103
+ client.messages.send_template(
104
+ phone_number_id: phone_number_id,
105
+ to: phone_number,
106
+ template_name: "order_confirmation",
107
+ template_language: "en_US",
108
+ components: [
109
+ {
110
+ type: "body",
111
+ parameters: [
112
+ { type: "text", text: "John" }, # Customer name
113
+ { type: "text", text: "12345" } # Order number
114
+ ]
115
+ }
116
+ ]
117
+ )
118
+ ```
119
+
120
+ ## Next Steps
121
+
122
+ 1. **Immediate**: Use `ruby template_test.rb` to test template messages
123
+ 2. **Short-term**: Create templates in Meta Business Manager
124
+ 3. **Long-term**: Design your messaging strategy around the 24h rule
125
+
126
+ ## Why This Exists
127
+
128
+ WhatsApp enforces this to:
129
+ - Prevent spam
130
+ - Ensure quality business communications
131
+ - Protect user experience
132
+ - Maintain platform integrity
133
+
134
+ The Ruby SDK is working perfectly - this is just how WhatsApp Business API works! 🚀
@@ -0,0 +1,350 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'whatsapp_cloud_api'
4
+
5
+ puts "=== Advanced Features with Kapso Proxy ==="
6
+
7
+ # Initialize Kapso client
8
+ kapso_client = WhatsAppCloudApi::Client.new(
9
+ kapso_api_key: ENV['KAPSO_API_KEY'],
10
+ base_url: 'https://app.kapso.ai/api/meta',
11
+ debug: true
12
+ )
13
+
14
+ phone_number_id = ENV['PHONE_NUMBER_ID']
15
+
16
+ # Example 1: Message History and Analytics
17
+ puts "\n--- Message History ---"
18
+
19
+ begin
20
+ # Query message history
21
+ messages = kapso_client.messages.query(
22
+ phone_number_id: phone_number_id,
23
+ direction: 'inbound',
24
+ since: '2024-01-01T00:00:00Z',
25
+ limit: 10
26
+ )
27
+
28
+ puts "Found #{messages.data.length} messages:"
29
+ messages.data.each do |message|
30
+ puts "- #{message['id']}: #{message['type']} from #{message['from']}"
31
+ end
32
+
33
+ # Get messages by conversation
34
+ if messages.data.any? && messages.data.first['conversation_id']
35
+ conv_messages = kapso_client.messages.list_by_conversation(
36
+ phone_number_id: phone_number_id,
37
+ conversation_id: messages.data.first['conversation_id'],
38
+ limit: 5
39
+ )
40
+
41
+ puts "\nConversation messages: #{conv_messages.data.length}"
42
+ end
43
+
44
+ rescue WhatsAppCloudApi::Errors::KapsoProxyRequiredError => e
45
+ puts "Kapso Proxy required: #{e.message}"
46
+ rescue WhatsAppCloudApi::Errors::GraphApiError => e
47
+ puts "Message history error: #{e.message}"
48
+ end
49
+
50
+ # Example 2: Conversation Management
51
+ puts "\n--- Conversation Management ---"
52
+
53
+ begin
54
+ # List active conversations
55
+ conversations = kapso_client.conversations.list(
56
+ phone_number_id: phone_number_id,
57
+ status: 'active',
58
+ limit: 10
59
+ )
60
+
61
+ puts "Active conversations: #{conversations.data.length}"
62
+
63
+ conversations.data.each do |conv|
64
+ puts "Conversation #{conv.id}:"
65
+ puts " Phone: #{conv.phone_number}"
66
+ puts " Status: #{conv.status}"
67
+ puts " Last Active: #{conv.last_active_at}"
68
+
69
+ if conv.kapso
70
+ puts " Contact Name: #{conv.kapso['contact_name']}"
71
+ puts " Messages Count: #{conv.kapso['messages_count']}"
72
+ puts " Last Message: #{conv.kapso['last_message_text']}"
73
+ end
74
+ end
75
+
76
+ # Get specific conversation details
77
+ if conversations.data.any?
78
+ conversation_id = conversations.data.first.id
79
+
80
+ conv_details = kapso_client.conversations.get(
81
+ conversation_id: conversation_id
82
+ )
83
+
84
+ puts "\nDetailed conversation info:"
85
+ puts "ID: #{conv_details.id}"
86
+ puts "Status: #{conv_details.status}"
87
+ puts "Metadata: #{conv_details.metadata}"
88
+
89
+ # Update conversation status
90
+ kapso_client.conversations.update_status(
91
+ conversation_id: conversation_id,
92
+ status: 'archived'
93
+ )
94
+
95
+ puts "Conversation archived successfully"
96
+
97
+ # Unarchive it
98
+ kapso_client.conversations.unarchive(conversation_id: conversation_id)
99
+ puts "Conversation unarchived"
100
+ end
101
+
102
+ rescue WhatsAppCloudApi::Errors::GraphApiError => e
103
+ puts "Conversation management error: #{e.message}"
104
+ end
105
+
106
+ # Example 3: Contact Management
107
+ puts "\n--- Contact Management ---"
108
+
109
+ begin
110
+ # List contacts
111
+ contacts = kapso_client.contacts.list(
112
+ phone_number_id: phone_number_id,
113
+ limit: 10
114
+ )
115
+
116
+ puts "Found #{contacts.data.length} contacts:"
117
+
118
+ contacts.data.each do |contact|
119
+ puts "Contact #{contact.wa_id}:"
120
+ puts " Phone: #{contact.phone_number}"
121
+ puts " Profile Name: #{contact.profile_name}"
122
+ puts " Metadata: #{contact.metadata}"
123
+ end
124
+
125
+ # Get specific contact
126
+ if contacts.data.any?
127
+ wa_id = contacts.data.first.wa_id
128
+
129
+ contact_details = kapso_client.contacts.get(
130
+ phone_number_id: phone_number_id,
131
+ wa_id: wa_id
132
+ )
133
+
134
+ puts "\nContact details for #{wa_id}:"
135
+ puts "Profile Name: #{contact_details.profile_name}"
136
+
137
+ # Update contact metadata
138
+ kapso_client.contacts.update(
139
+ phone_number_id: phone_number_id,
140
+ wa_id: wa_id,
141
+ metadata: {
142
+ tags: ['ruby_sdk', 'test_contact'],
143
+ source: 'api_example',
144
+ notes: 'Updated via Ruby SDK'
145
+ }
146
+ )
147
+
148
+ puts "Contact metadata updated"
149
+
150
+ # Add tags
151
+ kapso_client.contacts.add_tags(
152
+ phone_number_id: phone_number_id,
153
+ wa_id: wa_id,
154
+ tags: ['premium_customer']
155
+ )
156
+
157
+ puts "Tags added to contact"
158
+
159
+ # Search contacts
160
+ search_results = kapso_client.contacts.search(
161
+ phone_number_id: phone_number_id,
162
+ query: 'john',
163
+ search_in: ['profile_name', 'phone_number']
164
+ )
165
+
166
+ puts "Search results: #{search_results.data.length} contacts"
167
+ end
168
+
169
+ rescue WhatsAppCloudApi::Errors::GraphApiError => e
170
+ puts "Contact management error: #{e.message}"
171
+ end
172
+
173
+ # Example 4: Call Management
174
+ puts "\n--- Call Management ---"
175
+
176
+ begin
177
+ # List recent calls
178
+ calls = kapso_client.calls.list(
179
+ phone_number_id: phone_number_id,
180
+ direction: 'INBOUND',
181
+ limit: 5
182
+ )
183
+
184
+ puts "Recent calls: #{calls.data.length}"
185
+
186
+ calls.data.each do |call|
187
+ puts "Call #{call.id}:"
188
+ puts " Direction: #{call.direction}"
189
+ puts " Status: #{call.status}"
190
+ puts " Duration: #{call.duration_seconds} seconds"
191
+ puts " Started: #{call.started_at}"
192
+ end
193
+
194
+ # Initiate a call (example - requires proper setup)
195
+ begin
196
+ call_response = kapso_client.calls.connect(
197
+ phone_number_id: phone_number_id,
198
+ to: '+1234567890',
199
+ session: {
200
+ sdp_type: 'offer',
201
+ sdp: 'v=0\r\no=- 123456789 123456789 IN IP4 127.0.0.1\r\n...'
202
+ }
203
+ )
204
+
205
+ puts "Call initiated: #{call_response.calls.first['id']}"
206
+ rescue WhatsAppCloudApi::Errors::GraphApiError => e
207
+ puts "Call initiation error (expected in example): #{e.message}"
208
+ end
209
+
210
+ rescue WhatsAppCloudApi::Errors::GraphApiError => e
211
+ puts "Call management error: #{e.message}"
212
+ end
213
+
214
+ # Example 5: Advanced Error Handling and Monitoring
215
+ puts "\n--- Advanced Error Handling ---"
216
+
217
+ class WhatsAppMonitor
218
+ def initialize(client)
219
+ @client = client
220
+ @error_counts = Hash.new(0)
221
+ @last_errors = []
222
+ end
223
+
224
+ def send_with_monitoring(method, *args, **kwargs)
225
+ start_time = Time.now
226
+
227
+ begin
228
+ result = @client.messages.public_send(method, **kwargs)
229
+
230
+ duration = Time.now - start_time
231
+ puts "✓ #{method} succeeded in #{duration.round(2)}s"
232
+
233
+ result
234
+ rescue WhatsAppCloudApi::Errors::GraphApiError => e
235
+ duration = Time.now - start_time
236
+ @error_counts[e.category] += 1
237
+ @last_errors << {
238
+ timestamp: Time.now,
239
+ method: method,
240
+ error: e,
241
+ duration: duration
242
+ }
243
+
244
+ puts "✗ #{method} failed in #{duration.round(2)}s"
245
+ puts " Category: #{e.category}"
246
+ puts " Message: #{e.message}"
247
+ puts " Retry: #{e.retry_hint[:action]}"
248
+
249
+ # Automatic retry logic
250
+ case e.retry_hint[:action]
251
+ when :retry
252
+ if kwargs[:_retry_count].to_i < 3
253
+ retry_count = kwargs[:_retry_count].to_i + 1
254
+ puts " Auto-retrying (#{retry_count}/3)..."
255
+ sleep(retry_count)
256
+ return send_with_monitoring(method, **kwargs.merge(_retry_count: retry_count))
257
+ end
258
+ when :retry_after
259
+ if e.retry_hint[:retry_after_ms] && e.retry_hint[:retry_after_ms] < 30000
260
+ delay = e.retry_hint[:retry_after_ms] / 1000.0
261
+ puts " Waiting #{delay}s for rate limit..."
262
+ sleep(delay)
263
+ return send_with_monitoring(method, **kwargs)
264
+ end
265
+ end
266
+
267
+ raise
268
+ end
269
+ end
270
+
271
+ def print_statistics
272
+ puts "\n--- Error Statistics ---"
273
+ puts "Total error categories: #{@error_counts.keys.length}"
274
+ @error_counts.each do |category, count|
275
+ puts " #{category}: #{count} errors"
276
+ end
277
+
278
+ if @last_errors.any?
279
+ puts "\nRecent errors:"
280
+ @last_errors.last(3).each do |error_info|
281
+ puts " #{error_info[:timestamp]}: #{error_info[:method]} -> #{error_info[:error].category}"
282
+ end
283
+ end
284
+ end
285
+ end
286
+
287
+ # Test the monitoring system
288
+ monitor = WhatsAppMonitor.new(kapso_client)
289
+
290
+ # Test various operations with monitoring
291
+ test_operations = [
292
+ [:send_text, {
293
+ phone_number_id: phone_number_id,
294
+ to: '+1234567890',
295
+ body: 'Test message from monitoring system'
296
+ }],
297
+ [:send_template, {
298
+ phone_number_id: phone_number_id,
299
+ to: '+1234567890',
300
+ name: 'nonexistent_template',
301
+ language: 'en_US'
302
+ }],
303
+ [:send_image, {
304
+ phone_number_id: phone_number_id,
305
+ to: '+1234567890',
306
+ image: { link: 'https://invalid-url.example/image.jpg' }
307
+ }]
308
+ ]
309
+
310
+ test_operations.each do |method, kwargs|
311
+ begin
312
+ monitor.send_with_monitoring(method, **kwargs)
313
+ rescue => e
314
+ puts "Final error for #{method}: #{e.message}"
315
+ end
316
+
317
+ sleep(1) # Rate limiting prevention
318
+ end
319
+
320
+ monitor.print_statistics
321
+
322
+ # Example 6: Webhook Signature Verification (helper function)
323
+ puts "\n--- Webhook Signature Verification ---"
324
+
325
+ def verify_webhook_signature(payload, signature, app_secret)
326
+ require 'openssl'
327
+
328
+ # Extract signature from header (format: "sha256=...")
329
+ sig_hash = signature.sub('sha256=', '')
330
+
331
+ # Calculate expected signature
332
+ expected_sig = OpenSSL::HMAC.hexdigest('sha256', app_secret, payload)
333
+
334
+ # Secure comparison
335
+ sig_hash == expected_sig
336
+ end
337
+
338
+ # Example webhook payload verification
339
+ webhook_payload = '{"object":"whatsapp_business_account","entry":[...]}'
340
+ webhook_signature = 'sha256=abcdef123456...' # From X-Hub-Signature-256 header
341
+ app_secret = ENV['WHATSAPP_APP_SECRET']
342
+
343
+ if app_secret
344
+ is_valid = verify_webhook_signature(webhook_payload, webhook_signature, app_secret)
345
+ puts "Webhook signature valid: #{is_valid}"
346
+ else
347
+ puts "Set WHATSAPP_APP_SECRET to test webhook verification"
348
+ end
349
+
350
+ puts "\n=== Advanced Features Examples Completed ==="