flow_chat 0.9.0 → 0.10.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 (103) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/pages.yml +43 -0
  3. data/CHANGELOG.md +26 -0
  4. data/README.md +166 -295
  5. data/Rakefile +12 -1
  6. data/SECURITY.md +1 -1
  7. data/docs/architecture.md +52 -479
  8. data/docs/async-background-processing.md +31 -265
  9. data/docs/configuration.md +106 -613
  10. data/docs/factory-pattern.md +27 -324
  11. data/docs/gateway-context-variables.md +140 -143
  12. data/docs/gateway-development.md +86 -650
  13. data/docs/getting-started.md +40 -379
  14. data/docs/instrumentation.md +88 -279
  15. data/docs/platforms/instagram.md +278 -0
  16. data/docs/platforms/messenger.md +205 -0
  17. data/docs/platforms/telegram.md +47 -951
  18. data/docs/platforms/ussd.md +38 -653
  19. data/docs/platforms/whatsapp.md +73 -1321
  20. data/docs/superpowers/plans/2026-07-09-inbound-media-support.md +732 -0
  21. data/docs/superpowers/plans/2026-07-09-inbound-media-support.md.tasks.json +58 -0
  22. data/docs/superpowers/plans/2026-08-10-messenger-instagram.md +4064 -0
  23. data/docs/superpowers/plans/2026-08-10-messenger-instagram.md.tasks.json +226 -0
  24. data/docs/superpowers/plans/2026-08-16-unified-choice-resolution.md +972 -0
  25. data/docs/superpowers/plans/2026-08-16-unified-choice-resolution.md.tasks.json +88 -0
  26. data/docs/superpowers/specs/2026-07-09-inbound-media-support-design.md +195 -0
  27. data/docs/superpowers/specs/2026-08-10-messenger-instagram-design.md +391 -0
  28. data/docs/testing.md +26 -297
  29. data/examples/http_controller.rb +12 -10
  30. data/examples/intercom_configuration_example.rb +19 -24
  31. data/examples/intercom_controller.rb +8 -20
  32. data/examples/multi_tenant_whatsapp_controller.rb +61 -166
  33. data/examples/simulator_controller.rb +0 -1
  34. data/examples/ussd_controller.rb +86 -158
  35. data/examples/whatsapp_controller.rb +16 -15
  36. data/examples/whatsapp_media_examples.rb +27 -79
  37. data/lib/flow_chat/app.rb +129 -11
  38. data/lib/flow_chat/choice_titles.rb +95 -0
  39. data/lib/flow_chat/config.rb +124 -1
  40. data/lib/flow_chat/delivery_error.rb +9 -0
  41. data/lib/flow_chat/http/configuration_error.rb +9 -0
  42. data/lib/flow_chat/http/gateway/simple.rb +24 -4
  43. data/lib/flow_chat/http/middleware/choice_mapper.rb +94 -0
  44. data/lib/flow_chat/input.rb +86 -0
  45. data/lib/flow_chat/instagram/client.rb +32 -0
  46. data/lib/flow_chat/instagram/configuration.rb +147 -0
  47. data/lib/flow_chat/instagram/configuration_error.rb +7 -0
  48. data/lib/flow_chat/instagram/gateway/send_api.rb +63 -0
  49. data/lib/flow_chat/instagram/middleware/choice_mapper.rb +22 -0
  50. data/lib/flow_chat/instagram/renderer.rb +23 -0
  51. data/lib/flow_chat/instrumentation/metrics_collector.rb +6 -1
  52. data/lib/flow_chat/instrumentation.rb +160 -1
  53. data/lib/flow_chat/intercom/client.rb +34 -28
  54. data/lib/flow_chat/intercom/configuration.rb +2 -49
  55. data/lib/flow_chat/intercom/configuration_error.rb +9 -0
  56. data/lib/flow_chat/intercom/gateway/intercom_api.rb +81 -57
  57. data/lib/flow_chat/intercom/middleware/choice_mapper.rb +101 -0
  58. data/lib/flow_chat/intercom/renderer.rb +57 -5
  59. data/lib/flow_chat/media.rb +121 -0
  60. data/lib/flow_chat/messenger/client.rb +264 -0
  61. data/lib/flow_chat/messenger/configuration.rb +103 -0
  62. data/lib/flow_chat/messenger/configuration_error.rb +9 -0
  63. data/lib/flow_chat/messenger/gateway/send_api.rb +42 -0
  64. data/lib/flow_chat/messenger/middleware/choice_mapper.rb +185 -0
  65. data/lib/flow_chat/messenger/renderer.rb +150 -0
  66. data/lib/flow_chat/meta/challenge.rb +24 -0
  67. data/lib/flow_chat/meta/choice_ladder.rb +37 -0
  68. data/lib/flow_chat/meta/configuration_error.rb +7 -0
  69. data/lib/flow_chat/meta/gateway_identity.rb +38 -0
  70. data/lib/flow_chat/meta/messaging_gateway.rb +468 -0
  71. data/lib/flow_chat/meta/signature.rb +30 -0
  72. data/lib/flow_chat/meta/signature_validation.rb +66 -0
  73. data/lib/flow_chat/meta/webhook_verification.rb +43 -0
  74. data/lib/flow_chat/named_configuration.rb +65 -0
  75. data/lib/flow_chat/prompt.rb +13 -16
  76. data/lib/flow_chat/renderers/markdown_support.rb +109 -0
  77. data/lib/flow_chat/security.rb +76 -0
  78. data/lib/flow_chat/session/middleware.rb +11 -2
  79. data/lib/flow_chat/simulator/controller.rb +31 -15
  80. data/lib/flow_chat/simulator/views/simulator.html.erb +184 -20
  81. data/lib/flow_chat/telegram/client.rb +47 -4
  82. data/lib/flow_chat/telegram/configuration.rb +2 -42
  83. data/lib/flow_chat/telegram/configuration_error.rb +9 -0
  84. data/lib/flow_chat/telegram/gateway/bot_api.rb +46 -28
  85. data/lib/flow_chat/telegram/middleware/choice_mapper.rb +77 -16
  86. data/lib/flow_chat/telegram/renderer.rb +10 -2
  87. data/lib/flow_chat/text_truncator.rb +75 -0
  88. data/lib/flow_chat/ussd/middleware/choice_mapper.rb +10 -0
  89. data/lib/flow_chat/version.rb +1 -1
  90. data/lib/flow_chat/whatsapp/client.rb +14 -7
  91. data/lib/flow_chat/whatsapp/configuration.rb +12 -51
  92. data/lib/flow_chat/whatsapp/configuration_error.rb +9 -0
  93. data/lib/flow_chat/whatsapp/gateway/cloud_api.rb +254 -203
  94. data/lib/flow_chat/whatsapp/middleware/choice_mapper.rb +144 -57
  95. data/lib/flow_chat/whatsapp/renderer.rb +121 -60
  96. data/lib/flow_chat.rb +0 -10
  97. data/site/.nojekyll +0 -0
  98. data/site/.og-card.html +89 -0
  99. data/site/favicon.svg +6 -0
  100. data/site/index.html +209 -0
  101. data/site/og.png +0 -0
  102. metadata +51 -3
  103. data/lib/flow_chat/whatsapp/id_generator.rb +0 -124
