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.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/README.md +642 -86
- data/examples/initializer.rb +31 -0
- data/examples/media_prompts_examples.rb +28 -0
- data/examples/multi_tenant_whatsapp_controller.rb +244 -0
- data/examples/ussd_controller.rb +264 -0
- data/examples/whatsapp_controller.rb +140 -0
- data/examples/whatsapp_media_examples.rb +406 -0
- data/examples/whatsapp_message_job.rb +111 -0
- data/lib/flow_chat/base_processor.rb +67 -0
- data/lib/flow_chat/config.rb +36 -0
- data/lib/flow_chat/session/cache_session_store.rb +84 -0
- data/lib/flow_chat/session/middleware.rb +14 -6
- data/lib/flow_chat/simulator/controller.rb +78 -0
- data/lib/flow_chat/simulator/views/simulator.html.erb +1707 -0
- data/lib/flow_chat/ussd/app.rb +25 -0
- data/lib/flow_chat/ussd/gateway/nalo.rb +2 -0
- data/lib/flow_chat/ussd/gateway/nsano.rb +6 -0
- data/lib/flow_chat/ussd/middleware/resumable_session.rb +1 -1
- data/lib/flow_chat/ussd/processor.rb +14 -42
- data/lib/flow_chat/ussd/prompt.rb +39 -5
- data/lib/flow_chat/version.rb +1 -1
- data/lib/flow_chat/whatsapp/app.rb +64 -0
- data/lib/flow_chat/whatsapp/client.rb +439 -0
- data/lib/flow_chat/whatsapp/configuration.rb +113 -0
- data/lib/flow_chat/whatsapp/gateway/cloud_api.rb +213 -0
- data/lib/flow_chat/whatsapp/middleware/executor.rb +30 -0
- data/lib/flow_chat/whatsapp/processor.rb +26 -0
- data/lib/flow_chat/whatsapp/prompt.rb +251 -0
- data/lib/flow_chat/whatsapp/send_job_support.rb +79 -0
- data/lib/flow_chat/whatsapp/template_manager.rb +162 -0
- data/lib/flow_chat.rb +1 -0
- metadata +21 -3
- data/lib/flow_chat/ussd/simulator/controller.rb +0 -51
- data/lib/flow_chat/ussd/simulator/views/simulator.html.erb +0 -239
data/README.md
CHANGED
@@ -1,14 +1,16 @@
|
|
1
1
|
# FlowChat
|
2
2
|
|
3
|
-
FlowChat is a Rails framework designed for building sophisticated conversational workflows
|
3
|
+
FlowChat is a Rails framework designed for building sophisticated conversational workflows for both USSD (Unstructured Supplementary Service Data) systems and WhatsApp messaging. It provides an intuitive Ruby DSL for creating multi-step, menu-driven conversations with automatic session management, input validation, and flow control.
|
4
4
|
|
5
5
|
**Key Features:**
|
6
6
|
- ๐ฏ **Declarative Flow Definition** - Define conversation flows as Ruby classes
|
7
7
|
- ๐ **Automatic Session Management** - Persistent state across requests
|
8
8
|
- โ
**Input Validation & Transformation** - Built-in validation and data conversion
|
9
9
|
- ๐ **Middleware Architecture** - Flexible request processing pipeline
|
10
|
-
- ๐ฑ **USSD Gateway Support** - Currently supports Nalo
|
11
|
-
-
|
10
|
+
- ๐ฑ **USSD Gateway Support** - Currently supports Nalo gateways
|
11
|
+
- ๐ฌ **WhatsApp Integration** - Full WhatsApp Cloud API support with multiple processing modes
|
12
|
+
- ๐ง **Reusable WhatsApp Client** - Standalone client for out-of-band messaging
|
13
|
+
- ๐งช **Built-in Testing Tools** - Unified simulator for both USSD and WhatsApp testing
|
12
14
|
|
13
15
|
## Architecture Overview
|
14
16
|
|
@@ -21,9 +23,9 @@ User Input โ Gateway โ Session โ Pagination โ Custom โ Executor โ Fl
|
|
21
23
|
```
|
22
24
|
|
23
25
|
**Middleware Pipeline:**
|
24
|
-
- **Gateway**:
|
26
|
+
- **Gateway**: Communication with providers (USSD: Nalo, WhatsApp: Cloud API)
|
25
27
|
- **Session**: Load/save conversation state
|
26
|
-
- **Pagination**: Split long responses into pages
|
28
|
+
- **Pagination**: Split long responses into pages (USSD only)
|
27
29
|
- **Custom**: Your application middleware (logging, auth, etc.)
|
28
30
|
- **Executor**: Execute flow methods and handle interrupts
|
29
31
|
|
@@ -43,6 +45,10 @@ bundle install
|
|
43
45
|
|
44
46
|
## Quick Start
|
45
47
|
|
48
|
+
FlowChat supports both USSD and WhatsApp. Choose the platform that fits your needs:
|
49
|
+
|
50
|
+
### USSD Setup
|
51
|
+
|
46
52
|
### 1. Create Your First Flow
|
47
53
|
|
48
54
|
Create a flow class in `app/flow_chat/welcome_flow.rb`:
|
@@ -60,7 +66,7 @@ class WelcomeFlow < FlowChat::Flow
|
|
60
66
|
end
|
61
67
|
```
|
62
68
|
|
63
|
-
### 2. Set Up the Controller
|
69
|
+
### 2. Set Up the USSD Controller
|
64
70
|
|
65
71
|
Create a controller to handle USSD requests:
|
66
72
|
|
@@ -89,6 +95,501 @@ Rails.application.routes.draw do
|
|
89
95
|
end
|
90
96
|
```
|
91
97
|
|
98
|
+
๐ก **Tip**: See [examples/ussd_controller.rb](examples/ussd_controller.rb) for a complete USSD controller example with payment flows, customer support, and custom middleware.
|
99
|
+
|
100
|
+
### WhatsApp Setup
|
101
|
+
|
102
|
+
### 1. Configure WhatsApp Credentials
|
103
|
+
|
104
|
+
FlowChat supports two ways to configure WhatsApp credentials:
|
105
|
+
|
106
|
+
**Option A: Using Rails Credentials**
|
107
|
+
|
108
|
+
Add your WhatsApp credentials to Rails credentials:
|
109
|
+
|
110
|
+
```bash
|
111
|
+
rails credentials:edit
|
112
|
+
```
|
113
|
+
|
114
|
+
```yaml
|
115
|
+
whatsapp:
|
116
|
+
access_token: "your_access_token"
|
117
|
+
phone_number_id: "your_phone_number_id"
|
118
|
+
verify_token: "your_verify_token"
|
119
|
+
app_id: "your_app_id"
|
120
|
+
app_secret: "your_app_secret"
|
121
|
+
webhook_url: "your_webhook_url"
|
122
|
+
business_account_id: "your_business_account_id"
|
123
|
+
```
|
124
|
+
|
125
|
+
**Option B: Using Environment Variables**
|
126
|
+
|
127
|
+
Alternatively, you can use environment variables:
|
128
|
+
|
129
|
+
```bash
|
130
|
+
# Add to your .env file or environment
|
131
|
+
export WHATSAPP_ACCESS_TOKEN="your_access_token"
|
132
|
+
export WHATSAPP_PHONE_NUMBER_ID="your_phone_number_id"
|
133
|
+
export WHATSAPP_VERIFY_TOKEN="your_verify_token"
|
134
|
+
export WHATSAPP_APP_ID="your_app_id"
|
135
|
+
export WHATSAPP_APP_SECRET="your_app_secret"
|
136
|
+
export WHATSAPP_WEBHOOK_URL="your_webhook_url"
|
137
|
+
export WHATSAPP_BUSINESS_ACCOUNT_ID="your_business_account_id"
|
138
|
+
```
|
139
|
+
|
140
|
+
FlowChat will automatically use Rails credentials first, falling back to environment variables if credentials are not available.
|
141
|
+
|
142
|
+
**Option C: Per-Setup Configuration**
|
143
|
+
|
144
|
+
For multi-tenant applications or when you need different WhatsApp accounts per endpoint:
|
145
|
+
|
146
|
+
```ruby
|
147
|
+
# Create custom configuration
|
148
|
+
custom_config = FlowChat::Whatsapp::Configuration.new
|
149
|
+
custom_config.access_token = "your_specific_access_token"
|
150
|
+
custom_config.phone_number_id = "your_specific_phone_number_id"
|
151
|
+
custom_config.verify_token = "your_specific_verify_token"
|
152
|
+
custom_config.app_id = "your_specific_app_id"
|
153
|
+
custom_config.app_secret = "your_specific_app_secret"
|
154
|
+
custom_config.business_account_id = "your_specific_business_account_id"
|
155
|
+
|
156
|
+
# Use in processor
|
157
|
+
processor = FlowChat::Whatsapp::Processor.new(self) do |config|
|
158
|
+
config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi, custom_config
|
159
|
+
config.use_session_store FlowChat::Session::CacheSessionStore
|
160
|
+
end
|
161
|
+
```
|
162
|
+
|
163
|
+
๐ก **Tip**: See [examples/multi_tenant_whatsapp_controller.rb](examples/multi_tenant_whatsapp_controller.rb) for comprehensive multi-tenant and per-setup configuration examples.
|
164
|
+
|
165
|
+
### 2. Choose Message Handling Mode
|
166
|
+
|
167
|
+
FlowChat offers three WhatsApp message handling modes. Configure them in an initializer:
|
168
|
+
|
169
|
+
**Create an initializer** `config/initializers/flowchat.rb`:
|
170
|
+
|
171
|
+
```ruby
|
172
|
+
# config/initializers/flowchat.rb
|
173
|
+
|
174
|
+
# Configure WhatsApp message handling mode
|
175
|
+
FlowChat::Config.whatsapp.message_handling_mode = :inline # or :background, :simulator
|
176
|
+
FlowChat::Config.whatsapp.background_job_class = 'WhatsappMessageJob'
|
177
|
+
```
|
178
|
+
|
179
|
+
**Inline Mode (Default)** - Process messages synchronously:
|
180
|
+
```ruby
|
181
|
+
# config/initializers/flowchat.rb
|
182
|
+
FlowChat::Config.whatsapp.message_handling_mode = :inline
|
183
|
+
|
184
|
+
# app/controllers/whatsapp_controller.rb
|
185
|
+
class WhatsappController < ApplicationController
|
186
|
+
skip_forgery_protection
|
187
|
+
|
188
|
+
def webhook
|
189
|
+
processor = FlowChat::Whatsapp::Processor.new(self) do |config|
|
190
|
+
config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi
|
191
|
+
config.use_session_store FlowChat::Session::CacheSessionStore
|
192
|
+
end
|
193
|
+
|
194
|
+
processor.run WelcomeFlow, :main_page
|
195
|
+
end
|
196
|
+
end
|
197
|
+
```
|
198
|
+
|
199
|
+
**Background Mode** - Process flows synchronously, send responses asynchronously:
|
200
|
+
```ruby
|
201
|
+
# config/initializers/flowchat.rb
|
202
|
+
FlowChat::Config.whatsapp.message_handling_mode = :background
|
203
|
+
FlowChat::Config.whatsapp.background_job_class = 'WhatsappMessageJob'
|
204
|
+
|
205
|
+
# app/controllers/whatsapp_controller.rb
|
206
|
+
class WhatsappController < ApplicationController
|
207
|
+
skip_forgery_protection
|
208
|
+
|
209
|
+
def webhook
|
210
|
+
processor = FlowChat::Whatsapp::Processor.new(self) do |config|
|
211
|
+
config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi
|
212
|
+
config.use_session_store FlowChat::Session::CacheSessionStore
|
213
|
+
end
|
214
|
+
|
215
|
+
processor.run WelcomeFlow, :main_page
|
216
|
+
end
|
217
|
+
end
|
218
|
+
```
|
219
|
+
|
220
|
+
**Simulator Mode** - Return response data instead of sending via WhatsApp API:
|
221
|
+
```ruby
|
222
|
+
# config/initializers/flowchat.rb
|
223
|
+
FlowChat::Config.whatsapp.message_handling_mode = :simulator
|
224
|
+
|
225
|
+
# app/controllers/whatsapp_controller.rb
|
226
|
+
class WhatsappController < ApplicationController
|
227
|
+
skip_forgery_protection
|
228
|
+
|
229
|
+
def webhook
|
230
|
+
processor = FlowChat::Whatsapp::Processor.new(self) do |config|
|
231
|
+
config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi
|
232
|
+
config.use_session_store FlowChat::Session::CacheSessionStore
|
233
|
+
end
|
234
|
+
|
235
|
+
processor.run WelcomeFlow, :main_page
|
236
|
+
end
|
237
|
+
end
|
238
|
+
```
|
239
|
+
|
240
|
+
๐ก **See [examples/README_whatsapp_modes.md](examples/README_whatsapp_modes.md) for detailed mode explanations and use cases.**
|
241
|
+
|
242
|
+
### 3. Add WhatsApp Route
|
243
|
+
|
244
|
+
```ruby
|
245
|
+
Rails.application.routes.draw do
|
246
|
+
match '/whatsapp/webhook', to: 'whatsapp#webhook', via: [:get, :post]
|
247
|
+
end
|
248
|
+
```
|
249
|
+
|
250
|
+
### 4. Enhanced Features for WhatsApp
|
251
|
+
|
252
|
+
The same flow works for both USSD and WhatsApp, but WhatsApp provides additional data and better interactive features:
|
253
|
+
|
254
|
+
```ruby
|
255
|
+
class WelcomeFlow < FlowChat::Flow
|
256
|
+
def main_page
|
257
|
+
# Access WhatsApp-specific data
|
258
|
+
Rails.logger.info "Contact: #{app.contact_name}, Phone: #{app.phone_number}"
|
259
|
+
Rails.logger.info "Message ID: #{app.message_id}, Timestamp: #{app.timestamp}"
|
260
|
+
|
261
|
+
# Handle location sharing
|
262
|
+
if app.location
|
263
|
+
app.say "Thanks for sharing your location! We see you're at #{app.location['latitude']}, #{app.location['longitude']}"
|
264
|
+
return
|
265
|
+
end
|
266
|
+
|
267
|
+
# Handle media messages
|
268
|
+
if app.media
|
269
|
+
app.say "Thanks for the #{app.media['type']} file! We received: #{app.media['id']}"
|
270
|
+
return
|
271
|
+
end
|
272
|
+
|
273
|
+
name = app.screen(:name) do |prompt|
|
274
|
+
prompt.ask "Hello! Welcome to our WhatsApp service. What's your name?",
|
275
|
+
transform: ->(input) { input.strip.titleize }
|
276
|
+
end
|
277
|
+
|
278
|
+
# WhatsApp supports interactive buttons and lists via prompt.select
|
279
|
+
choice = app.screen(:main_menu) do |prompt|
|
280
|
+
prompt.select "Hi #{name}! How can I help you?", {
|
281
|
+
"info" => "๐ Get Information",
|
282
|
+
"support" => "๐ Contact Support",
|
283
|
+
"feedback" => "๐ฌ Give Feedback"
|
284
|
+
}
|
285
|
+
end
|
286
|
+
|
287
|
+
case choice
|
288
|
+
when "info"
|
289
|
+
show_information_menu
|
290
|
+
when "support"
|
291
|
+
contact_support
|
292
|
+
when "feedback"
|
293
|
+
collect_feedback
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
private
|
298
|
+
|
299
|
+
def show_information_menu
|
300
|
+
info_choice = app.screen(:info_menu) do |prompt|
|
301
|
+
prompt.select "What information do you need?", {
|
302
|
+
"hours" => "๐ Business Hours",
|
303
|
+
"location" => "๐ Our Location",
|
304
|
+
"services" => "๐ผ Our Services"
|
305
|
+
}
|
306
|
+
end
|
307
|
+
|
308
|
+
case info_choice
|
309
|
+
when "hours"
|
310
|
+
app.say "We're open Monday-Friday 9AM-6PM, Saturday 10AM-4PM. Closed Sundays."
|
311
|
+
when "location"
|
312
|
+
app.say "๐ Visit us at 123 Main Street, Downtown. We're next to the coffee shop!"
|
313
|
+
when "services"
|
314
|
+
app.say "๐ผ We offer: Web Development, Mobile Apps, Cloud Services, and IT Consulting."
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
def contact_support
|
319
|
+
support_choice = app.screen(:support_menu) do |prompt|
|
320
|
+
prompt.select "How would you like to contact support?", {
|
321
|
+
"call" => "๐ Call Us",
|
322
|
+
"email" => "๐ง Email Us",
|
323
|
+
"chat" => "๐ฌ Continue Here"
|
324
|
+
}
|
325
|
+
end
|
326
|
+
|
327
|
+
case support_choice
|
328
|
+
when "call"
|
329
|
+
app.say "๐ Call us at: +1-555-HELP (4357)\nAvailable Mon-Fri 9AM-5PM"
|
330
|
+
when "email"
|
331
|
+
app.say "๐ง Email us at: support@company.com\nWe typically respond within 24 hours"
|
332
|
+
when "chat"
|
333
|
+
app.say "๐ฌ Great! Please describe your issue and we'll help you right away."
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
def collect_feedback
|
338
|
+
rating = app.screen(:rating) do |prompt|
|
339
|
+
prompt.select "How would you rate our service?", {
|
340
|
+
"5" => "โญโญโญโญโญ Excellent",
|
341
|
+
"4" => "โญโญโญโญ Good",
|
342
|
+
"3" => "โญโญโญ Average",
|
343
|
+
"2" => "โญโญ Poor",
|
344
|
+
"1" => "โญ Very Poor"
|
345
|
+
}
|
346
|
+
end
|
347
|
+
|
348
|
+
feedback = app.screen(:feedback_text) do |prompt|
|
349
|
+
prompt.ask "Thank you for the #{rating}-star rating! Please share any additional feedback:"
|
350
|
+
end
|
351
|
+
|
352
|
+
# Use WhatsApp-specific data for logging
|
353
|
+
Rails.logger.info "Feedback from #{app.contact_name} (#{app.phone_number}): #{rating} stars - #{feedback}"
|
354
|
+
|
355
|
+
app.say "Thank you for your feedback! We really appreciate it. ๐"
|
356
|
+
end
|
357
|
+
end
|
358
|
+
```
|
359
|
+
|
360
|
+
For detailed WhatsApp setup instructions, see [WhatsApp Integration Guide](docs/whatsapp_setup.md).
|
361
|
+
|
362
|
+
## ๐ง Reusable WhatsApp Client
|
363
|
+
|
364
|
+
FlowChat provides a standalone WhatsApp client for out-of-band messaging:
|
365
|
+
|
366
|
+
```ruby
|
367
|
+
# Initialize client
|
368
|
+
config = FlowChat::Whatsapp::Configuration.from_credentials
|
369
|
+
client = FlowChat::Whatsapp::Client.new(config)
|
370
|
+
|
371
|
+
# Send text message
|
372
|
+
client.send_text("+1234567890", "Hello, World!")
|
373
|
+
|
374
|
+
# Send interactive buttons
|
375
|
+
client.send_buttons(
|
376
|
+
"+1234567890",
|
377
|
+
"Choose an option:",
|
378
|
+
[
|
379
|
+
{ id: 'option1', title: 'Option 1' },
|
380
|
+
{ id: 'option2', title: 'Option 2' }
|
381
|
+
]
|
382
|
+
)
|
383
|
+
|
384
|
+
# Send interactive list
|
385
|
+
client.send_list(
|
386
|
+
"+1234567890",
|
387
|
+
"Select from menu:",
|
388
|
+
[
|
389
|
+
{
|
390
|
+
title: "Services",
|
391
|
+
rows: [
|
392
|
+
{ id: 'service1', title: 'Service 1', description: 'Description 1' },
|
393
|
+
{ id: 'service2', title: 'Service 2', description: 'Description 2' }
|
394
|
+
]
|
395
|
+
}
|
396
|
+
]
|
397
|
+
)
|
398
|
+
|
399
|
+
# Handle media
|
400
|
+
media_url = client.get_media_url("media_id_123")
|
401
|
+
media_content = client.download_media("media_id_123")
|
402
|
+
```
|
403
|
+
|
404
|
+
### Out-of-Band Messaging Service Example
|
405
|
+
|
406
|
+
```ruby
|
407
|
+
class NotificationService
|
408
|
+
def initialize
|
409
|
+
@config = FlowChat::Whatsapp::Configuration.from_credentials
|
410
|
+
@client = FlowChat::Whatsapp::Client.new(@config)
|
411
|
+
end
|
412
|
+
|
413
|
+
def send_order_confirmation(phone_number, order_id, items, total)
|
414
|
+
item_list = items.map { |item| "โข #{item[:name]} x#{item[:quantity]}" }.join("\n")
|
415
|
+
|
416
|
+
@client.send_buttons(
|
417
|
+
phone_number,
|
418
|
+
"โ
Order Confirmed!\n\nOrder ##{order_id}\n\n#{item_list}\n\nTotal: $#{total}",
|
419
|
+
[
|
420
|
+
{ id: 'track_order', title: '๐ฆ Track Order' },
|
421
|
+
{ id: 'contact_support', title: '๐ฌ Contact Support' }
|
422
|
+
]
|
423
|
+
)
|
424
|
+
end
|
425
|
+
|
426
|
+
def send_appointment_reminder(phone_number, appointment)
|
427
|
+
@client.send_buttons(
|
428
|
+
phone_number,
|
429
|
+
"๐ฅ Appointment Reminder\n\n#{appointment[:service]} with #{appointment[:provider]}\n๐
#{appointment[:date]}\n๐ #{appointment[:time]}",
|
430
|
+
[
|
431
|
+
{ id: 'confirm', title: 'โ
Confirm' },
|
432
|
+
{ id: 'reschedule', title: '๐
Reschedule' },
|
433
|
+
{ id: 'cancel', title: 'โ Cancel' }
|
434
|
+
]
|
435
|
+
)
|
436
|
+
end
|
437
|
+
end
|
438
|
+
```
|
439
|
+
|
440
|
+
๐ก **See [examples/whatsapp_controller_modes.rb](examples/whatsapp_controller_modes.rb) for comprehensive usage examples.**
|
441
|
+
|
442
|
+
## Cross-Platform Compatibility
|
443
|
+
|
444
|
+
FlowChat provides a unified API that works across both USSD and WhatsApp platforms, with graceful degradation for platform-specific features:
|
445
|
+
|
446
|
+
### Shared Features (Both USSD & WhatsApp)
|
447
|
+
- โ
`app.screen()` - Interactive screens with prompts
|
448
|
+
- โ
`app.say()` - Send messages to users
|
449
|
+
- โ
`prompt.ask()` - Text input collection
|
450
|
+
- โ
`prompt.select()` - Menu selection (renders as numbered list in USSD, interactive buttons/lists in WhatsApp)
|
451
|
+
- โ
`prompt.yes?()` - Yes/no questions
|
452
|
+
- โ
`app.phone_number` - User's phone number
|
453
|
+
- โ
`app.message_id` - Unique message identifier
|
454
|
+
- โ
`app.timestamp` - Message timestamp
|
455
|
+
|
456
|
+
### WhatsApp-Only Features
|
457
|
+
- โ
`app.contact_name` - WhatsApp contact name (returns `nil` in USSD)
|
458
|
+
- โ
`app.location` - Location sharing data (returns `nil` in USSD)
|
459
|
+
- โ
`app.media` - Media file attachments (returns `nil` in USSD)
|
460
|
+
- โ
Rich interactive elements (buttons, lists) automatically generated from `prompt.select()`
|
461
|
+
|
462
|
+
This design allows you to write flows once and deploy them on both platforms, with WhatsApp users getting enhanced interactive features automatically.
|
463
|
+
|
464
|
+
## ๐ฑ Media Support
|
465
|
+
|
466
|
+
FlowChat supports rich media attachments for enhanced conversational experiences. Media can be attached to `ask()` and `say()` prompts, with automatic cross-platform optimization.
|
467
|
+
|
468
|
+
### Supported Media Types
|
469
|
+
|
470
|
+
- **๐ท Images** (`type: :image`) - Photos, screenshots, diagrams
|
471
|
+
- **๐ Documents** (`type: :document`) - PDFs, forms, receipts
|
472
|
+
- **๐ฅ Videos** (`type: :video`) - Tutorials, demos, explanations
|
473
|
+
- **๐ต Audio** (`type: :audio`) - Voice messages, recordings
|
474
|
+
- **๐ Stickers** (`type: :sticker`) - Fun visual elements
|
475
|
+
|
476
|
+
### Basic Usage
|
477
|
+
|
478
|
+
```ruby
|
479
|
+
class ProductFlow < FlowChat::Flow
|
480
|
+
def main_page
|
481
|
+
# โ
Text input with context image
|
482
|
+
feedback = app.screen(:feedback) do |prompt|
|
483
|
+
prompt.ask "What do you think of our new product?",
|
484
|
+
media: {
|
485
|
+
type: :image,
|
486
|
+
url: "https://cdn.example.com/products/new_product.jpg"
|
487
|
+
}
|
488
|
+
end
|
489
|
+
|
490
|
+
# โ
Send informational media
|
491
|
+
app.say "Thanks for your feedback! Here's what's coming next:",
|
492
|
+
media: {
|
493
|
+
type: :video,
|
494
|
+
url: "https://videos.example.com/roadmap.mp4"
|
495
|
+
}
|
496
|
+
|
497
|
+
# โ
Document with filename
|
498
|
+
app.say "Here's your receipt:",
|
499
|
+
media: {
|
500
|
+
type: :document,
|
501
|
+
url: "https://api.example.com/receipt.pdf",
|
502
|
+
filename: "receipt.pdf"
|
503
|
+
}
|
504
|
+
end
|
505
|
+
end
|
506
|
+
```
|
507
|
+
|
508
|
+
### Media Hash Format
|
509
|
+
|
510
|
+
```ruby
|
511
|
+
{
|
512
|
+
type: :image, # Required: :image, :document, :audio, :video, :sticker
|
513
|
+
url: "https://...", # Required: URL to the media file OR WhatsApp media ID
|
514
|
+
filename: "doc.pdf" # Optional: Only for documents
|
515
|
+
}
|
516
|
+
```
|
517
|
+
|
518
|
+
### Using WhatsApp Media IDs
|
519
|
+
|
520
|
+
For better performance and to avoid external dependencies, you can upload files to WhatsApp and use the media ID:
|
521
|
+
|
522
|
+
```ruby
|
523
|
+
# Upload a file first
|
524
|
+
client = FlowChat::Whatsapp::Client.new(config)
|
525
|
+
media_id = client.upload_media('path/to/image.jpg', 'image/jpeg')
|
526
|
+
|
527
|
+
# Then use the media ID in your flow
|
528
|
+
app.screen(:product_demo) do |prompt|
|
529
|
+
prompt.ask "What do you think?",
|
530
|
+
media: {
|
531
|
+
type: :image,
|
532
|
+
url: media_id # Use the media ID instead of URL
|
533
|
+
}
|
534
|
+
end
|
535
|
+
```
|
536
|
+
|
537
|
+
### Client Media Methods
|
538
|
+
|
539
|
+
The WhatsApp client provides methods for uploading and sending media:
|
540
|
+
|
541
|
+
```ruby
|
542
|
+
client = FlowChat::Whatsapp::Client.new(config)
|
543
|
+
|
544
|
+
# Upload media and get media ID
|
545
|
+
media_id = client.upload_media('image.jpg', 'image/jpeg')
|
546
|
+
media_id = client.upload_media(file_io, 'image/jpeg', 'photo.jpg')
|
547
|
+
|
548
|
+
# Send media directly
|
549
|
+
client.send_image("+1234567890", "https://example.com/image.jpg", "Caption")
|
550
|
+
client.send_image("+1234567890", media_id, "Caption")
|
551
|
+
|
552
|
+
# Send document with MIME type and filename
|
553
|
+
client.send_document("+1234567890", "https://example.com/doc.pdf", "Your receipt", "receipt.pdf", "application/pdf")
|
554
|
+
|
555
|
+
# Send other media types
|
556
|
+
client.send_video("+1234567890", "https://example.com/video.mp4", "Demo video", "video/mp4")
|
557
|
+
client.send_audio("+1234567890", "https://example.com/audio.mp3", "audio/mpeg")
|
558
|
+
client.send_sticker("+1234567890", "https://example.com/sticker.webp", "image/webp")
|
559
|
+
```
|
560
|
+
|
561
|
+
### Cross-Platform Behavior
|
562
|
+
|
563
|
+
**WhatsApp Experience:**
|
564
|
+
- Media is sent directly to the chat
|
565
|
+
- Prompt text becomes the media caption
|
566
|
+
- Rich, native messaging experience
|
567
|
+
|
568
|
+
**USSD Experience:**
|
569
|
+
- Media URL is included in text message
|
570
|
+
- Graceful degradation with clear media indicators
|
571
|
+
- Users can access media via the provided link
|
572
|
+
|
573
|
+
```ruby
|
574
|
+
# This code works on both platforms:
|
575
|
+
app.screen(:help) do |prompt|
|
576
|
+
prompt.ask "Describe your issue:",
|
577
|
+
media: {
|
578
|
+
type: :image,
|
579
|
+
url: "https://support.example.com/help_example.jpg"
|
580
|
+
}
|
581
|
+
end
|
582
|
+
```
|
583
|
+
|
584
|
+
**WhatsApp Result:** Image sent with caption "Describe your issue:"
|
585
|
+
|
586
|
+
**USSD Result:**
|
587
|
+
```
|
588
|
+
Describe your issue:
|
589
|
+
|
590
|
+
๐ท Image: https://support.example.com/help_example.jpg
|
591
|
+
```
|
592
|
+
|
92
593
|
## Core Concepts
|
93
594
|
|
94
595
|
### Flows and Screens
|
@@ -228,6 +729,68 @@ app.screen(:credit_card) do |prompt|
|
|
228
729
|
end
|
229
730
|
```
|
230
731
|
|
732
|
+
### Background Job Support
|
733
|
+
|
734
|
+
For high-volume WhatsApp applications, use background response delivery:
|
735
|
+
|
736
|
+
```ruby
|
737
|
+
# app/jobs/whatsapp_message_job.rb
|
738
|
+
class WhatsappMessageJob < ApplicationJob
|
739
|
+
include FlowChat::Whatsapp::SendJobSupport
|
740
|
+
|
741
|
+
def perform(send_data)
|
742
|
+
perform_whatsapp_send(send_data)
|
743
|
+
end
|
744
|
+
end
|
745
|
+
|
746
|
+
# config/initializers/flowchat.rb
|
747
|
+
FlowChat::Config.whatsapp.message_handling_mode = :background
|
748
|
+
FlowChat::Config.whatsapp.background_job_class = 'WhatsappMessageJob'
|
749
|
+
|
750
|
+
# config/application.rb
|
751
|
+
config.active_job.queue_adapter = :sidekiq
|
752
|
+
```
|
753
|
+
|
754
|
+
**The `SendJobSupport` module provides:**
|
755
|
+
- โ
**Automatic config resolution** - Resolves named configurations automatically
|
756
|
+
- โ
**Response delivery** - Handles sending responses to WhatsApp
|
757
|
+
- โ
**Error handling** - Comprehensive error handling with user notifications
|
758
|
+
- โ
**Retry logic** - Built-in exponential backoff retry
|
759
|
+
- โ
**Extensible** - Override methods for custom behavior
|
760
|
+
|
761
|
+
**How it works:**
|
762
|
+
1. **Controller receives webhook** - WhatsApp message arrives
|
763
|
+
2. **Flow processes synchronously** - Maintains controller context and session state
|
764
|
+
3. **Response queued for delivery** - Only the sending is moved to background
|
765
|
+
4. **Job sends response** - Background job handles API call to WhatsApp
|
766
|
+
|
767
|
+
**Advanced job with custom callbacks:**
|
768
|
+
|
769
|
+
```ruby
|
770
|
+
class AdvancedWhatsappMessageJob < ApplicationJob
|
771
|
+
include FlowChat::Whatsapp::SendJobSupport
|
772
|
+
|
773
|
+
def perform(send_data)
|
774
|
+
perform_whatsapp_send(send_data)
|
775
|
+
end
|
776
|
+
|
777
|
+
private
|
778
|
+
|
779
|
+
# Override for custom success handling
|
780
|
+
def on_whatsapp_send_success(send_data, result)
|
781
|
+
Rails.logger.info "Successfully sent WhatsApp message to #{send_data[:msisdn]}"
|
782
|
+
UserEngagementTracker.track_message_sent(phone: send_data[:msisdn])
|
783
|
+
end
|
784
|
+
|
785
|
+
# Override for custom error handling
|
786
|
+
def on_whatsapp_send_error(error, send_data)
|
787
|
+
ErrorTracker.notify(error, user_phone: send_data[:msisdn])
|
788
|
+
end
|
789
|
+
end
|
790
|
+
```
|
791
|
+
|
792
|
+
๐ก **See [examples/whatsapp_message_job.rb](examples/whatsapp_message_job.rb) for complete job implementation examples.**
|
793
|
+
|
231
794
|
### Middleware Configuration
|
232
795
|
|
233
796
|
FlowChat uses a **middleware architecture** to process USSD requests through a configurable pipeline. Each request flows through multiple middleware layers in a specific order.
|
@@ -240,7 +803,7 @@ When you run a flow, FlowChat automatically builds this middleware stack:
|
|
240
803
|
User Input โ Gateway โ Session โ Pagination โ Custom Middleware โ Executor โ Flow
|
241
804
|
```
|
242
805
|
|
243
|
-
1. **Gateway Middleware** - Handles USSD provider communication (Nalo
|
806
|
+
1. **Gateway Middleware** - Handles USSD provider communication (Nalo)
|
244
807
|
2. **Session Middleware** - Manages session storage and retrieval
|
245
808
|
3. **Pagination Middleware** - Automatically splits long responses across pages
|
246
809
|
4. **Custom Middleware** - Your application-specific middleware (optional)
|
@@ -338,9 +901,6 @@ FlowChat supports multiple USSD gateways:
|
|
338
901
|
```ruby
|
339
902
|
# Nalo Solutions Gateway
|
340
903
|
config.use_gateway FlowChat::Ussd::Gateway::Nalo
|
341
|
-
|
342
|
-
# Nsano Gateway
|
343
|
-
config.use_gateway FlowChat::Ussd::Gateway::Nsano
|
344
904
|
```
|
345
905
|
|
346
906
|
## Testing
|
@@ -465,27 +1025,79 @@ class ProcessorMiddlewareTest < Minitest::Test
|
|
465
1025
|
end
|
466
1026
|
```
|
467
1027
|
|
468
|
-
###
|
1028
|
+
### FlowChat Unified Simulator
|
469
1029
|
|
470
|
-
Use the built-in simulator for interactive testing:
|
1030
|
+
Use the built-in unified simulator for interactive testing of both USSD and WhatsApp flows:
|
471
1031
|
|
472
1032
|
```ruby
|
473
|
-
class
|
474
|
-
include FlowChat::
|
1033
|
+
class SimulatorController < ApplicationController
|
1034
|
+
include FlowChat::Simulator::Controller
|
1035
|
+
|
1036
|
+
def index
|
1037
|
+
flowchat_simulator
|
1038
|
+
end
|
475
1039
|
|
476
1040
|
protected
|
477
1041
|
|
478
|
-
def
|
479
|
-
|
1042
|
+
def configurations
|
1043
|
+
{
|
1044
|
+
production_ussd: {
|
1045
|
+
name: "Production USSD",
|
1046
|
+
icon: "๐ญ",
|
1047
|
+
processor_type: "ussd",
|
1048
|
+
provider: "nalo",
|
1049
|
+
endpoint: "/ussd",
|
1050
|
+
color: "#28a745"
|
1051
|
+
},
|
1052
|
+
staging_whatsapp: {
|
1053
|
+
name: "Staging WhatsApp",
|
1054
|
+
icon: "๐งช",
|
1055
|
+
processor_type: "whatsapp",
|
1056
|
+
provider: "cloud_api",
|
1057
|
+
endpoint: "/whatsapp/webhook",
|
1058
|
+
color: "#17a2b8"
|
1059
|
+
},
|
1060
|
+
local_ussd: {
|
1061
|
+
name: "Local USSD",
|
1062
|
+
icon: "๐ป",
|
1063
|
+
processor_type: "ussd",
|
1064
|
+
provider: "nalo",
|
1065
|
+
endpoint: "http://localhost:3000/ussd",
|
1066
|
+
color: "#6f42c1"
|
1067
|
+
}
|
1068
|
+
}
|
480
1069
|
end
|
481
1070
|
|
482
|
-
def
|
483
|
-
:
|
1071
|
+
def default_config_key
|
1072
|
+
:local_ussd
|
1073
|
+
end
|
1074
|
+
|
1075
|
+
def default_phone_number
|
1076
|
+
"+254712345678"
|
1077
|
+
end
|
1078
|
+
|
1079
|
+
def default_contact_name
|
1080
|
+
"John Doe"
|
484
1081
|
end
|
485
1082
|
end
|
486
1083
|
```
|
487
1084
|
|
488
|
-
Add to routes and visit `http://localhost:3000/
|
1085
|
+
Add to routes and visit `http://localhost:3000/simulator`.
|
1086
|
+
|
1087
|
+
**Key Features:**
|
1088
|
+
- ๐ **Platform Toggle** - Switch between USSD and WhatsApp modes with configuration selection
|
1089
|
+
- ๐ฑ **USSD Mode** - Classic green-screen terminal simulation with provider support (Nalo, Nsano)
|
1090
|
+
- ๐ฌ **WhatsApp Mode** - Full WhatsApp interface with interactive buttons, lists, and rich messaging
|
1091
|
+
- โ๏ธ **Multi-Environment** - Support for different configurations (local, staging, production)
|
1092
|
+
- ๐จ **Modern UI** - Beautiful, responsive interface with real-time status indicators
|
1093
|
+
- ๐ **Request Logging** - View all HTTP requests and responses in real-time
|
1094
|
+
- ๐ง **Developer Tools** - Character counts, connection status, and comprehensive error handling
|
1095
|
+
|
1096
|
+
The simulator automatically adapts its interface based on the selected configuration:
|
1097
|
+
- **USSD**: Shows traditional terminal-style interface with character limits and pagination
|
1098
|
+
- **WhatsApp**: Displays realistic WhatsApp chat interface with support for interactive elements
|
1099
|
+
|
1100
|
+
๐ก **Tip**: See [examples/simulator_controller.rb](examples/simulator_controller.rb) for advanced configurations including multi-tenant support and environment-specific endpoints.
|
489
1101
|
|
490
1102
|
## Best Practices
|
491
1103
|
|
@@ -554,76 +1166,20 @@ def main_page
|
|
554
1166
|
end
|
555
1167
|
```
|
556
1168
|
|
557
|
-
|
558
|
-
|
559
|
-
### Session Storage Options
|
1169
|
+
### 5. Choose the Right WhatsApp Mode
|
560
1170
|
|
561
|
-
Configure
|
1171
|
+
Configure the appropriate mode in your initializer:
|
562
1172
|
|
563
1173
|
```ruby
|
564
|
-
#
|
565
|
-
config.use_session_store FlowChat::Session::RailsSessionStore
|
566
|
-
|
567
|
-
# Custom session store
|
568
|
-
class MySessionStore
|
569
|
-
def initialize(context)
|
570
|
-
@context = context
|
571
|
-
end
|
572
|
-
|
573
|
-
def get(key)
|
574
|
-
# Your storage logic
|
575
|
-
end
|
576
|
-
|
577
|
-
def set(key, value)
|
578
|
-
# Your storage logic
|
579
|
-
end
|
580
|
-
end
|
581
|
-
|
582
|
-
config.use_session_store MySessionStore
|
583
|
-
```
|
584
|
-
|
585
|
-
## Development
|
586
|
-
|
587
|
-
### Running Tests
|
588
|
-
|
589
|
-
FlowChat uses Minitest for testing:
|
590
|
-
|
591
|
-
```bash
|
592
|
-
# Run all tests
|
593
|
-
bundle exec rake test
|
594
|
-
|
595
|
-
# Run specific test file
|
596
|
-
bundle exec rake test TEST=test/unit/flow_test.rb
|
597
|
-
|
598
|
-
# Run specific test
|
599
|
-
bundle exec rake test TESTOPTS="--name=test_flow_initialization"
|
600
|
-
```
|
601
|
-
|
602
|
-
### Contributing
|
603
|
-
|
604
|
-
1. Fork the repository
|
605
|
-
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
606
|
-
3. Add tests for your changes
|
607
|
-
4. Ensure all tests pass (`bundle exec rake test`)
|
608
|
-
5. Commit your changes (`git commit -am 'Add amazing feature'`)
|
609
|
-
6. Push to the branch (`git push origin feature/amazing-feature`)
|
610
|
-
7. Open a Pull Request
|
611
|
-
|
612
|
-
## Roadmap
|
613
|
-
|
614
|
-
- ๐ฑ **WhatsApp Integration** - Support for WhatsApp Business API
|
615
|
-
- ๐ฌ **Telegram Bot Support** - Native Telegram bot integration
|
616
|
-
- ๐ **Sub-flows** - Reusable conversation components
|
617
|
-
- ๐ **Analytics Integration** - Built-in conversation analytics
|
618
|
-
- ๐ **Multi-language Support** - Internationalization features
|
619
|
-
- โก **Performance Optimizations** - Improved middleware performance
|
620
|
-
|
621
|
-
## License
|
1174
|
+
# config/initializers/flowchat.rb
|
622
1175
|
|
623
|
-
|
1176
|
+
# Development/Testing - use simulator mode
|
1177
|
+
FlowChat::Config.whatsapp.message_handling_mode = :simulator
|
624
1178
|
|
625
|
-
|
1179
|
+
# Low-volume Applications - use inline mode
|
1180
|
+
FlowChat::Config.whatsapp.message_handling_mode = :inline
|
626
1181
|
|
627
|
-
-
|
628
|
-
|
629
|
-
|
1182
|
+
# High-volume Production - use background mode (sync processing + async sending)
|
1183
|
+
FlowChat::Config.whatsapp.message_handling_mode = :background
|
1184
|
+
FlowChat::Config.whatsapp.background_job_class = 'WhatsappMessageJob'
|
1185
|
+
```
|