flow_chat 0.8.1 → 0.9.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.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/.cliff.toml +74 -0
  3. data/.github/workflows/ci.yml +2 -3
  4. data/.github/workflows/release.yml +56 -0
  5. data/.standard.yml +4 -0
  6. data/CHANGELOG.md +22 -0
  7. data/CLAUDE.md +327 -0
  8. data/CONTRIBUTING.md +134 -0
  9. data/Gemfile +1 -0
  10. data/README.md +290 -105
  11. data/Rakefile +5 -1
  12. data/SECURITY.md +42 -349
  13. data/docs/architecture.md +510 -0
  14. data/docs/async-background-processing.md +298 -0
  15. data/docs/configuration.md +556 -226
  16. data/docs/factory-pattern.md +355 -0
  17. data/docs/gateway-context-variables.md +171 -0
  18. data/docs/gateway-development.md +723 -0
  19. data/docs/getting-started.md +429 -0
  20. data/docs/instrumentation.md +264 -153
  21. data/docs/platforms/telegram.md +1013 -0
  22. data/docs/platforms/ussd.md +693 -0
  23. data/docs/platforms/whatsapp.md +1395 -0
  24. data/docs/testing.md +243 -365
  25. data/examples/custom_session_id_example.rb +119 -0
  26. data/examples/http_controller.rb +154 -0
  27. data/examples/intercom_configuration_example.rb +118 -0
  28. data/examples/intercom_controller.rb +194 -0
  29. data/examples/multi_tenant_whatsapp_controller.rb +4 -4
  30. data/examples/simulator_controller.rb +21 -1
  31. data/examples/ussd_controller.rb +10 -10
  32. data/examples/whatsapp_controller.rb +2 -2
  33. data/flow_chat.gemspec +4 -0
  34. data/lib/flow_chat/{base_app.rb → app.rb} +28 -9
  35. data/lib/flow_chat/async_job.rb +176 -0
  36. data/lib/flow_chat/config.rb +23 -28
  37. data/lib/flow_chat/{base_executor.rb → executor.rb} +7 -12
  38. data/lib/flow_chat/factory.rb +94 -0
  39. data/lib/flow_chat/gateway_async_support.rb +106 -0
  40. data/lib/flow_chat/generic_async_job.rb +30 -0
  41. data/lib/flow_chat/http/gateway/simple.rb +125 -0
  42. data/lib/flow_chat/http/renderer.rb +41 -0
  43. data/lib/flow_chat/instrumentation/setup.rb +1 -1
  44. data/lib/flow_chat/instrumentation.rb +25 -0
  45. data/lib/flow_chat/intercom/client.rb +155 -0
  46. data/lib/flow_chat/intercom/configuration.rb +149 -0
  47. data/lib/flow_chat/intercom/gateway/intercom_api.rb +396 -0
  48. data/lib/flow_chat/intercom/renderer.rb +71 -0
  49. data/lib/flow_chat/phone_number_util.rb +49 -0
  50. data/lib/flow_chat/processor.rb +188 -0
  51. data/lib/flow_chat/renderers/markdown_support.rb +58 -0
  52. data/lib/flow_chat/session/cache_session_store.rb +1 -17
  53. data/lib/flow_chat/session/middleware.rb +43 -26
  54. data/lib/flow_chat/simulator/controller.rb +17 -5
  55. data/lib/flow_chat/simulator/views/simulator.html.erb +220 -8
  56. data/lib/flow_chat/telegram/client.rb +240 -0
  57. data/lib/flow_chat/telegram/configuration.rb +118 -0
  58. data/lib/flow_chat/telegram/gateway/bot_api.rb +300 -0
  59. data/lib/flow_chat/telegram/middleware/choice_mapper.rb +35 -0
  60. data/lib/flow_chat/telegram/renderer.rb +125 -0
  61. data/lib/flow_chat/telegram.rb +7 -0
  62. data/lib/flow_chat/ussd/gateway/nalo.rb +27 -11
  63. data/lib/flow_chat/ussd/middleware/pagination.rb +9 -5
  64. data/lib/flow_chat/ussd/renderer.rb +1 -1
  65. data/lib/flow_chat/version.rb +1 -1
  66. data/lib/flow_chat/whatsapp/client.rb +144 -13
  67. data/lib/flow_chat/whatsapp/configuration.rb +1 -1
  68. data/lib/flow_chat/whatsapp/gateway/cloud_api.rb +144 -106
  69. data/lib/flow_chat/whatsapp/id_generator.rb +124 -0
  70. data/lib/flow_chat/whatsapp/middleware/choice_mapper.rb +147 -0
  71. data/lib/flow_chat/whatsapp/renderer.rb +145 -11
  72. data/lib/flow_chat.rb +11 -1
  73. data/lib/tasks/release.rake +165 -0
  74. metadata +88 -21
  75. data/docs/flows.md +0 -320
  76. data/docs/images/simulator.png +0 -0
  77. data/docs/media.md +0 -153
  78. data/docs/sessions.md +0 -433
  79. data/docs/ussd-setup.md +0 -322
  80. data/docs/whatsapp-setup.md +0 -162
  81. data/examples/whatsapp_message_job.rb +0 -113
  82. data/lib/flow_chat/base_processor.rb +0 -145
  83. data/lib/flow_chat/session/rails_session_store.rb +0 -68
  84. data/lib/flow_chat/ussd/app.rb +0 -6
  85. data/lib/flow_chat/ussd/gateway/nsano.rb +0 -98
  86. data/lib/flow_chat/ussd/middleware/executor.rb +0 -24
  87. data/lib/flow_chat/ussd/processor.rb +0 -39
  88. data/lib/flow_chat/whatsapp/app.rb +0 -29
  89. data/lib/flow_chat/whatsapp/middleware/executor.rb +0 -24
  90. data/lib/flow_chat/whatsapp/processor.rb +0 -32
  91. data/lib/flow_chat/whatsapp/send_job_support.rb +0 -79