data/docs/testing.md CHANGED
@@ -1,29 +1,23 @@
1
- # Testing & Simulation
1
+ # Testing
2
2
 
3
- FlowChat provides a sophisticated web-based simulator for testing conversational flows across multiple platforms (USSD, WhatsApp, HTTP) without requiring actual gateway integrations.
3
+ FlowChat gives you two ways to exercise a flow without a live gateway: a web simulator for manual, interactive testing during development, and the HTTP gateway for automated request tests.
4
4
 
5
- ## Built-in Web Simulator
5
+ ## The simulator
6
6
 
7
- ### Overview
7
+ The simulator is a web page that drives your flows the way a real platform would, so you can step through a conversation from the browser. It works the same regardless of which gateway a flow targets.
8
8
 
9
- The FlowChat simulator is a browser-based testing interface that provides:
9
+ Enable it in two steps.
10
10
 
11
- - **Multi-Platform Testing**: USSD, WhatsApp, and HTTP with platform-specific UI
12
- - **Real-Time Request Logging**: Complete HTTP traffic monitoring and debugging
13
- - **Security**: HMAC-signed cookie authentication
14
- - **Platform-Specific Rendering**: Accurate simulation of each platform's behavior
15
- - **Configuration Management**: Test multiple endpoints from a single interface
16
-
17
- ### Setup & Configuration
18
-
19
- #### 1. Basic Configuration
11
+ Set the simulator secret during boot:
20
12
 
