flow_chat 0.8.1 → 0.8.2
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/docs/configuration.md +2 -2
- data/docs/http-gateway-protocol.md +432 -0
- data/docs/sessions.md +7 -7
- data/docs/ussd-setup.md +1 -1
- data/examples/http_controller.rb +154 -0
- data/examples/simulator_controller.rb +21 -1
- data/examples/ussd_controller.rb +1 -1
- data/lib/flow_chat/base_app.rb +13 -1
- data/lib/flow_chat/base_executor.rb +1 -1
- data/lib/flow_chat/base_processor.rb +7 -6
- data/lib/flow_chat/config.rb +17 -2
- data/lib/flow_chat/http/app.rb +6 -0
- data/lib/flow_chat/http/gateway/simple.rb +77 -0
- data/lib/flow_chat/http/middleware/executor.rb +24 -0
- data/lib/flow_chat/http/processor.rb +33 -0
- data/lib/flow_chat/http/renderer.rb +41 -0
- data/lib/flow_chat/instrumentation.rb +2 -0
- data/lib/flow_chat/phone_number_util.rb +47 -0
- data/lib/flow_chat/session/cache_session_store.rb +1 -17
- data/lib/flow_chat/session/middleware.rb +19 -18
- data/lib/flow_chat/simulator/controller.rb +17 -5
- data/lib/flow_chat/simulator/views/simulator.html.erb +220 -8
- data/lib/flow_chat/ussd/gateway/nalo.rb +3 -7
- data/lib/flow_chat/ussd/gateway/nsano.rb +0 -2
- data/lib/flow_chat/version.rb +1 -1
- data/lib/flow_chat/whatsapp/gateway/cloud_api.rb +16 -14
- metadata +10 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 419f60cf88904f992a2bcf4e93646e68adaee3fcdac5a96d080708b199a2d8a6
|
4
|
+
data.tar.gz: 24ffafb2b588fdaee9775422e8057c073ce133d693dd6e78f19ef32978d40f88
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b49e73727d931e33d9ebff02c0809f31bd3adb7478335be76272961b04db219d98b10664675e32b45998bbcaa9d2d33de3644b84d3d924dc719d797341cd3fe0
|
7
|
+
data.tar.gz: 789fe300b2e3863b88e11a347bf2b205440427b2a70a6cec0d3995bd5a5efc4210dfc66e571412ca79cf642d93ba35890ba6d5270eb1c3b23a727b6beb62f326
|
data/docs/configuration.md
CHANGED
@@ -24,7 +24,7 @@ FlowChat.setup_instrumentation!
|
|
24
24
|
```ruby
|
25
25
|
# Session boundaries control how session IDs are constructed
|
26
26
|
FlowChat::Config.session.boundaries = [:flow, :platform] # default
|
27
|
-
FlowChat::Config.session.
|
27
|
+
FlowChat::Config.session.hash_identifiers = true # hash identifiers for privacy
|
28
28
|
FlowChat::Config.session.identifier = nil # let platforms choose (default)
|
29
29
|
|
30
30
|
# Available boundary options:
|
@@ -193,7 +193,7 @@ processor = FlowChat::Ussd::Processor.new(self) do |config|
|
|
193
193
|
# Configure session boundaries
|
194
194
|
config.use_session_config(
|
195
195
|
boundaries: [:flow, :platform], # which boundaries to enforce
|
196
|
-
|
196
|
+
hash_identifiers: true, # hash identifiers for privacy
|
197
197
|
identifier: :msisdn # use MSISDN for durable sessions (optional)
|
198
198
|
)
|
199
199
|
|
@@ -0,0 +1,432 @@
|
|
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.
|
data/docs/sessions.md
CHANGED
@@ -23,7 +23,7 @@ Controls how session IDs are generated and sessions are isolated:
|
|
23
23
|
```ruby
|
24
24
|
# Global session configuration
|
25
25
|
FlowChat::Config.session.boundaries = [:flow, :gateway, :platform] # isolation boundaries
|
26
|
-
FlowChat::Config.session.
|
26
|
+
FlowChat::Config.session.hash_identifiers = true # privacy protection
|
27
27
|
FlowChat::Config.session.identifier = nil # platform chooses default
|
28
28
|
```
|
29
29
|
|
@@ -99,15 +99,15 @@ identifier: :msisdn
|
|
99
99
|
# Same session resumes across conversations
|
100
100
|
```
|
101
101
|
|
102
|
-
###
|
102
|
+
### Identifier Hashing
|
103
103
|
|
104
104
|
When using `:msisdn` identifier, phone numbers are automatically hashed for privacy:
|
105
105
|
|
106
106
|
```ruby
|
107
|
-
FlowChat::Config.session.
|
107
|
+
FlowChat::Config.session.hash_identifiers = true # default
|
108
108
|
# "+256700123456" becomes "a1b2c3d4" (8-character hash)
|
109
109
|
|
110
|
-
FlowChat::Config.session.
|
110
|
+
FlowChat::Config.session.hash_identifiers = false
|
111
111
|
# "+256700123456" used directly (not recommended for production)
|
112
112
|
```
|
113
113
|
|
@@ -135,7 +135,7 @@ processor = FlowChat::Ussd::Processor.new(self) do |config|
|
|
135
135
|
# Explicit session configuration
|
136
136
|
config.use_session_config(
|
137
137
|
boundaries: [:flow, :platform], # isolate by flow and platform
|
138
|
-
|
138
|
+
hash_identifiers: true, # hash identifiers for privacy
|
139
139
|
identifier: :msisdn # use phone number for durable sessions
|
140
140
|
)
|
141
141
|
end
|
@@ -352,8 +352,8 @@ end
|
|
352
352
|
### 5. Security Considerations
|
353
353
|
|
354
354
|
```ruby
|
355
|
-
# Always hash
|
356
|
-
FlowChat::Config.session.
|
355
|
+
# Always hash identifiers in production
|
356
|
+
FlowChat::Config.session.hash_identifiers = true
|
357
357
|
|
358
358
|
# Use secure cache backends
|
359
359
|
config.cache_store = :redis_cache_store, {
|
data/docs/ussd-setup.md
CHANGED
@@ -147,7 +147,7 @@ Session identifier options:
|
|
147
147
|
- **`nil`** - Platform chooses default (`:request_id` for USSD, `:msisdn` for WhatsApp)
|
148
148
|
- **`:msisdn`** - Use phone number (durable sessions)
|
149
149
|
- **`:request_id`** - Use request ID (ephemeral sessions)
|
150
|
-
- **`
|
150
|
+
- **`hash_identifiers`** - Hash identifiers for privacy (recommended)
|
151
151
|
|
152
152
|
## Middleware
|
153
153
|
|
@@ -0,0 +1,154 @@
|
|
1
|
+
# Example HTTP Controller for FlowChat HTTP Gateway
|
2
|
+
#
|
3
|
+
# This controller demonstrates how to use FlowChat with HTTP requests
|
4
|
+
# for simple JSON-based conversational interfaces.
|
5
|
+
#
|
6
|
+
# Usage:
|
7
|
+
# POST /http/webhook
|
8
|
+
# Content-Type: application/json
|
9
|
+
#
|
10
|
+
# {
|
11
|
+
# "session_id": "unique_session_123",
|
12
|
+
# "user_id": "user_456",
|
13
|
+
# "input": "Hello"
|
14
|
+
# }
|
15
|
+
#
|
16
|
+
# Response:
|
17
|
+
# {
|
18
|
+
# "type": "prompt",
|
19
|
+
# "session_id": "unique_session_123",
|
20
|
+
# "user_id": "user_456",
|
21
|
+
# "timestamp": "2024-01-01T12:00:00Z",
|
22
|
+
# "message": "Hello! What's your name?",
|
23
|
+
# "choices": [
|
24
|
+
# {"key": "1", "text": "Continue"}
|
25
|
+
# ]
|
26
|
+
# }
|
27
|
+
|
28
|
+
class HttpController < ApplicationController
|
29
|
+
skip_forgery_protection
|
30
|
+
|
31
|
+
def webhook
|
32
|
+
processor = FlowChat::Http::Processor.new(self) do |config|
|
33
|
+
config.use_gateway FlowChat::Http::Gateway::Simple
|
34
|
+
config.use_session_store FlowChat::Session::CacheSessionStore
|
35
|
+
|
36
|
+
# Configure session management
|
37
|
+
config.use_session_config(
|
38
|
+
boundaries: [:flow, :platform],
|
39
|
+
hash_identifiers: true,
|
40
|
+
identifier: :msisdn # Use phone number for durable sessions
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
processor.run WelcomeFlow, :main_page
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Example flow for HTTP gateway
|
49
|
+
class WelcomeFlow < FlowChat::Flow
|
50
|
+
def main_page
|
51
|
+
name = app.screen(:name) do |prompt|
|
52
|
+
prompt.ask "Hello! What's your name?",
|
53
|
+
validate: ->(input) { "Name is required" if input.blank? },
|
54
|
+
transform: ->(input) { input.strip.titleize }
|
55
|
+
end
|
56
|
+
|
57
|
+
age = app.screen(:age) do |prompt|
|
58
|
+
prompt.ask "Nice to meet you, #{name}! How old are you?",
|
59
|
+
validate: ->(input) {
|
60
|
+
return "Please enter a number" unless input.match?(/^\d+$/)
|
61
|
+
return "Age must be between 1 and 120" unless (1..120).include?(input.to_i)
|
62
|
+
nil
|
63
|
+
},
|
64
|
+
transform: ->(input) { input.to_i }
|
65
|
+
end
|
66
|
+
|
67
|
+
preferences = app.screen(:preferences) do |prompt|
|
68
|
+
prompt.select "What are you interested in?", {
|
69
|
+
"tech" => "Technology",
|
70
|
+
"sports" => "Sports",
|
71
|
+
"music" => "Music",
|
72
|
+
"travel" => "Travel"
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
# Summary
|
77
|
+
app.say "Great! Here's what I learned about you:"
|
78
|
+
app.say "Name: #{name}"
|
79
|
+
app.say "Age: #{age}"
|
80
|
+
app.say "Interest: #{preferences.capitalize}"
|
81
|
+
|
82
|
+
# Ask if they want to continue
|
83
|
+
continue = app.screen(:continue) do |prompt|
|
84
|
+
prompt.yes? "Would you like to explore more features?"
|
85
|
+
end
|
86
|
+
|
87
|
+
if continue
|
88
|
+
features_demo
|
89
|
+
else
|
90
|
+
app.say "Thanks for trying FlowChat HTTP Gateway! 👋"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def features_demo
|
97
|
+
choice = app.screen(:feature_choice) do |prompt|
|
98
|
+
prompt.select "What would you like to try?", {
|
99
|
+
"media" => "Media Support",
|
100
|
+
"validation" => "Input Validation",
|
101
|
+
"session" => "Session Management"
|
102
|
+
}
|
103
|
+
end
|
104
|
+
|
105
|
+
case choice
|
106
|
+
when "media"
|
107
|
+
media_demo
|
108
|
+
when "validation"
|
109
|
+
validation_demo
|
110
|
+
when "session"
|
111
|
+
session_demo
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def media_demo
|
116
|
+
app.say "FlowChat supports rich media in HTTP responses!",
|
117
|
+
media: {
|
118
|
+
url: "https://via.placeholder.com/300x200.png?text=FlowChat+HTTP",
|
119
|
+
type: :image,
|
120
|
+
caption: "FlowChat HTTP Gateway Demo"
|
121
|
+
}
|
122
|
+
|
123
|
+
app.say "Media is returned in the JSON response for your frontend to display."
|
124
|
+
end
|
125
|
+
|
126
|
+
def validation_demo
|
127
|
+
email = app.screen(:email) do |prompt|
|
128
|
+
prompt.ask "Enter your email address:",
|
129
|
+
validate: ->(input) {
|
130
|
+
return "Email is required" if input.blank?
|
131
|
+
return "Invalid email format" unless input.match?(/\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i)
|
132
|
+
nil
|
133
|
+
},
|
134
|
+
transform: ->(input) { input.downcase.strip }
|
135
|
+
end
|
136
|
+
|
137
|
+
app.say "Perfect! Your email #{email} has been validated and normalized."
|
138
|
+
end
|
139
|
+
|
140
|
+
def session_demo
|
141
|
+
# Store some data in session
|
142
|
+
app.session.set("demo_timestamp", Time.current.to_s)
|
143
|
+
app.session.set("demo_counter", (app.session.get("demo_counter") || 0) + 1)
|
144
|
+
|
145
|
+
counter = app.session.get("demo_counter")
|
146
|
+
timestamp = app.session.get("demo_timestamp")
|
147
|
+
|
148
|
+
app.say "Session Demo:"
|
149
|
+
app.say "• This is visit ##{counter} in this session"
|
150
|
+
app.say "• Session started at: #{timestamp}"
|
151
|
+
app.say "• Session data persists across HTTP requests"
|
152
|
+
app.say "• Session ID: #{app.session.context['session.id']}"
|
153
|
+
end
|
154
|
+
end
|