flow_chat 0.8.2 → 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 +11 -11
  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/ussd_controller.rb +9 -9
  31. data/examples/whatsapp_controller.rb +2 -2
  32. data/flow_chat.gemspec +4 -0
  33. data/lib/flow_chat/{base_app.rb → app.rb} +16 -9
  34. data/lib/flow_chat/async_job.rb +176 -0
  35. data/lib/flow_chat/config.rb +10 -30
  36. data/lib/flow_chat/{base_executor.rb → executor.rb} +6 -11
  37. data/lib/flow_chat/factory.rb +94 -0
  38. data/lib/flow_chat/gateway_async_support.rb +106 -0
  39. data/lib/flow_chat/generic_async_job.rb +30 -0
  40. data/lib/flow_chat/http/gateway/simple.rb +81 -33
  41. data/lib/flow_chat/http/renderer.rb +3 -3
  42. data/lib/flow_chat/instrumentation/setup.rb +1 -1
  43. data/lib/flow_chat/instrumentation.rb +23 -0
  44. data/lib/flow_chat/intercom/client.rb +155 -0
  45. data/lib/flow_chat/intercom/configuration.rb +149 -0
  46. data/lib/flow_chat/intercom/gateway/intercom_api.rb +396 -0
  47. data/lib/flow_chat/intercom/renderer.rb +71 -0
  48. data/lib/flow_chat/phone_number_util.rb +37 -35
  49. data/lib/flow_chat/processor.rb +188 -0
  50. data/lib/flow_chat/renderers/markdown_support.rb +58 -0
  51. data/lib/flow_chat/session/middleware.rb +25 -9
  52. data/lib/flow_chat/telegram/client.rb +240 -0
  53. data/lib/flow_chat/telegram/configuration.rb +118 -0
  54. data/lib/flow_chat/telegram/gateway/bot_api.rb +300 -0
  55. data/lib/flow_chat/telegram/middleware/choice_mapper.rb +35 -0
  56. data/lib/flow_chat/telegram/renderer.rb +125 -0
  57. data/lib/flow_chat/telegram.rb +7 -0
  58. data/lib/flow_chat/ussd/gateway/nalo.rb +24 -4
  59. data/lib/flow_chat/ussd/middleware/pagination.rb +9 -5
  60. data/lib/flow_chat/ussd/renderer.rb +1 -1
  61. data/lib/flow_chat/version.rb +1 -1
  62. data/lib/flow_chat/whatsapp/client.rb +144 -13
  63. data/lib/flow_chat/whatsapp/configuration.rb +1 -1
  64. data/lib/flow_chat/whatsapp/gateway/cloud_api.rb +132 -96
  65. data/lib/flow_chat/whatsapp/id_generator.rb +124 -0
  66. data/lib/flow_chat/whatsapp/middleware/choice_mapper.rb +147 -0
  67. data/lib/flow_chat/whatsapp/renderer.rb +145 -11
  68. data/lib/flow_chat.rb +11 -1
  69. data/lib/tasks/release.rake +165 -0
  70. metadata +84 -25
  71. data/docs/flows.md +0 -320
  72. data/docs/http-gateway-protocol.md +0 -432
  73. data/docs/images/simulator.png +0 -0
  74. data/docs/media.md +0 -153
  75. data/docs/sessions.md +0 -433
  76. data/docs/ussd-setup.md +0 -322
  77. data/docs/whatsapp-setup.md +0 -162
  78. data/examples/whatsapp_message_job.rb +0 -113
  79. data/lib/flow_chat/base_processor.rb +0 -146
  80. data/lib/flow_chat/http/app.rb +0 -6
  81. data/lib/flow_chat/http/middleware/executor.rb +0 -24
  82. data/lib/flow_chat/http/processor.rb +0 -33
  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 -96
  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