21
13
  ```ruby
22
14
  # config/initializers/flow_chat.rb
23
- FlowChat::Config.simulator_secret = "your_secure_secret_key_here"
15
+ FlowChat::Config.simulator_secret = Rails.application.credentials.flow_chat_simulator_secret
24
16
  ```
25
17
 
26
- #### 2. Create Simulator Controller
18
+ The secret gates access. The simulator controller signs a cookie with it, and the gateways only enter simulator mode when that cookie is valid, so the simulator stays off in any environment where the secret is unset.
19
+
20
+ Mount a controller that includes the simulator module and lists the endpoints to test:
27
21
 
28
22
  ```ruby
29
23
  # app/controllers/simulator_controller.rb
@@ -36,318 +30,53 @@ class SimulatorController < ApplicationController
36
30
 
37
31
  protected
38
32
 
39
- # Configure available test endpoints
40
33
  def configurations
41
34
  {
42
35
  ussd_main: {
43
- name: "USSD (Nalo)",
44
- description: "USSD integration using Nalo gateway",
36
+ name: "USSD",
45
37
  processor_type: "ussd",
46
38
  gateway: "nalo",
47
- endpoint: "/ussd",
48
- icon: "📱",
49
- color: "#28a745",
50
- settings: {
51
- phone_number: default_phone_number,
52
- session_timeout: 300
53
- }
39
+ endpoint: "/ussd"
54
40
  },
55
41
  whatsapp_main: {
56
- name: "WhatsApp (Cloud API)",
57
- description: "WhatsApp integration using Cloud API",
42
+ name: "WhatsApp",
58
43
  processor_type: "whatsapp",
59
- gateway: "cloud_api",
60
- endpoint: "/whatsapp/webhook",
61
- icon: "💬",
62
- color: "#25D366",
63
- settings: {
64
- phone_number: default_phone_number,
65
- contact_name: default_contact_name
66
- }
67
- },
68
- http_api: {
69
- name: "HTTP API",
70
- description: "JSON HTTP API endpoint",
71
- processor_type: "http",
72
- gateway: "http_simple",
73
- endpoint: "/http/webhook",
74
- icon: "🌐",
75
- color: "#0066cc",
76
- settings: {
77
- user_id: default_phone_number
78
- }
44
+ gateway: "cloud_api",
45
+ endpoint: "/whatsapp/webhook"
79
46
  }
80
47
  }
81
48
  end
82
-
83
- def default_phone_number
84
- "+233244123456"
85
- end
86
-
87
- def default_contact_name
88
- "John Doe"
89
- end
90
-
91
- def default_config_key
92
- :ussd_main
93
- end
94
49
  end
95
50
  ```
96
51
 
97
- #### 3. Add Route
98
-
99
52
  ```ruby
100
53
  # config/routes.rb
101
- Rails.application.routes.draw do
102
- get '/simulator' => 'simulator#index'
103
- # ... other routes
104
- end
54
+ get "/simulator", to: "simulator#index"
105
55
  ```
106
56
 
107
- ### Security Features
108
-
109
- #### HMAC-Signed Cookies
110
-
111
- The simulator uses secure, timestamped cookies for authentication:
112
-
113
- ```ruby
114
- # Automatic cookie generation in simulator controller
115
- def set_simulator_cookie
116
- simulator_secret = FlowChat::Config.simulator_secret
117
- timestamp = Time.now.to_i
118
- message = "simulator:#{timestamp}"
119
- signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new("sha256"), simulator_secret, message)
120
-
121
- cookies[:flowchat_simulator] = {
122
- value: "#{timestamp}:#{signature}",
123
- expires: 24.hours.from_now,
124
- secure: request.ssl?, # HTTPS only in production
125
- httponly: true, # Prevent XSS
126
- same_site: :lax # CSRF protection
127
- }
128
- end
129
- ```
57
+ Each entry in `configurations` points at one of your real webhook endpoints, so the simulator posts to the same controller actions the platform would. Open `/simulator`, pick an endpoint, and send messages.
130
58
 
131
- #### Automatic Enablement
59
+ ## Automated tests
132
60
 