@@ -0,0 +1,429 @@
1
+ # Getting Started with FlowChat
2
+
3
+ This guide will walk you through setting up FlowChat and building your first conversational application that works across multiple platforms.
4
+
5
+ ## Prerequisites
6
+
7
+ - Ruby 2.3+ (Ruby 3.0+ recommended)
8
+ - Rails 6.0+ (Rails 7.0+ recommended)
9
+ - Basic understanding of Rails controllers and routing
10
+
11
+ ## Installation
12
+
13
+ Add FlowChat to your Rails application:
14
+
15
+ ```ruby
16
+ # Gemfile
17
+ gem 'flow_chat'
18
+ ```
19
+
20
+ ```bash
21
+ bundle install
22
+ ```
23
+
24
+ ## Quick Start: Your First Multi-Platform App
25
+
26
+ Let's build a simple survey application that works across USSD, WhatsApp, Telegram, and HTTP APIs.
27
+
28
+ ### 1. Create the Flow
29
+
30
+ Create a new file `app/flow_chat/survey_flow.rb`:
31
+
32
+ ```ruby
33
+ class SurveyFlow < FlowChat::Flow
34
+ def start
35
+ # Welcome message that works on all platforms
36
+ name = app.screen(:name) do |prompt|
37
+ prompt.ask "Welcome to our survey! What's your name?",
38
+ validate: ->(input) {
39
+ return "Name must be at least 2 characters" if input.length < 2
40
+ nil
41
+ },
42
+ transform: ->(input) { input.strip.titleize }
43
+ end
44
+
45
+ # Rating question with platform-appropriate choices
46
+ rating = app.screen(:rating) do |prompt|
47
+ prompt.select "Hi #{name}! Rate our service:", {
48
+ "5" => "⭐⭐⭐⭐⭐ Excellent",
49
+ "4" => "⭐⭐⭐⭐ Good",
50
+ "3" => "⭐⭐⭐ Average",
51
+ "2" => "⭐⭐ Poor",
52
+ "1" => "⭐ Very Poor"
53
+ }
54
+ end
55
+
56
+ # Feedback collection
57
+ feedback = app.screen(:feedback) do |prompt|
58
+ prompt.ask "Any additional feedback?",
59
+ validate: ->(input) {
60
+ return "Feedback too short" if input.length < 5
61
+ nil
62
+ }
63
+ end
64
+
65
+ # Save the survey (your business logic)
66
+ save_survey(name, rating, feedback, app.msisdn)
67
+
68
+ # Thank you message
69
+ app.say "Thank you #{name}! Your feedback (#{rating}⭐) has been recorded."
70
+ end
71
+
72
+ private
73
+
74
+ def save_survey(name, rating, feedback, phone)
75
+ # Your database logic here
76
+ Rails.logger.info "Survey: #{name} (#{phone}) rated #{rating}/5: #{feedback}"
77
+
78
+ # Example: Save to database
79
+ # Survey.create!(
80
+ # name: name,
81
+ # rating: rating.to_i,
82
+ # feedback: feedback,
83
+ # phone: phone,
84
+ # platform: app.platform
85
+ # )
86
+ end
87
+ end
88
+ ```
89
+
90
+ ### 2. Create Controllers for Each Platform
91
+
92
+ #### USSD Controller
93
+
94
+ ```ruby
95
+ # app/controllers/ussd_controller.rb
96
+ class UssdController < ApplicationController
97
+ skip_forgery_protection
98
+
99
+ def nalo_webhook
100
+ processor = FlowChat::Processor.new(self) do |config|
101
+ config.use_gateway FlowChat::Ussd::Gateway::Nalo
102
+ config.use_session_store FlowChat::Session::CacheSessionStore
103
+
104
+ # Optional: Configure USSD-specific settings
105
+ config.use_session_config(boundaries: [:flow, :platform])
106
+ end
107
+
108
+ processor.run SurveyFlow, :start
109
+ end
110
+ end
111
+ ```
112
+
113
+ #### WhatsApp Controller
114
+
115
+ ```ruby
116
+ # app/controllers/whatsapp_controller.rb
117
+ class WhatsappController < ApplicationController
118
+ skip_forgery_protection
119
+
120
+ def webhook
121
+ processor = FlowChat::Processor.new(self) do |config|
122
+ config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi
123
+ config.use_session_store FlowChat::Session::CacheSessionStore
124
+ end
125
+
126
+ processor.run SurveyFlow, :start
127
+ rescue => e
128
+ Rails.logger.error "WhatsApp error: #{e.message}"
129
+ head :internal_server_error
130
+ end
131
+ end
132
+ ```
133
+
134
+ #### Telegram Controller
135
+
136
+ ```ruby
137
+ # app/controllers/telegram_controller.rb
138
+ class TelegramController < ApplicationController
139
+ skip_forgery_protection
140
+
141
+ def webhook
142
+ processor = FlowChat::Processor.new(self) do |config|
143
+ config.use_gateway FlowChat::Telegram::Gateway::BotApi
144
+ config.use_session_store FlowChat::Session::CacheSessionStore
145
+ end
146
+
147
+ processor.run SurveyFlow, :start
148
+ rescue => e
149
+ Rails.logger.error { "Telegram error: #{e.message}" }
150
+ head :internal_server_error
151
+ end
152
+ end
153
+ ```
154
+
155
+ #### HTTP API Controller
156
+
157
+ ```ruby
158
+ # app/controllers/api/chat_controller.rb
159
+ class Api::ChatController < ApplicationController
160
+ before_action :authenticate_api_user # Your auth logic
161
+
162
+ def message
163
+ processor = FlowChat::Processor.new(self) do |config|
164
+ config.use_gateway FlowChat::Http::Gateway::Simple
165
+ config.use_session_store FlowChat::Session::CacheSessionStore
166
+
167
+ # Session isolation per API user
168
+ config.use_session_config(identifier: :user_id)
169
+ end
170
+
171
+ processor.run SurveyFlow, :start
172
+ # Automatically returns JSON response
173
+ end
174
+ end
175
+ ```
176
+
177
+ ### 3. Add Routes
178
+
179
+ ```ruby
180
+ # config/routes.rb
181
+ Rails.application.routes.draw do
182
+ # USSD routes
183
+ post '/ussd/nalo', to: 'ussd#nalo_webhook'
184
+
185
+ # WhatsApp routes
186
+ post '/whatsapp/webhook', to: 'whatsapp#webhook'
187
+ get '/whatsapp/webhook', to: 'whatsapp#verify' # For webhook verification
188
+
189
+ # Telegram routes
190
+ post '/telegram/webhook', to: 'telegram#webhook'
191
+
192
+ # API routes
193
+ namespace :api do
194
+ post '/chat/message', to: 'chat#message'
195
+ end
196
+ end
197
+ ```
198
+
199
+ ### 4. Configuration
200
+
201
+ #### Environment Variables
202
+
203
+ Add these to your `.env` file:
204
+
205
+ ```bash
206
+ # WhatsApp Configuration
207
+ WHATSAPP_ACCESS_TOKEN=your_access_token
208
+ WHATSAPP_PHONE_NUMBER_ID=your_phone_number_id
209
+ WHATSAPP_VERIFY_TOKEN=your_verify_token
210
+ WHATSAPP_APP_SECRET=your_app_secret
211
+
212
+ # Telegram Configuration
213
+ TELEGRAM_BOT_TOKEN=your_bot_token
214
+ TELEGRAM_SECRET_TOKEN=your_webhook_secret # Optional: for webhook validation
215
+ ```
216
+
217
+ #### FlowChat Configuration
218
+
219
+ Create `config/initializers/flow_chat.rb`:
220
+
221
+ ```ruby
222
+ FlowChat::Config.logger = Rails.logger
223
+ FlowChat::Config.cache = Rails.cache
224
+
225
+ # USSD Configuration
226
+ FlowChat::Config.ussd.pagination_page_size = 160 # Adjust for your network
227
+ FlowChat::Config.ussd.pagination_next_option = "#"
228
+ FlowChat::Config.ussd.pagination_back_option = "0"
229
+
230
+ # WhatsApp Configuration
231
+ FlowChat::Config.whatsapp.message_handling_mode = :inline # or :background
232
+ ```
233
+
234
+ ### 5. Test Your Application
235
+
236
+ #### Using the Built-in Simulator
237
+
238
+ Create a simple test in `rails console`:
239
+
240
+ ```ruby
241
+ # Start a simulator session
242
+ simulator = FlowChat::Simulator.new(SurveyFlow, :start)
243
+
244
+ # Simulate the conversation
245
+ simulator.start
246
+ # => "Welcome to our survey! What's your name?"
247
+
248
+ simulator.send_message("John Doe")
249
+ # => "Hi John Doe! Rate our service:"
250
+
251
+ simulator.select_option("5")
252
+ # => "Any additional feedback?"
253
+
254
+ simulator.send_message("Great service, very helpful!")
255
+ # => "Thank you John Doe! Your feedback (5⭐) has been recorded."
256
+ ```
257
+
258
+ #### Testing HTTP API
259
+
260
+ ```bash
261
+ # Test the HTTP endpoint
262
+ curl -X POST http://localhost:3000/api/chat/message \
263
+ -H "Content-Type: application/json" \
264
+ -H "Authorization: Bearer your_api_token" \
265
+ -d '{
266
+ "user_id": "user123",
267
+ "input": "Hello"
268
+ }'
269
+ ```
270
+
271
+ #### Testing USSD (using simulator)
272
+
273
+ Visit your Rails app with the simulator enabled:
274
+
275
+ ```ruby
276
+ # In development, add this to your controller
277
+ processor = FlowChat::Processor.new(self, enable_simulator: true) do |config|
278
+ config.use_gateway FlowChat::Ussd::Gateway::Nalo
279
+ # ... other config
280
+ end
281
+ ```
282
+
283
+ ## Key Concepts
284
+
285
+ ### 1. Screen-Based Navigation
286
+
287
+ FlowChat uses a **screen-based** approach where each `app.screen(:key)` represents a step in your conversation:
288
+
289
+ - **Automatic State Management**: Each screen's result is automatically cached
290
+ - **Navigation Stack**: FlowChat tracks where users are in the flow
291
+ - **Resume Capability**: Users can return to where they left off
292
+
293
+ ### 2. Platform Abstraction
294
+
295
+ The same flow code works across all platforms because:
296
+
297
+ - **Unified Prompts**: `prompt.ask()` and `prompt.select()` adapt to each platform
298
+ - **Context Normalization**: All platforms provide consistent context (phone number, input, etc.)
299
+ - **Flexible Rendering**: Emojis show on WhatsApp, get stripped for USSD
300
+
301
+ ### 3. Pluggable Gateways
302
+
303
+ Each platform can have multiple gateway implementations:
304
+
305
+ ```ruby
306
+ # USSD with different providers
307
+ config.use_gateway FlowChat::Ussd::Gateway::Nalo
308
+ # config.use_gateway FlowChat::Ussd::Gateway::Africaist
309
+ # config.use_gateway YourCompany::Ussd::Gateway::CustomProvider
310
+
311
+ # WhatsApp with different APIs
312
+ config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi
313
+ # config.use_gateway FlowChat::Whatsapp::Gateway::OnPremise
314
+ # config.use_gateway YourCompany::Whatsapp::Gateway::Twilio
315
+ ```
316
+
317
+ ### 4. Session Management
318
+
319
+ FlowChat provides flexible session configuration:
320
+
321
+ ```ruby
322
+ # Ephemeral sessions (restart on each request)
323
+ config.use_session_config(identifier: :request_id)
324
+
325
+ # Durable sessions (persist across timeouts using phone number)
326
+ config.use_durable_sessions
327
+
328
+ # Cross-platform sessions (same user, different platforms)
329
+ config.use_cross_platform_sessions
330
+
331
+ # Multi-tenant isolation
332
+ config.use_url_isolation
333
+ ```
334
+
335
+ ## Next Steps
336
+
337
+ Now that you have a basic multi-platform application running:
338
+
339
+ 1. **Explore Platform Features**: Learn platform-specific capabilities
340
+ - [USSD Development](platforms/ussd.md) - Pagination, choice mapping
341
+ - [WhatsApp Development](platforms/whatsapp.md) - Rich media, templates
342
+ - [Telegram Development](platforms/telegram.md) - Inline keyboards, media, callbacks
343
+ - [HTTP Development](platforms/http.md) - API integration patterns
344
+
345
+ 2. **Advanced Topics**:
346
+ - [Session Management](../session-management.md) - Deep dive into session boundaries
347
+ - [Middleware Development](../middleware.md) - Custom processing logic
348
+ - [Gateway Development](../gateway-development.md) - Build your own platform support
349
+
350
+ 3. **Production Deployment**:
351
+ - [Configuration](../configuration.md) - Production-ready settings
352
+ - [Background Jobs](../background-jobs.md) - Async processing for WhatsApp
353
+ - [Testing](../testing.md) - Comprehensive testing strategies
354
+
355
+ ## Common Patterns
356
+
357
+ ### Validation and Transformation
358
+
359
+ ```ruby
360
+ app.screen(:phone) do |prompt|
361
+ prompt.ask "Enter your phone number:",
362
+ validate: ->(input) {
363
+ return "Invalid phone format" unless input.match?(/^\+?[\d\s-()]+$/)
364
+ parsed = Phonelib.parse(input)
365
+ return "Invalid phone number" unless parsed.valid?
366
+ nil
367
+ },
368
+ transform: ->(input) { Phonelib.parse(input).e164 }
369
+ end
370
+ ```
371
+
372
+ ### Conditional Flow Logic
373
+
374
+ ```ruby
375
+ def main_menu
376
+ user_type = determine_user_type(app.msisdn)
377
+
378
+ if user_type == :premium
379
+ premium_menu
380
+ else
381
+ basic_menu
382
+ end
383
+ end
384
+ ```
385
+
386
+ ### Error Handling
387
+
388
+ ```ruby
389
+ def payment_flow
390
+ begin
391
+ amount = app.screen(:amount) { |p| p.ask "Enter amount:" }
392
+ process_payment(amount)
393
+ app.say "Payment successful!"
394
+ rescue PaymentError => e
395
+ app.say "Payment failed: #{e.message}"
396
+ end
397
+ end
398
+ ```
399
+
400
+ ## Troubleshooting
401
+
402
+ ### Common Issues
403
+
404
+ 1. **Session Not Persisting**
405
+ - Check your session store configuration
406
+ - Verify session boundaries match your use case
407
+
408
+ 2. **Gateway Errors**
409
+ - Ensure environment variables are set correctly
410
+ - Check gateway-specific configuration requirements
411
+
412
+ 3. **Platform Differences**
413
+ - Test flows on each platform's simulator
414
+ - Be aware of character limits (USSD) vs rich features (WhatsApp)
415
+
416
+ ### Debug Mode
417
+
418
+ Enable comprehensive logging:
419
+
420
+ ```ruby
421
+ # config/initializers/flow_chat.rb
422
+ FlowChat::Config.logger.level = Logger::DEBUG
423
+
424
+ # In your flow
425
+ Rails.logger.debug "Current screen: #{app.navigation_stack.last}"
426
+ Rails.logger.debug "User input: #{app.input.inspect}"
427
+ ```
428
+
429
+ Ready to build more sophisticated flows? Continue with the [Architecture Overview](architecture.md) to understand FlowChat's design principles.