flow_chat 0.3.0 → 0.4.1

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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -0
  3. data/README.md +642 -86
  4. data/examples/initializer.rb +31 -0
  5. data/examples/media_prompts_examples.rb +28 -0
  6. data/examples/multi_tenant_whatsapp_controller.rb +244 -0
  7. data/examples/ussd_controller.rb +264 -0
  8. data/examples/whatsapp_controller.rb +140 -0
  9. data/examples/whatsapp_media_examples.rb +406 -0
  10. data/examples/whatsapp_message_job.rb +111 -0
  11. data/lib/flow_chat/base_processor.rb +67 -0
  12. data/lib/flow_chat/config.rb +36 -0
  13. data/lib/flow_chat/session/cache_session_store.rb +84 -0
  14. data/lib/flow_chat/session/middleware.rb +14 -6
  15. data/lib/flow_chat/simulator/controller.rb +78 -0
  16. data/lib/flow_chat/simulator/views/simulator.html.erb +1707 -0
  17. data/lib/flow_chat/ussd/app.rb +25 -0
  18. data/lib/flow_chat/ussd/gateway/nalo.rb +2 -0
  19. data/lib/flow_chat/ussd/gateway/nsano.rb +6 -0
  20. data/lib/flow_chat/ussd/middleware/resumable_session.rb +1 -1
  21. data/lib/flow_chat/ussd/processor.rb +14 -42
  22. data/lib/flow_chat/ussd/prompt.rb +39 -5
  23. data/lib/flow_chat/version.rb +1 -1
  24. data/lib/flow_chat/whatsapp/app.rb +64 -0
  25. data/lib/flow_chat/whatsapp/client.rb +439 -0
  26. data/lib/flow_chat/whatsapp/configuration.rb +113 -0
  27. data/lib/flow_chat/whatsapp/gateway/cloud_api.rb +213 -0
  28. data/lib/flow_chat/whatsapp/middleware/executor.rb +30 -0
  29. data/lib/flow_chat/whatsapp/processor.rb +26 -0
  30. data/lib/flow_chat/whatsapp/prompt.rb +251 -0
  31. data/lib/flow_chat/whatsapp/send_job_support.rb +79 -0
  32. data/lib/flow_chat/whatsapp/template_manager.rb +162 -0
  33. data/lib/flow_chat.rb +1 -0
  34. metadata +21 -3
  35. data/lib/flow_chat/ussd/simulator/controller.rb +0 -51
  36. data/lib/flow_chat/ussd/simulator/views/simulator.html.erb +0 -239