@@ -1,432 +0,0 @@
1
- # FlowChat HTTP Gateway Protocol Specification
2
-
3
- **Version:** 1.0
4
- **Date:** 2025-06-27
5
- **Status:** Stable
6
-
7
- ## Overview
8
-
9
- The FlowChat HTTP Gateway Protocol defines a simple JSON-based request/response format for building conversational flows over HTTP. It enables any HTTP endpoint to participate in FlowChat's conversation framework with session management, media support, and interactive elements.
10
-
11
- ## Protocol Design
12
-
13
- The protocol is designed to be:
14
- - **Simple**: Easy to implement with any web framework
15
- - **Stateless**: Each request contains all necessary context
16
- - **Flexible**: Supports various response types and media
17
- - **Extensible**: Can be enhanced without breaking compatibility
18
-
19
- ## Request Format
20
-
21
- ### HTTP Method
22
- All requests use **POST** method.
23
-
24
- ### Headers
25
- ```
26
- Content-Type: application/json
27
- Accept: application/json
28
- ```
29
-
30
- ### Request Body
31
- ```json
32
- {
33
- "session_id": "string",
34
- "user_id": "string",
35
- "input": "string",
36
- "simulator_mode": boolean
37
- }
38
- ```
39
-
40
- #### Request Fields
41
-
42
- | Field | Type | Required | Description |
43
- |-------|------|----------|-------------|
44
- | `session_id` | string | Yes | Unique identifier for the conversation session |
45
- | `user_id` | string | Yes | Unique identifier for the user (phone number, email, etc.) |
46
- | `input` | string | Yes | User's input message (empty string for initial request) |
47
- | `simulator_mode` | boolean | No | Indicates if request is from FlowChat simulator |
48
-
49
- #### Example Request
50
- ```json
51
- {
52
- "session_id": "sess_abc123",
53
- "user_id": "+256700123456",
54
- "input": "1",
55
- "simulator_mode": true
56
- }
57
- ```
58
-
59
- ## Response Format
60
-
61
- ### HTTP Status Codes
62
- - **200 OK**: Successful response with flow continuation
63
- - **400 Bad Request**: Invalid request format
64
- - **500 Internal Server Error**: Server processing error
65
-
66
- ### Response Body
67
- ```json
68
- {
69
- "type": "string",
70
- "message": "string",
71
- "choices": "object|null",
72
- "media": "object|null"
73
- }
74
- ```
75
-
76
- #### Response Fields
77
-
78
- | Field | Type | Required | Description |
79
- |-------|------|----------|-------------|
80
- | `type` | string | Yes | Response type: `prompt`, `text`, or `terminal` |
81
- | `message` | string | Yes | Text message to display to user |
82
- | `choices` | object | No | Interactive choices for user selection |
83
- | `media` | object | No | Media content (images, videos, etc.) |
84
-
85
- ## Response Types
86
-
87
- ### 1. Prompt Response
88
- Interactive response expecting user input with optional choices.
89
-
90
- ```json
91
- {
92
- "type": "prompt",
93
- "message": "Welcome! Please choose an option:",
94
- "choices": {
95
- "1": "View Profile",
96
- "2": "Send Message",
97
- "3": "Settings"
98
- }
99
- }
100
- ```
101
-
102
- **Behavior**: Session continues, awaiting user input.
103
-
104
- ### 2. Text Response
105
- Simple informational message.
106
-
107
- ```json
108
- {
109
- "type": "text",
110
- "message": "Processing your request..."
111
- }
112
- ```
113
-
114
- **Behavior**: Session continues, awaiting user input.
115
-
116
- ### 3. Terminal Response
117
- Final message that ends the conversation session.
118
-
119
- ```json
120
- {
121
- "type": "terminal",
122
- "message": "Thank you for using our service. Goodbye!"
123
- }
124
- ```
125
-
126
- **Behavior**: Session ends, no further input expected.
127
-
128
- ## Choices Format
129
-
130
- Choices enable interactive menu-driven flows. They can be provided in two formats:
131
-
132
- ### Simple Format
133
- ```json
134
- {
135
- "choices": {
136
- "1": "Option One",
137
- "2": "Option Two",
138
- "3": "Option Three"
139
- }
140
- }
141
- ```
142
-
143
- ### Object Format
144
- ```json
145
- {
146
- "choices": {
147
- "choice1": {
148
- "key": "1",
149
- "value": "Option One"
150
- },
151
- "choice2": {
152
- "key": "2",
153
- "value": "Option Two"
154
- }
155
- }
156
- }
157
- ```
158
-
159
- **Key Guidelines:**
160
- - Keys should be simple (numbers, single letters)
161
- - Values should be descriptive text
162
- - Maximum 10 choices recommended for usability
163
- - Empty choices object `{}` means free-form input expected
164
-
165
- ## Media Support
166
-
167
- The protocol supports various media types in responses.
168
-
169
- ### Image Media
170
- ```json
171
- {
172
- "media": {
173
- "type": "image",
174
- "url": "https://example.com/image.jpg",
175
- "caption": "Optional image caption"
176
- }
177
- }
178
- ```
179
-
180
- ### Video Media
181
- ```json
182
- {
183
- "media": {
184
- "type": "video",
185
- "url": "https://example.com/video.mp4",
186
- "caption": "Optional video caption"
187
- }
188
- }
189
- ```
190
-
191
- ### Document Media
192
- ```json
193
- {
194
- "media": {
195
- "type": "document",
196
- "url": "https://example.com/document.pdf",
197
- "filename": "report.pdf",
198
- "caption": "Download the full report"
199
- }
200
- }
201
- ```
202
-
203
- ### Audio Media
204
- ```json
205
- {
206
- "media": {
207
- "type": "audio",
208
- "url": "https://example.com/audio.mp3",
209
- "caption": "Voice message"
210
- }
211
- }
212
- ```
213
-
214
- **Media Guidelines:**
215
- - URLs must be publicly accessible
216
- - HTTPS URLs recommended for security
217
- - Include appropriate file extensions
218
- - Captions are optional but recommended for accessibility
219
-
220
- ## Session Management
221
-
222
- ### Session Lifecycle
223
- 1. **Initialization**: First request has empty `input` field
224
- 2. **Continuation**: Subsequent requests include user input
225
- 3. **Termination**: Response with `type: "terminal"` ends session
226
-
227
- ### Session State
228
- The protocol is stateless from HTTP perspective. Session state must be managed by the endpoint implementation using:
229
- - Database storage
230
- - In-memory cache
231
- - External session stores
232
- - Cookies/tokens (if supported)
233
-
234
- ### Session ID Format
235
- - Must be unique across all sessions
236
- - Recommended: UUID, random string, or timestamped identifier
237
- - Length: 8-64 characters
238
- - Characters: alphanumeric, hyphens, underscores
239
-
240
- ## Error Handling
241
-
242
- ### Client Errors (4xx)
243
- ```json
244
- {
245
- "type": "terminal",
246
- "message": "Invalid request format. Please try again later."
247
- }
248
- ```
249
-
250
- ### Server Errors (5xx)
251
- ```json
252
- {
253
- "type": "terminal",
254
- "message": "Service temporarily unavailable. Please try again later."
255
- }
256
- ```
257
-
258
- ### Validation Errors
259
- ```json
260
- {
261
- "type": "prompt",
262
- "message": "Invalid selection. Please choose from the available options:",
263
- "choices": {
264
- "1": "Option A",
265
- "2": "Option B"
266
- }
267
- }
268
- ```
269
-
270
- ## Implementation Examples
271
-
272
- ### Basic Flow Handler
273
- ```ruby
274
- def handle_http_webhook
275
- data = JSON.parse(request.body.read)
276
-
277
- session_id = data['session_id']
278
- user_id = data['user_id']
279
- input = data['input']
280
-
281
- response = case input.downcase.strip
282
- when '', 'start'
283
- {
284
- type: 'prompt',
285
- message: 'Welcome! What would you like to do?',
286
- choices: {
287
- '1' => 'View Account',
288
- '2' => 'Make Payment',
289
- '3' => 'Get Help'
290
- }
291
- }
292
- when '1'
293
- {
294
- type: 'text',
295
- message: "Account Balance: $150.00\nLast Transaction: -$25.00"
296
- }
297
- when '2'
298
- {
299
- type: 'prompt',
300
- message: 'Enter payment amount:',
301
- choices: {}
302
- }
303
- when '3'
304
- {
305
- type: 'terminal',
306
- message: 'For help, call 1-800-HELP or visit our website.'
307
- }
308
- else
309
- {
310
- type: 'prompt',
311
- message: 'Please select a valid option:',
312
- choices: {
313
- '1' => 'View Account',
314
- '2' => 'Make Payment',
315
- '3' => 'Get Help'
316
- }
317
- }
318
- end
319
-
320
- render json: response
321
- end
322
- ```
323
-
324
- ### With Media Support
325
- ```ruby
326
- def handle_media_flow
327
- # ... input parsing ...
328
-
329
- case input
330
- when 'show_chart'
331
- {
332
- type: 'prompt',
333
- message: 'Here is your sales chart:',
334
- media: {
335
- type: 'image',
336
- url: generate_chart_url(user_id),
337
- caption: 'Monthly sales data'
338
- },
339
- choices: {
340
- '0' => 'Back to menu'
341
- }
342
- }
343
- end
344
- end
345
- ```
346
-
347
- ## Security Considerations
348
-
349
- ### Input Validation
350
- - Always validate and sanitize user input
351
- - Check session_id format and existence
352
- - Validate user_id against expected format
353
- - Limit input length to prevent abuse
354
-
355
- ### Authentication
356
- - Implement endpoint authentication if needed
357
- - Validate session ownership
358
- - Use HTTPS for sensitive data
359
- - Consider rate limiting
360
-
361
- ### Data Protection
362
- - Don't log sensitive user input
363
- - Encrypt session data if stored
364
- - Use secure session identifiers
365
- - Implement proper CORS headers
366
-
367
- ## Best Practices
368
-
369
- ### Response Design
370
- 1. **Keep messages concise** - Users prefer short, clear text
371
- 2. **Limit choices** - Maximum 5-7 options for better UX
372
- 3. **Use clear labels** - Make choice text descriptive
373
- 4. **Provide fallbacks** - Handle invalid input gracefully
374
- 5. **End flows properly** - Always provide terminal responses
375
-
376
- ### Session Management
377
- 1. **Set timeouts** - Expire inactive sessions
378
- 2. **Clean up data** - Remove old session data
379
- 3. **Handle duplicates** - Manage duplicate session IDs
380
- 4. **Log activities** - Track session flow for debugging
381
-
382
- ### Error Handling
383
- 1. **Fail gracefully** - Provide helpful error messages
384
- 2. **Log errors** - Capture errors for debugging
385
- 3. **Retry logic** - Handle temporary failures
386
- 4. **Fallback flows** - Provide alternative paths
387
-
388
- ## Protocol Extensions
389
-
390
- ### Custom Fields
391
- Implementations may include additional fields in requests/responses:
392
-
393
- ```json
394
- {
395
- "type": "prompt",
396
- "message": "Hello!",
397
- "choices": {...},
398
- "metadata": {
399
- "flow_version": "1.2",
400
- "user_lang": "en"
401
- }
402
- }
403
- ```
404
-
405
- ### Future Enhancements
406
- - Rich media support (carousels, buttons)
407
- - Location sharing
408
- - File uploads
409
- - Real-time messaging
410
- - Multi-turn conversations
411
-
412
- ## Compliance
413
-
414
- ### HTTP Standards
415
- - Follows HTTP/1.1 and HTTP/2 specifications
416
- - Uses standard status codes and headers
417
- - Supports CORS for cross-origin requests
418
-
419
- ### JSON Standards
420
- - Follows RFC 7159 JSON specification
421
- - Uses UTF-8 encoding
422
- - Validates JSON schema on input
423
-
424
- ## Version History
425
-
426
- | Version | Date | Changes |
427
- |---------|------|---------|
428
- | 1.0 | 2025-06-27 | Initial specification |
429
-
430
- ---
431
-
432
- For implementation questions or protocol clarifications, please refer to the FlowChat documentation or contact the development team.
Binary file
data/docs/media.md DELETED
@@ -1,153 +0,0 @@
1
- # Media Support
2
-
3
- FlowChat supports rich media attachments for enhanced conversational experiences with automatic cross-platform optimization.
4
-
5
- ## Supported Media Types
6
-
7
- - **📷 Images** (`type: :image`) - Photos, screenshots, diagrams
8
- - **📄 Documents** (`type: :document`) - PDFs, forms, receipts
9
- - **🎥 Videos** (`type: :video`) - Tutorials, demos, explanations
10
- - **🎵 Audio** (`type: :audio`) - Voice messages, recordings
11
- - **😊 Stickers** (`type: :sticker`) - Fun visual elements
12
-
13
- ## Basic Usage
14
-
15
- ```ruby
16
- class ProductFlow < FlowChat::Flow
17
- def main_page
18
- # Text input with context image
19
- feedback = app.screen(:feedback) do |prompt|
20
- prompt.ask "What do you think of our new product?",
21
- media: {
22
- type: :image,
23
- url: "https://cdn.example.com/products/new_product.jpg"
24
- }
25
- end
26
-
27
- # Send informational media
28
- app.say "Thanks for your feedback! Here's what's coming next:",
29
- media: {
30
- type: :video,
31
- url: "https://videos.example.com/roadmap.mp4"
32
- }
33
-
34
- # Document with filename
35
- app.say "Here's your receipt:",
36
- media: {
37
- type: :document,
38
- url: "https://api.example.com/receipt.pdf",
39
- filename: "receipt.pdf"
40
- }
41
- end
42
- end
43
- ```
44
-
45
- ## Media Hash Format
46
-
47
- ```ruby
48
- {
49
- type: :image, # Required: :image, :document, :audio, :video, :sticker
50
- url: "https://...", # Required: URL to the media file OR WhatsApp media ID
51
- filename: "doc.pdf" # Optional: Only for documents
52
- }
53
- ```
54
-
55
- ## Using WhatsApp Media IDs
56
-
57
- For better performance and to avoid external dependencies, you can upload files to WhatsApp and use the media ID:
58
-
59
- ```ruby
60
- # Upload a file first
61
- client = FlowChat::Whatsapp::Client.new(config)
62
- media_id = client.upload_media('path/to/image.jpg', 'image/jpeg')
63
-
64
- # Then use the media ID in your flow
65
- app.screen(:product_demo) do |prompt|
66
- prompt.ask "What do you think?",
67
- media: {
68
- type: :image,
69
- url: media_id # Use the media ID instead of URL
70
- }
71
- end
72
- ```
73
-
74
- ## WhatsApp Client Media Methods
75
-
76
- The WhatsApp client provides methods for uploading and sending media:
77
-
78
- ```ruby
79
- client = FlowChat::Whatsapp::Client.new(config)
80
-
81
- # Upload media and get media ID
82
- media_id = client.upload_media('image.jpg', 'image/jpeg')
83
- media_id = client.upload_media(file_io, 'image/jpeg', 'photo.jpg')
84
-
85
- # Send media directly
86
- client.send_image("+1234567890", "https://example.com/image.jpg", "Caption")
87
- client.send_image("+1234567890", media_id, "Caption")
88
-
89
- # Send document with MIME type and filename
90
- client.send_document("+1234567890", "https://example.com/doc.pdf", "Your receipt", "receipt.pdf", "application/pdf")
91
-
92
- # Send other media types
93
- client.send_video("+1234567890", "https://example.com/video.mp4", "Demo video", "video/mp4")
94
- client.send_audio("+1234567890", "https://example.com/audio.mp3", "audio/mpeg")
95
- client.send_sticker("+1234567890", "https://example.com/sticker.webp", "image/webp")
96
- ```
97
-
98
- ## Cross-Platform Behavior
99
-
100
- ### WhatsApp Experience
101
- - Media is sent directly to the chat
102
- - Prompt text becomes the media caption
103
- - Rich, native messaging experience
104
-
105
- ### USSD Experience
106
- - Media URL is included in text message
107
- - Graceful degradation with clear media indicators
108
- - Users can access media via the provided link
109
-
110
- ```ruby
111
- # This code works on both platforms:
112
- app.screen(:help) do |prompt|
113
- prompt.ask "Describe your issue:",
114
- media: {
115
- type: :image,
116
- url: "https://support.example.com/help_example.jpg"
117
- }
118
- end
119
- ```
120
-
121
- **WhatsApp Result:** Image sent with caption "Describe your issue:"
122
-
123
- **USSD Result:**
124
- ```
125
- 📷 Image: https://support.example.com/help_example.jpg
126
-
127
- Describe your issue:
128
- ```
129
-
130
- ## Advanced Examples
131
-
132
- ### Dynamic Media Based on User Context
133
-
134
- ```ruby
135
- class SupportFlow < FlowChat::Flow
136
- def show_help
137
- user_level = determine_user_level
138
-
139
- help_media = case user_level
140
- when :beginner
141
- { type: :video, url: "https://help.example.com/beginner_guide.mp4" }
142
- when :advanced
143
- { type: :document, url: "https://help.example.com/advanced_manual.pdf", filename: "advanced_manual.pdf" }
144
- else
145
- { type: :image, url: "https://help.example.com/quick_reference.jpg" }
146
- end
147
-
148
- app.say "Here's help tailored for you:", media: help_media
149
- end
150
- end
151
- ```
152
-
153
- See [examples/whatsapp_media_examples.rb](../examples/whatsapp_media_examples.rb) for complete media implementation examples.