133
- Simulator mode is automatically enabled when:
61
+ For request specs, drive a flow through the HTTP gateway (`FlowChat::Http::Gateway::Simple`), which speaks plain JSON. Point a controller at your flow:
134
62
 
135
63
  ```ruby
136
- # In processor initialization
137
- enable_simulator: Rails.env.local? # true for development/test
138
- ```
139
-
140
- ### Platform-Specific Features
141
-
142
- #### USSD Testing
143
-
144
- **Interface**: Menu-style display with character limits and pagination
145
-
146
- **Features**:
147
- - Network-specific character limits (MTN: 160, Airtel: 140, etc.)
148
- - Session timeout simulation
149
- - Menu option selection
150
- - Automatic pagination for long content
151
-
152
- **Request Format**:
153
- ```ruby
154
- {
155
- msisdn: "+233244123456",
156
- text: "user_input",
157
- session_id: "session_123",
158
- network: "mtn"
159
- }
160
- ```
161
-
162
- #### WhatsApp Testing
163
-
164
- **Interface**: Chat-style interface with contact avatars and message history
165
-
166
- **Features**:
167
- - Contact name and avatar display
168
- - Interactive buttons and lists
169
- - Media support (images, documents, audio, video)
170
- - Cloud API webhook simulation
171
-
172
- **Request Format**:
173
- ```javascript
174
- {
175
- simulator_mode: true,
176
- entry: [{
177
- changes: [{
178
- value: {
179
- messaging_product: "whatsapp",
180
- metadata: {
181
- display_phone_number: "233244123456",
182
- phone_number_id: "phone_number_id_123"
183
- },
184
- messages: [{
185
- text: { body: "user_input" },
186
- type: 'text'
187
- }],
188
- contacts: [{
189
- profile: { name: "John Doe" },
190
- wa_id: "233244123456"
191
- }]
192
- }
193
- }]
194
- }]
195
- }
196
- ```
197
-
198
- **Response Handling**:
199
- ```javascript
200
- // Simulator expects JSON response for full simulation
201
- {
202
- mode: "simulator",
203
- webhook_processed: true,
204
- would_send: {
205
- to: "233244123456",
206
- type: "text",
207
- text: { body: "Response message" }
208
- },
209
- message_info: {
210
- to: "233244123456",
211
- contact_name: "John Doe",
212
- timestamp: "2024-01-01T12:00:00Z"
213
- }
214
- }
215
- ```
216
-
217
- #### HTTP Testing
218
-
219
- **Interface**: API-style interface showing JSON request/response
220
-
221
- **Features**:
222
- - JSON payload display
223
- - Session management
224
- - Custom headers
225
- - Error handling
226
-
227
- **Request Format**:
228
- ```javascript
229
- {
230
- session_id: "session_123",
231
- user_id: "+233244123456",
232
- input: "user_input",
233
- simulator_mode: true
234
- }
235
- ```
236
-
237
- ### Request Logging & Debugging
238
-
239
- #### Real-Time Traffic Monitoring
240
-
241
- The simulator provides comprehensive request logging:
242
-
243
- ```javascript
244
- // Automatic logging of all HTTP traffic
245
- addRequestLog('POST', endpoint, requestData, responseData, statusCode, errorMessage)
246
- ```
247
-
248
- **Log Information**:
249
- - Request method and URL
250
- - Complete request payload
251
- - Response status and data
252
- - Timestamps and duration
253
- - Error messages with helpful debugging hints
254
-
255
- #### Error Handling
256
-
257
- The simulator provides detailed error messages for common issues:
258
-
259
- - **Connection Failures**: CORS, SSL, network issues
260
- - **404 Not Found**: Route configuration problems
261
- - **500 Server Errors**: Application errors
262
- - **Authentication Issues**: Invalid simulator cookies
263
-
264
- ### Enabling Simulator Mode in Your Endpoints
265
-
266
- #### For WhatsApp Endpoints
267
-
268
- Your WhatsApp processor automatically handles simulator mode:
269
-
270
- ```ruby
271
- class WhatsappController < ApplicationController
272
- def webhook
273
- processor = FlowCha::Processor.new(self, enable_simulator: !Rails.env.production?) do |config|
274
- config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi
275
- config.use_session_store FlowChat::Session::CacheSessionStore
276
- end
277
-
278
- processor.run WelcomeFlow, :main_page
279
- end
280
- end
281
- ```
282
-
283
- #### For HTTP Endpoints
284
-
285
- HTTP endpoints work automatically with the simulator:
286
-
287
- ```ruby
288
- class HttpController < ApplicationController
64
+ class ChatController < ApplicationController
289
65
  def webhook
290
66
  processor = FlowChat::Processor.new(self) do |config|
291
67
  config.use_gateway FlowChat::Http::Gateway::Simple
292
68
  config.use_session_store FlowChat::Session::CacheSessionStore
293
69
  end
294
-
295
- processor.run WelcomeFlow, :main_page
70
+ processor.run RegistrationFlow, :main_page
296
71
  end
297
72
  end
298
73
  ```