@@ -0,0 +1,140 @@
1
+ # Example WhatsApp Controller
2
+ # Add this to your Rails application as app/controllers/whatsapp_controller.rb
3
+
4
+ class WhatsappController < ApplicationController
5
+ skip_forgery_protection
6
+
7
+ def webhook
8
+ processor = FlowChat::Whatsapp::Processor.new(self) do |config|
9
+ config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi
10
+ # Use cache-based session store for longer WhatsApp conversations
11
+ config.use_session_store FlowChat::Session::CacheSessionStore
12
+ end
13
+
14
+ processor.run WelcomeFlow, :main_page
15
+ end
16
+ end
17
+
18
+ # Example with Custom Configuration
19
+ class CustomWhatsappController < ApplicationController
20
+ skip_forgery_protection
21
+
22
+ def webhook
23
+ # Create custom WhatsApp configuration for this endpoint
24
+ custom_config = FlowChat::Whatsapp::Configuration.new
25
+ custom_config.access_token = ENV['MY_WHATSAPP_ACCESS_TOKEN']
26
+ custom_config.phone_number_id = ENV['MY_WHATSAPP_PHONE_NUMBER_ID']
27
+ custom_config.verify_token = ENV['MY_WHATSAPP_VERIFY_TOKEN']
28
+ custom_config.app_id = ENV['MY_WHATSAPP_APP_ID']
29
+ custom_config.app_secret = ENV['MY_WHATSAPP_APP_SECRET']
30
+ custom_config.business_account_id = ENV['MY_WHATSAPP_BUSINESS_ACCOUNT_ID']
31
+
32
+ processor = FlowChat::Whatsapp::Processor.new(self) do |config|
33
+ config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi, custom_config
34
+ config.use_session_store FlowChat::Session::CacheSessionStore
35
+ end
36
+
37
+ processor.run WelcomeFlow, :main_page
38
+ end
39
+ end
40
+
41
+ # Example Flow for WhatsApp
42
+ # Add this to your Rails application as app/flow_chat/welcome_flow.rb
43
+
44
+ class WelcomeFlow < FlowChat::Flow
45
+ def main_page
46
+ # Welcome the user
47
+ name = app.screen(:name) do |prompt|
48
+ prompt.ask "Hello! Welcome to our WhatsApp service. What's your name?",
49
+ transform: ->(input) { input.strip.titleize }
50
+ end
51
+
52
+ # Show main menu
53
+ choice = app.screen(:main_menu) do |prompt|
54
+ prompt.select "Hi #{name}! What can I help you with today?", {
55
+ "info" => "📋 Get Information",
56
+ "support" => "🆘 Contact Support",
57
+ "feedback" => "💬 Give Feedback"
58
+ }
59
+ end
60
+
61
+ case choice
62
+ when "info"
63
+ show_information_menu
64
+ when "support"
65
+ contact_support
66
+ when "feedback"
67
+ collect_feedback
68
+ end
69
+ end
70
+
71
+ private
72
+
73
+ def show_information_menu
74
+ info_choice = app.screen(:info_menu) do |prompt|
75
+ prompt.select "What information do you need?", {
76
+ "hours" => "🕒 Business Hours",
77
+ "location" => "📍 Our Location",
78
+ "services" => "🛠 Our Services"
79
+ }
80
+ end
81
+
82
+ case info_choice
83
+ when "hours"
84
+ app.say "We're open Monday-Friday 9AM-6PM, Saturday 9AM-2PM. Closed Sundays."
85
+ when "location"
86
+ app.say "📍 We're located at 123 Main Street, City, State 12345"
87
+ when "services"
88
+ app.say "Here are our main services:\\n\\n🌐 Web Development - Custom websites and applications\\n📱 Mobile Apps - iOS and Android development\\n🔧 Consulting - Technical consulting services"
89
+ end
90
+ end
91
+
92
+ def contact_support
93
+ # Use standard select menu instead of send_buttons
94
+ contact_method = app.screen(:contact_method) do |prompt|
95
+ prompt.select "How would you like to contact support?", {
96
+ "call" => "📞 Call Us",
97
+ "email" => "📧 Email Us",
98
+ "chat" => "💬 Live Chat"
99
+ }
100
+ end
101
+
102
+ case contact_method
103
+ when "call"
104
+ app.say "📞 You can call us at (555) 123-4567"
105
+ when "email"
106
+ app.say "📧 Send us an email at support@example.com"
107
+ when "chat"
108
+ app.say "💬 Our live chat is available on our website: www.example.com"
109
+ end
110
+ end
111
+
112
+ def collect_feedback
113
+ rating = app.screen(:rating) do |prompt|
114
+ prompt.select "How would you rate our service?", {
115
+ "5" => "⭐⭐⭐⭐⭐ Excellent",
116
+ "4" => "⭐⭐⭐⭐ Good",
117
+ "3" => "⭐⭐⭐ Average",
118
+ "2" => "⭐⭐ Poor",
119
+ "1" => "⭐ Very Poor"
120
+ }
121
+ end
122
+
123
+ feedback = app.screen(:feedback_text) do |prompt|
124
+ prompt.ask "Thank you for the #{rating}-star rating! Please share any additional feedback:"
125
+ end
126
+
127
+ # Save feedback (implement your logic here)
128
+ save_feedback(app.phone_number, rating, feedback)
129
+
130
+ app.say "Thank you for your feedback! We really appreciate it. 🙏"
131
+ end
132
+
133
+ def save_feedback(phone, rating, feedback)
134
+ # Implement your feedback saving logic here
135
+ Rails.logger.info "Feedback from #{phone}: #{rating} stars - #{feedback}"
136
+ end
137
+ end
138
+
139
+ # Add this route to your config/routes.rb:
140
+ # post '/whatsapp/webhook', to: 'whatsapp#webhook'
@@ -0,0 +1,406 @@
1
+ # WhatsApp Media Messaging Examples
2
+ # This file demonstrates how to send different types of media via WhatsApp using FlowChat
3
+
4
+ # ============================================================================
5
+ # BASIC MEDIA SENDING (Out-of-Band Messaging)
6
+ # ============================================================================
7
+
8
+ # Initialize the WhatsApp client
9
+ config = FlowChat::Whatsapp::Configuration.from_credentials
10
+ client = FlowChat::Whatsapp::Client.new(config)
11
+
12
+ # Send an image from URL with caption
13
+ client.send_image("+1234567890", "https://example.com/images/product.jpg", "Check out this amazing photo!")
14
+
15
+ # Send a document from URL
16
+ client.send_document("+1234567890", "https://example.com/reports/monthly_report.pdf", "Here's the monthly report")
17
+
18
+ # Send an audio file from URL
19
+ client.send_audio("+1234567890", "https://example.com/audio/greeting.mp3")
20
+
21
+ # Send a video from URL with caption
22
+ client.send_video("+1234567890", "https://example.com/videos/demo.mp4", "Product demo video")
23
+
24
+ # Send a sticker from URL
25
+ client.send_sticker("+1234567890", "https://example.com/stickers/happy.webp")
26
+
27
+ # You can also still use existing WhatsApp media IDs
28
+ client.send_image("+1234567890", "1234567890", "Image from existing media ID")
29
+
30
+ # ============================================================================
31
+ # USING MEDIA IN FLOWS
32
+ # ============================================================================
33
+
34
+ class MediaSupportFlow < FlowChat::Flow
35
+ def main_page
36
+ # Handle incoming media from user
37
+ if app.media
38
+ handle_received_media
39
+ return
40
+ end
41
+
42
+ choice = app.screen(:main_menu) do |prompt|
43
+ prompt.select "Welcome! How can I help you?", {
44
+ "catalog" => "📷 View Product Catalog",
45
+ "report" => "📄 Get Report",
46
+ "support" => "🎵 Voice Support",
47
+ "feedback" => "📝 Send Feedback"
48
+ }
49
+ end
50
+
51
+ case choice
52
+ when "catalog"
53
+ send_product_catalog
54
+ when "report"
55
+ send_report
56
+ when "support"
57
+ send_voice_message
58
+ when "feedback"
59
+ collect_feedback
60
+ end
61
+ end
62
+
63
+ private
64
+
65
+ def handle_received_media
66
+ media_type = app.media['type']
67
+ media_id = app.media['id']
68
+
69
+ Rails.logger.info "Received #{media_type} from #{app.phone_number}: #{media_id}"
70
+
71
+ case media_type
72
+ when 'image'
73
+ app.say "Thanks for the image! I can see it's a #{media_type} file. Let me process it for you."
74
+ when 'document'
75
+ app.say "I've received your document. I'll review it and get back to you shortly."
76
+ when 'audio'
77
+ app.say "Got your voice message! I'll listen to it and respond appropriately."
78
+ when 'video'
79
+ app.say "Thanks for the video! I'll analyze it and provide feedback."
80
+ end
81
+ end
82
+
83
+ def send_product_catalog
84
+ # Send multiple product images from URLs
85
+ client = get_whatsapp_client
86
+
87
+ app.say "Here's our latest product catalog:"
88
+
89
+ # Product images stored in cloud storage (CDN, S3, etc.)
90
+ product_images = [
91
+ "https://cdn.example.com/products/product1.jpg",
92
+ "https://cdn.example.com/products/product2.jpg",
93
+ "https://cdn.example.com/products/product3.jpg"
94
+ ]
95
+
96
+ product_images.each_with_index do |image_url, index|
97
+ client.send_image(app.phone_number, image_url, "Product #{index + 1}")
98
+ sleep(0.5) # Small delay to avoid rate limiting
99
+ end
100
+
101
+ app.say "Which product interests you the most?"
102
+ end
103
+
104
+ def send_report
105
+ # Send a PDF report from cloud storage
106
+ report_url = generate_report_url # Your method to generate/get report URL
107
+
108
+ if report_url
109
+ client = get_whatsapp_client
110
+ client.send_document(app.phone_number, report_url, "Your monthly report is ready!")
111
+
112
+ app.say "📊 Report sent! Please check the document above."
113
+ else
114
+ app.say "Sorry, I couldn't generate the report right now. Please try again later."
115
+ end
116
+ end
117
+
118
+ def send_voice_message
119
+ # Send a pre-recorded voice message from cloud storage
120
+ audio_url = "https://cdn.example.com/audio/support_greeting.mp3"
121
+
122
+ client = get_whatsapp_client
123
+ client.send_audio(app.phone_number, audio_url)
124
+
125
+ app.say "🎵 Please listen to the voice message above. You can also send me a voice message with your question!"
126
+ end
127
+
128
+ def collect_feedback
129
+ feedback = app.screen(:feedback_text) do |prompt|
130
+ prompt.ask "Please share your feedback. You can also send images or documents if needed:"
131
+ end
132
+
133
+ # Save feedback to database
134
+ save_feedback(feedback, app.phone_number)
135
+
136
+ # Send a thank you sticker from cloud storage
137
+ sticker_url = "https://cdn.example.com/stickers/thanks.webp"
138
+ client = get_whatsapp_client
139
+ client.send_sticker(app.phone_number, sticker_url)
140
+
141
+ app.say "Thank you for your feedback! We really appreciate it. 🙏"
142
+ end
143
+
144
+ def get_whatsapp_client
145
+ config = FlowChat::Whatsapp::Configuration.from_credentials
146
+ FlowChat::Whatsapp::Client.new(config)
147
+ end
148
+
149
+ def generate_report_url
150
+ # Your report generation logic here
151
+ # This could return a signed URL from S3, Google Cloud Storage, etc.
152
+ "https://storage.example.com/reports/monthly_report_#{Time.current.strftime('%Y%m')}.pdf"
153
+ end
154
+
155
+ def save_feedback(feedback, phone_number)
156
+ # Your feedback saving logic here
157
+ Rails.logger.info "Feedback from #{phone_number}: #{feedback}"
158
+ end
159
+ end
160
+
161
+ # ============================================================================
162
+ # ADVANCED MEDIA SERVICE CLASS
163
+ # ============================================================================
164
+
165
+ class WhatsAppMediaService
166
+ def initialize
167
+ @config = FlowChat::Whatsapp::Configuration.from_credentials
168
+ @client = FlowChat::Whatsapp::Client.new(@config)
169
+ end
170
+
171
+ # Send welcome package with multiple media types from URLs
172
+ def send_welcome_package(phone_number, user_name)
173
+ # Welcome image from CDN
174
+ welcome_image_url = "https://cdn.example.com/welcome/banner.jpg"
175
+ @client.send_image(phone_number, welcome_image_url, "Welcome to our service, #{user_name}! 🎉")
176
+
177
+ # Welcome guide document from cloud storage
178
+ guide_url = "https://storage.example.com/guides/user_guide.pdf"
179
+ @client.send_document(phone_number, guide_url, "Here's your user guide")
180
+
181
+ # Welcome audio message from media server
182
+ audio_url = "https://media.example.com/audio/welcome.mp3"
183
+ @client.send_audio(phone_number, audio_url)
184
+ end
185
+
186
+ # Send order confirmation with invoice from cloud storage
187
+ def send_order_confirmation(phone_number, order_id, invoice_url)
188
+ # Send invoice document from cloud storage
189
+ @client.send_document(
190
+ phone_number,
191
+ invoice_url,
192
+ "Order ##{order_id} confirmed! Here's your invoice.",
193
+ "Invoice_#{order_id}.pdf"
194
+ )
195
+
196
+ # Send confirmation buttons
197
+ @client.send_buttons(
198
+ phone_number,
199
+ "Your order has been confirmed! 🛍️",
200
+ [
201
+ { id: 'track_order', title: '📦 Track Order' },
202
+ { id: 'modify_order', title: '✏️ Modify Order' },
203
+ { id: 'support', title: '💬 Contact Support' }
204
+ ]
205
+ )
206
+ end
207
+
208
+ # Send promotional content from CDN
209
+ def send_promotion(phone_number, promo_image_url, promo_video_url = nil)
210
+ # Send promotional image from CDN
211
+ @client.send_image(phone_number, promo_image_url, "🔥 Special offer just for you!")
212
+
213
+ # Optionally send promotional video from video server
214
+ if promo_video_url
215
+ @client.send_video(phone_number, promo_video_url, "Watch this exciting video!")
216
+ end
217
+
218
+ # Follow up with action buttons
219
+ @client.send_buttons(
220
+ phone_number,
221
+ "Don't miss out on this amazing deal!",
222
+ [
223
+ { id: 'buy_now', title: '🛒 Buy Now' },
224
+ { id: 'more_info', title: 'ℹ️ More Info' },
225
+ { id: 'remind_later', title: '⏰ Remind Later' }
226
+ ]
227
+ )
228
+ end
229
+
230
+ # Handle media uploads with processing
231
+ def process_uploaded_media(media_id, media_type, user_phone)
232
+ begin
233
+ # Download the media from WhatsApp
234
+ media_url = @client.get_media_url(media_id)
235
+ media_content = @client.download_media(media_id) if media_url
236
+
237
+ if media_content
238
+ # Upload to your cloud storage (S3, Google Cloud, etc.)
239
+ cloud_url = upload_to_cloud_storage(media_content, media_type, media_id)
240
+
241
+ # Process based on media type
242
+ case media_type
243
+ when 'image'
244
+ process_image(cloud_url, user_phone)
245
+ when 'document'
246
+ process_document(cloud_url, user_phone)
247
+ when 'audio'
248
+ process_audio(cloud_url, user_phone)
249
+ when 'video'
250
+ process_video(cloud_url, user_phone)
251
+ end
252
+
253
+ Rails.logger.info "Successfully processed #{media_type} from #{user_phone}"
254
+ end
255
+ rescue => e
256
+ Rails.logger.error "Error processing media: #{e.message}"
257
+ @client.send_text(user_phone, "Sorry, I couldn't process your file. Please try again.")
258
+ end
259
+ end
260
+
261
+ # Send personalized content based on user data
262
+ def send_personalized_content(phone_number, user_id)
263
+ # Get user's preferred content from your system
264
+ user_content = fetch_user_content(user_id)
265
+
266
+ # Send personalized image
267
+ if user_content[:image_url]
268
+ @client.send_image(phone_number, user_content[:image_url], user_content[:image_caption])
269
+ end
270
+
271
+ # Send personalized document
272
+ if user_content[:document_url]
273
+ @client.send_document(phone_number, user_content[:document_url], user_content[:document_description])
274
+ end
275
+ end
276
+
277
+ # Send real-time generated content
278
+ def send_qr_code(phone_number, data)
279
+ # Generate QR code and get URL (using your QR service)
280
+ qr_url = generate_qr_code_url(data)
281
+
282
+ @client.send_image(phone_number, qr_url, "Here's your QR code!")
283
+ end
284
+
285
+ # Send chart/graph from analytics service
286
+ def send_analytics_chart(phone_number, chart_type, period)
287
+ # Generate chart URL from your analytics service
288
+ chart_url = generate_analytics_chart_url(chart_type, period)
289
+
290
+ @client.send_image(phone_number, chart_url, "#{chart_type.humanize} for #{period}")
291
+ end
292
+
293
+ private
294
+
295
+ def upload_to_cloud_storage(content, media_type, media_id)
296
+ # Your cloud storage upload logic here
297
+ # Return the public URL of the uploaded file
298
+ "https://storage.example.com/uploads/#{media_id}.#{get_file_extension(media_type)}"
299
+ end
300
+
301
+ def process_image(cloud_url, user_phone)
302
+ # Your image processing logic here
303
+ @client.send_text(user_phone, "Thanks for the image! I've processed it successfully. ✅")
304
+ end
305
+
306
+ def process_document(cloud_url, user_phone)
307
+ # Your document processing logic here
308
+ @client.send_text(user_phone, "Document received and processed! 📄")
309
+ end
310
+
311
+ def process_audio(cloud_url, user_phone)
312
+ # Your audio processing logic here
313
+ @client.send_text(user_phone, "Audio message processed! 🎵")
314
+ end
315
+
316
+ def process_video(cloud_url, user_phone)
317
+ # Your video processing logic here
318
+ @client.send_text(user_phone, "Video processed successfully! 🎥")
319
+ end
320
+
321
+ def fetch_user_content(user_id)
322
+ # Fetch personalized content URLs from your database
323
+ {
324
+ image_url: "https://cdn.example.com/personal/#{user_id}/welcome.jpg",
325
+ image_caption: "Your personalized welcome image!",
326
+ document_url: "https://storage.example.com/personal/#{user_id}/guide.pdf",
327
+ document_description: "Your personalized guide"
328
+ }
329
+ end
330
+
331
+ def generate_qr_code_url(data)
332
+ # Generate QR code URL using your service (or external API like QR Server)
333
+ "https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=#{CGI.escape(data)}"
334
+ end
335
+
336
+ def generate_analytics_chart_url(chart_type, period)
337
+ # Generate chart URL from your analytics service
338
+ "https://charts.example.com/api/generate?type=#{chart_type}&period=#{period}"
339
+ end
340
+
341
+ def get_file_extension(media_type)
342
+ case media_type
343
+ when 'image' then 'jpg'
344
+ when 'document' then 'pdf'
345
+ when 'audio' then 'mp3'
346
+ when 'video' then 'mp4'
347
+ else 'bin'
348
+ end
349
+ end
350
+ end
351
+
352
+ # ============================================================================
353
+ # USAGE IN CONTROLLERS
354
+ # ============================================================================
355
+
356
+ class NotificationController < ApplicationController
357
+ def send_media_notification
358
+ service = WhatsAppMediaService.new
359
+
360
+ # Send welcome package to new users
361
+ service.send_welcome_package(params[:phone_number], params[:user_name])
362
+
363
+ render json: { status: 'sent' }
364
+ end
365
+
366
+ def send_order_confirmation
367
+ service = WhatsAppMediaService.new
368
+
369
+ # Get invoice URL from your system (could be from S3, Google Cloud, etc.)
370
+ invoice_url = get_invoice_url(params[:order_id])
371
+
372
+ service.send_order_confirmation(
373
+ params[:phone_number],
374
+ params[:order_id],
375
+ invoice_url
376
+ )
377
+
378
+ render json: { status: 'sent' }
379
+ end
380
+
381
+ def send_promo
382
+ service = WhatsAppMediaService.new
383
+
384
+ # Promotional content from CDN
385
+ promo_image = "https://cdn.example.com/promos/#{params[:promo_id]}/banner.jpg"
386
+ promo_video = "https://cdn.example.com/promos/#{params[:promo_id]}/video.mp4"
387
+
388
+ service.send_promotion(params[:phone_number], promo_image, promo_video)
389
+
390
+ render json: { status: 'sent' }
391
+ end
392
+
393
+ def send_qr_code
394
+ service = WhatsAppMediaService.new
395
+ service.send_qr_code(params[:phone_number], params[:qr_data])
396
+
397
+ render json: { status: 'sent' }
398
+ end
399
+
400
+ private
401
+
402
+ def get_invoice_url(order_id)
403
+ # Your logic to get invoice URL from cloud storage
404
+ "https://storage.example.com/invoices/#{order_id}.pdf"
405
+ end
406
+ end
@@ -0,0 +1,111 @@
1
+ # Example Background Jobs for WhatsApp Response Delivery
2
+ # Add these to your Rails application
3
+
4
+ # Example: Basic WhatsApp Send Job
5
+ # Only handles sending responses - flows are processed synchronously in the controller
6
+ class WhatsappMessageJob < ApplicationJob
7
+ include FlowChat::Whatsapp::SendJobSupport
8
+
9
+ def perform(send_data)
10
+ perform_whatsapp_send(send_data)
11
+ end
12
+ end
13
+
14
+ # Example: Advanced WhatsApp Send Job with custom callbacks
15
+ class AdvancedWhatsappMessageJob < ApplicationJob
16
+ include FlowChat::Whatsapp::SendJobSupport
17
+
18
+ def perform(send_data)
19
+ perform_whatsapp_send(send_data)
20
+ end
21
+
22
+ private
23
+
24
+ # Override for custom success handling
25
+ def on_whatsapp_send_success(send_data, result)
26
+ Rails.logger.info "Successfully sent WhatsApp message to #{send_data[:msisdn]}"
27
+ UserEngagementTracker.track_message_sent(phone: send_data[:msisdn])
28
+ end
29
+
30
+ # Override for custom error handling
31
+ def on_whatsapp_send_error(error, send_data)
32
+ ErrorTracker.notify(error, user_phone: send_data[:msisdn])
33
+ end
34
+ end
35
+
36
+ # Example: Priority send job for urgent messages
37
+ class UrgentWhatsappSendJob < ApplicationJob
38
+ include FlowChat::Whatsapp::SendJobSupport
39
+
40
+ queue_as :urgent_whatsapp # Different queue for priority
41
+ retry_on StandardError, wait: 1.second, attempts: 5 # Override retry policy
42
+
43
+ def perform(send_data)
44
+ perform_whatsapp_send(send_data)
45
+ end
46
+
47
+ private
48
+
49
+ # Override error handling for urgent messages
50
+ def handle_whatsapp_send_error(error, send_data, config = nil)
51
+ # Immediately escalate urgent message failures
52
+ AlertingService.send_urgent_alert(
53
+ "Urgent WhatsApp send job failed",
54
+ error: error.message,
55
+ user: send_data[:msisdn]
56
+ )
57
+
58
+ # Still send user notification
59
+ super
60
+ end
61
+ end
62
+
63
+ # Example: Multi-tenant send job
64
+ class MultiTenantWhatsappSendJob < ApplicationJob
65
+ include FlowChat::Whatsapp::SendJobSupport
66
+
67
+ def perform(send_data)
68
+ perform_whatsapp_send(send_data)
69
+ end
70
+
71
+ private
72
+
73
+ # Override config resolution for tenant-specific configs
74
+ def resolve_whatsapp_config(send_data)
75
+ # Try tenant-specific config first
76
+ tenant_name = extract_tenant_from_phone(send_data[:msisdn])
77
+ if tenant_name && FlowChat::Whatsapp::Configuration.exists?(tenant_name)
78
+ return FlowChat::Whatsapp::Configuration.get(tenant_name)
79
+ end
80
+
81
+ # Fallback to default resolution
82
+ super
83
+ end
84
+
85
+ def extract_tenant_from_phone(phone)
86
+ # Extract tenant from phone number prefix or other identifier
87
+ case phone
88
+ when /^\+1800/
89
+ :enterprise
90
+ when /^\+1888/
91
+ :premium
92
+ else
93
+ :standard
94
+ end
95
+ end
96
+ end
97
+
98
+ # Usage in Rails configuration
99
+ #
100
+ # Add to config/application.rb:
101
+ # config.active_job.queue_adapter = :sidekiq
102
+ #
103
+ # Add to config/initializers/flowchat.rb:
104
+ # FlowChat::Config.whatsapp.message_handling_mode = :background
105
+ # FlowChat::Config.whatsapp.background_job_class = 'WhatsappMessageJob'
106
+ #
107
+ # How it works:
108
+ # 1. Controller receives WhatsApp webhook
109
+ # 2. Flow is processed synchronously (maintains controller context)
110
+ # 3. Response is queued for async delivery via background job
111
+ # 4. Job only handles sending the response, not processing flows