299
74
 
300
- ### Usage Workflow
301
-
302
- #### 1. Start the Simulator
303
-
304
- ```bash
305
- rails server
306
- # Visit http://localhost:3000/simulator
307
- ```
308
-
309
- #### 2. Configure Test Environment
310
-
311
- - Select endpoint from configuration dropdown
312
- - Set phone number and contact name
313
- - Choose platform type (USSD/WhatsApp/HTTP)
314
-
315
- #### 3. Test Conversation Flows
316
-
317
- - Click "Start Session" to begin
318
- - Send messages through the platform-specific interface
319
- - Monitor request/response logs in real-time
320
- - Reset session to test different scenarios
321
-
322
- #### 4. Debug Issues
323
-
324
- - Check request logs for HTTP traffic details
325
- - Verify endpoint responses and status codes
326
- - Review error messages for configuration issues
327
- - Test different platforms and configurations
328
-
329
- ### Best Practices
330
-
331
- #### Security
332
-
333
- - Always set `FlowChat::Config.simulator_secret` in production
334
- - Use secure cookies over HTTPS in production
335
- - Restrict simulator access to development/staging environments
336
-
337
- #### Testing Strategy
338
-
339
- 1. **Start with Simulator**: Test flows before gateway integration
340
- 2. **Test All Platforms**: Verify behavior across USSD, WhatsApp, HTTP
341
- 3. **Check Character Limits**: Test USSD character restrictions
342
- 4. **Verify Session Management**: Test session boundaries and persistence
343
- 5. **Monitor Request Logs**: Debug integration issues in real-time
344
-
345
- #### Performance
346
-
347
- - Use simulator for rapid prototyping and debugging
348
- - Test edge cases and error conditions safely
349
- - Validate webhook payloads before production deployment
75
+ The Simple gateway expects `session_id` and `user_id` in the request and returns JSON with the prompt, choices, and any media. Post the next input with the same `session_id` to advance the conversation, and assert on the returned message and choices. Set `FlowChat::Config.cache` to a real store (for example `ActiveSupport::Cache::MemoryStore.new`) in your test setup so sessions persist across the requests in a single test.
350
76
 
351
- The FlowChat simulator provides a complete testing environment that accurately simulates real-world platform behavior while offering comprehensive debugging capabilities for efficient development and testing workflows.
77
+ Because one flow behaves the same across platforms, a flow verified through the HTTP gateway behaves the same on USSD, WhatsApp, and Telegram, apart from each platform's rendering.
352
78
 
79
+ ## Related
353
80
 
81
+ - [Getting started](getting-started.md)
82
+ - [Configuration](configuration.md)
@@ -33,20 +33,22 @@ class HttpController < ApplicationController
33
33
  config.use_gateway FlowChat::Http::Gateway::Simple
34
34
  config.use_session_store FlowChat::Session::CacheSessionStore
35
35
 
36
- # Configure session management
36
+ # The HTTP gateway sets request.user_id from the request's user_id, so key
37
+ # sessions on it. (:msisdn would be nil here, collapsing every caller into
38
+ # one session.)
37
39
  config.use_session_config(
38
40
  boundaries: [:flow, :platform],
39
41
  hash_identifiers: true,
40
- identifier: :msisdn # Use phone number for durable sessions
42
+ identifier: :user_id
41
43
  )
42
44
  end
43
45
 
44
- processor.run WelcomeFlow, :main_page
46
+ processor.run HttpWelcomeFlow, :main_page
45
47
  end
46
48
  end
47
49
 
48
50
  # Example flow for HTTP gateway
49
- class WelcomeFlow < FlowChat::Flow
51
+ class HttpWelcomeFlow < FlowChat::Flow
50
52
  def main_page
51
53
  name = app.screen(:name) do |prompt|
52
54
  prompt.ask "Hello! What's your name?",
@@ -87,7 +89,7 @@ class WelcomeFlow < FlowChat::Flow
87
89
  if continue
88
90
  features_demo
89
91
  else
90
- app.say "Thanks for trying FlowChat HTTP Gateway! 👋"
92
+ app.say "Thanks for trying the FlowChat HTTP gateway!"
91
93
  end
92
94
  end
93
95
 
@@ -115,7 +117,7 @@ class WelcomeFlow < FlowChat::Flow
115
117
  def media_demo
116
118
  app.say "FlowChat supports rich media in HTTP responses!",
117
119
  media: {
118
- url: "https://via.placeholder.com/300x200.png?text=FlowChat+HTTP",
120
+ url: "https://example.com/images/flowchat-http-demo.png",
119
121
  type: :image,
120
122
  caption: "FlowChat HTTP Gateway Demo"
121
123
  }
@@ -146,9 +148,9 @@ class WelcomeFlow < FlowChat::Flow
146
148
  timestamp = app.session.get("demo_timestamp")
147
149
 
148
150
  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"]}"
151
+ app.say "This is visit ##{counter} in this session"
152
+ app.say "Session started at: #{timestamp}"
153
+ app.say "Session data persists across HTTP requests"
154
+ app.say "Session ID: #{app.context["session.id"]}"
153
155
  end
154
156
  end
@@ -21,15 +21,17 @@
21
21
  # 3. Named Configuration Example (for multi-tenant apps)
22
22
  class MultiTenantIntercomSetup
23
23
  def self.setup_configurations
24
- # Main company configuration
25
- FlowChat::Intercom::Configuration.new("main") do |config|
24
+ # Passing a name registers the configuration under that name, so you can
25
+ # fetch it later with Configuration.get(:main). Set attributes with tap;
26
+ # Configuration.new does not yield a block.
27
+ FlowChat::Intercom::Configuration.new("main").tap do |config|
26
28
  config.access_token = Rails.application.credentials.dig(:intercom, :main, :access_token)
27
29
  config.client_secret = Rails.application.credentials.dig(:intercom, :main, :client_secret)
28
30
  config.admin_id = Rails.application.credentials.dig(:intercom, :main, :admin_id)
29
31
  end
30
32
 
31
33
  # Enterprise customer configuration
32
- FlowChat::Intercom::Configuration.new("enterprise") do |config|
34
+ FlowChat::Intercom::Configuration.new("enterprise").tap do |config|
33
35
  config.access_token = Rails.application.credentials.dig(:intercom, :enterprise, :access_token)
34
36
  config.client_secret = Rails.application.credentials.dig(:intercom, :enterprise, :client_secret)
35
37
  config.admin_id = Rails.application.credentials.dig(:intercom, :enterprise, :admin_id)
@@ -53,17 +55,17 @@ class TenantAwareIntercomController < ApplicationController
53
55
  skip_forgery_protection
54
56
 
55
57
  def webhook
56
- # Get tenant-specific configuration
58
+ # Fetch the named configuration registered in setup_configurations above.
57
59
  tenant = params[:tenant] || "main"
58
- FlowChat::Intercom::Configuration.get(tenant)
60
+ tenant_config = FlowChat::Intercom::Configuration.get(tenant)
59
61
 
60
62
  processor = FlowChat::Processor.new(self) do |config|
61
- config.use_gateway FlowChat::Intercom::Gateway::IntercomApi, config
63
+ config.use_gateway FlowChat::Intercom::Gateway::IntercomApi, tenant_config
62
64
  config.use_session_store FlowChat::Session::CacheSessionStore
63
- config.use_session_config(
64
- boundaries: [:flow, :url], # Separate sessions per tenant
65
- identifier: :conversation_id
66
- )
65
+ # request.id is the conversation id (the default :request_id identifier),
66
+ # so each conversation gets its own session. The :url boundary separates
67
+ # tenants that share the flow.
68
+ config.use_session_config(boundaries: [:flow, :url])
67
69
  end
68
70
 
69
71
  processor.run CustomerSupportFlow, :handle_conversation
@@ -95,24 +97,17 @@ end
95
97
  # - Check logs for any signature validation issues
96
98
 
97
99
  # 7. Finding Your Admin ID (Required)
98
- # To find your admin ID for message sending, use this Rails console command:
100
+ # The admin_id identifies which admin sends messages to a conversation.
101
+ # FlowChat's own client only sends messages, so list admins with the official
102
+ # intercom gem in a Rails console:
99
103
  #
100
104
  # rails console
101
105
  # ```
102
- # config = FlowChat::Intercom::Configuration.from_credentials
103
- # client = FlowChat::Intercom::Client.new(config)
104
- # result = client.list_admins
105
- # result["admins"].each { |admin| puts "#{admin["name"]} (#{admin["email"]}) - ID: #{admin["id"]}" }
106
+ # intercom = Intercom::Client.new(token: Rails.application.credentials.dig(:intercom, :access_token))
107
+ # intercom.admins.all.each { |admin| puts "#{admin.name} (#{admin.email}) - ID: #{admin.id}" }
106
108
  # ```
107
109
  #
108
- # This will display all admins with their IDs. Copy the ID of the admin you want to use for sending messages.
109
- #
110
- # Alternative methods:
111
- # 1. Go to your Intercom Developer Hub (https://developers.intercom.com/)
112
- # 2. Navigate to your app → Configure → Basic Information
113
- # 3. Find the Admin ID in the app details
114
- #
115
- # The admin_id is required for sending messages from FlowChat to Intercom conversations.
116
- # The API uses this to identify which admin is sending the message.
110
+ # Copy the ID of the admin you want to send from. You can also find it in the
111
+ # Intercom Developer Hub under your app's Configure -> Basic Information.
117
112
  #
118
113
  # Note: The old bot_user_id configuration is no longer needed.
@@ -14,11 +14,10 @@ class IntercomController < ApplicationController
14
14
  # Use cache-based session storage for longer-lived sessions
15
15
  config.use_session_store FlowChat::Session::CacheSessionStore
16
16
 
17
- # Configure session boundaries - use conversation ID for session isolation
18
- config.use_session_config(
19
- boundaries: [:conversation], # Each conversation gets its own session
20
- identifier: :conversation_id # Use conversation ID as session key
21
- )
17
+ # The Intercom gateway sets request.id to the conversation id, and the
18
+ # default identifier (:request_id) uses it, so each conversation already
19
+ # gets its own session. Scope by flow as well.
20
+ config.use_session_config(boundaries: [:flow])
22
21
  end
23
22
 
24
23
  # Run the customer support flow
@@ -29,16 +28,10 @@ end
29
28
  # Example flow for handling customer conversations via Intercom
30
29
  class CustomerSupportFlow < FlowChat::Flow
31
30
  def handle_conversation
32
- # Greet the user
33
- greeting = if first_message?
34
- "Hello! I'm here to help you. How can I assist you today?"
35
- else
36
- "Thanks for your message! Let me help you with that."
37
- end
38
-
39
- # Get the user's inquiry
31
+ # Ask the opening question. The prompt is only shown on the first unanswered
32
+ # turn; once the user replies, screen(:inquiry) returns their stored answer.
40
33
  inquiry = app.screen(:inquiry) do |prompt|
41
- prompt.ask greeting
34
+ prompt.ask "Hello! I'm here to help you. How can I assist you today?"
42
35
  end
43
36
 
44
37
  # Categorize the inquiry and provide appropriate response
@@ -61,11 +54,6 @@ class CustomerSupportFlow < FlowChat::Flow
61
54
 
62
55
  private
63
56
 
64
- def first_message?
65
- # Check if this is the first message in the conversation
66
- app.session.get("message_count").nil?
67
- end
68
-
69
57
  def categorize_inquiry(inquiry)
70
58
  # Simple keyword-based categorization
71
59
  # In a real app, you might use AI/ML for better categorization
@@ -171,7 +159,7 @@ class CustomerSupportFlow < FlowChat::Flow
171
159
  end
172
160
 
173
161
  def escalate_to_human(reason, team_id: nil)
174
- conversation_id = app.context["request.conversation_id"]
162
+ conversation_id = app.context["request.id"]
175
163
 
176
164
  # Business logic: Use Intercom manager for conversation control
177
165
  if app.gateway == :intercom_api