flow_chat 0.8.2 → 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.
- checksums.yaml +4 -4
- data/.cliff.toml +74 -0
- data/.github/workflows/ci.yml +2 -3
- data/.github/workflows/pages.yml +43 -0
- data/.github/workflows/release.yml +56 -0
- data/.standard.yml +4 -0
- data/CHANGELOG.md +48 -0
- data/CLAUDE.md +327 -0
- data/CONTRIBUTING.md +134 -0
- data/Gemfile +1 -0
- data/README.md +189 -133
- data/Rakefile +17 -2
- data/SECURITY.md +42 -349
- data/docs/architecture.md +83 -0
- data/docs/async-background-processing.md +64 -0
- data/docs/configuration.md +110 -287
- data/docs/factory-pattern.md +58 -0
- data/docs/gateway-context-variables.md +168 -0
- data/docs/gateway-development.md +159 -0
- data/docs/getting-started.md +90 -0
- data/docs/instrumentation.md +95 -175
- data/docs/platforms/instagram.md +278 -0
- data/docs/platforms/messenger.md +205 -0
- data/docs/platforms/telegram.md +109 -0
- data/docs/platforms/ussd.md +78 -0
- data/docs/platforms/whatsapp.md +147 -0
- data/docs/superpowers/plans/2026-07-09-inbound-media-support.md +732 -0
- data/docs/superpowers/plans/2026-07-09-inbound-media-support.md.tasks.json +58 -0
- data/docs/superpowers/plans/2026-08-10-messenger-instagram.md +4064 -0
- data/docs/superpowers/plans/2026-08-10-messenger-instagram.md.tasks.json +226 -0
- data/docs/superpowers/plans/2026-08-16-unified-choice-resolution.md +972 -0
- data/docs/superpowers/plans/2026-08-16-unified-choice-resolution.md.tasks.json +88 -0
- data/docs/superpowers/specs/2026-07-09-inbound-media-support-design.md +195 -0
- data/docs/superpowers/specs/2026-08-10-messenger-instagram-design.md +391 -0
- data/docs/testing.md +33 -426
- data/examples/custom_session_id_example.rb +119 -0
- data/examples/http_controller.rb +22 -20
- data/examples/intercom_configuration_example.rb +113 -0
- data/examples/intercom_controller.rb +182 -0
- data/examples/multi_tenant_whatsapp_controller.rb +63 -168
- data/examples/simulator_controller.rb +0 -1
- data/examples/ussd_controller.rb +88 -160
- data/examples/whatsapp_controller.rb +18 -17
- data/examples/whatsapp_media_examples.rb +27 -79
- data/flow_chat.gemspec +4 -0
- data/lib/flow_chat/app.rb +211 -0
- data/lib/flow_chat/async_job.rb +176 -0
- data/lib/flow_chat/choice_titles.rb +95 -0
- data/lib/flow_chat/config.rb +126 -23
- data/lib/flow_chat/delivery_error.rb +9 -0
- data/lib/flow_chat/{base_executor.rb → executor.rb} +6 -11
- data/lib/flow_chat/factory.rb +94 -0
- data/lib/flow_chat/gateway_async_support.rb +106 -0
- data/lib/flow_chat/generic_async_job.rb +30 -0
- data/lib/flow_chat/http/configuration_error.rb +9 -0
- data/lib/flow_chat/http/gateway/simple.rb +104 -36
- data/lib/flow_chat/http/middleware/choice_mapper.rb +94 -0
- data/lib/flow_chat/http/renderer.rb +3 -3
- data/lib/flow_chat/input.rb +86 -0
- data/lib/flow_chat/instagram/client.rb +32 -0
- data/lib/flow_chat/instagram/configuration.rb +147 -0
- data/lib/flow_chat/instagram/configuration_error.rb +7 -0
- data/lib/flow_chat/instagram/gateway/send_api.rb +63 -0
- data/lib/flow_chat/instagram/middleware/choice_mapper.rb +22 -0
- data/lib/flow_chat/instagram/renderer.rb +23 -0
- data/lib/flow_chat/instrumentation/metrics_collector.rb +6 -1
- data/lib/flow_chat/instrumentation/setup.rb +1 -1
- data/lib/flow_chat/instrumentation.rb +182 -0
- data/lib/flow_chat/intercom/client.rb +161 -0
- data/lib/flow_chat/intercom/configuration.rb +102 -0
- data/lib/flow_chat/intercom/configuration_error.rb +9 -0
- data/lib/flow_chat/intercom/gateway/intercom_api.rb +420 -0
- data/lib/flow_chat/intercom/middleware/choice_mapper.rb +101 -0
- data/lib/flow_chat/intercom/renderer.rb +123 -0
- data/lib/flow_chat/media.rb +121 -0
- data/lib/flow_chat/messenger/client.rb +264 -0
- data/lib/flow_chat/messenger/configuration.rb +103 -0
- data/lib/flow_chat/messenger/configuration_error.rb +9 -0
- data/lib/flow_chat/messenger/gateway/send_api.rb +42 -0
- data/lib/flow_chat/messenger/middleware/choice_mapper.rb +185 -0
- data/lib/flow_chat/messenger/renderer.rb +150 -0
- data/lib/flow_chat/meta/challenge.rb +24 -0
- data/lib/flow_chat/meta/choice_ladder.rb +37 -0
- data/lib/flow_chat/meta/configuration_error.rb +7 -0
- data/lib/flow_chat/meta/gateway_identity.rb +38 -0
- data/lib/flow_chat/meta/messaging_gateway.rb +468 -0
- data/lib/flow_chat/meta/signature.rb +30 -0
- data/lib/flow_chat/meta/signature_validation.rb +66 -0
- data/lib/flow_chat/meta/webhook_verification.rb +43 -0
- data/lib/flow_chat/named_configuration.rb +65 -0
- data/lib/flow_chat/phone_number_util.rb +37 -35
- data/lib/flow_chat/processor.rb +188 -0
- data/lib/flow_chat/prompt.rb +13 -16
- data/lib/flow_chat/renderers/markdown_support.rb +167 -0
- data/lib/flow_chat/security.rb +76 -0
- data/lib/flow_chat/session/middleware.rb +36 -11
- data/lib/flow_chat/simulator/controller.rb +31 -15
- data/lib/flow_chat/simulator/views/simulator.html.erb +184 -20
- data/lib/flow_chat/telegram/client.rb +283 -0
- data/lib/flow_chat/telegram/configuration.rb +78 -0
- data/lib/flow_chat/telegram/configuration_error.rb +9 -0
- data/lib/flow_chat/telegram/gateway/bot_api.rb +318 -0
- data/lib/flow_chat/telegram/middleware/choice_mapper.rb +96 -0
- data/lib/flow_chat/telegram/renderer.rb +133 -0
- data/lib/flow_chat/telegram.rb +7 -0
- data/lib/flow_chat/text_truncator.rb +75 -0
- data/lib/flow_chat/ussd/gateway/nalo.rb +24 -4
- data/lib/flow_chat/ussd/middleware/choice_mapper.rb +10 -0
- data/lib/flow_chat/ussd/middleware/pagination.rb +9 -5
- data/lib/flow_chat/ussd/renderer.rb +1 -1
- data/lib/flow_chat/version.rb +1 -1
- data/lib/flow_chat/whatsapp/client.rb +158 -20
- data/lib/flow_chat/whatsapp/configuration.rb +13 -52
- data/lib/flow_chat/whatsapp/configuration_error.rb +9 -0
- data/lib/flow_chat/whatsapp/gateway/cloud_api.rb +335 -248
- data/lib/flow_chat/whatsapp/middleware/choice_mapper.rb +234 -0
- data/lib/flow_chat/whatsapp/renderer.rb +259 -64
- data/lib/flow_chat.rb +1 -1
- data/lib/tasks/release.rake +165 -0
- data/site/.nojekyll +0 -0
- data/site/.og-card.html +89 -0
- data/site/favicon.svg +6 -0
- data/site/index.html +209 -0
- data/site/og.png +0 -0
- metadata +132 -25
- data/docs/flows.md +0 -320
- data/docs/http-gateway-protocol.md +0 -432
- data/docs/images/simulator.png +0 -0
- data/docs/media.md +0 -153
- data/docs/sessions.md +0 -433
- data/docs/ussd-setup.md +0 -322
- data/docs/whatsapp-setup.md +0 -162
- data/examples/whatsapp_message_job.rb +0 -113
- data/lib/flow_chat/base_app.rb +0 -86
- data/lib/flow_chat/base_processor.rb +0 -146
- data/lib/flow_chat/http/app.rb +0 -6
- data/lib/flow_chat/http/middleware/executor.rb +0 -24
- data/lib/flow_chat/http/processor.rb +0 -33
- data/lib/flow_chat/session/rails_session_store.rb +0 -68
- data/lib/flow_chat/ussd/app.rb +0 -6
- data/lib/flow_chat/ussd/gateway/nsano.rb +0 -96
- data/lib/flow_chat/ussd/middleware/executor.rb +0 -24
- data/lib/flow_chat/ussd/processor.rb +0 -39
- data/lib/flow_chat/whatsapp/app.rb +0 -29
- data/lib/flow_chat/whatsapp/middleware/executor.rb +0 -24
- data/lib/flow_chat/whatsapp/processor.rb +0 -32
- data/lib/flow_chat/whatsapp/send_job_support.rb +0 -79
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: flow_chat
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.10.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Stefan Froelich
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-08-17 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: zeitwerk
|
|
@@ -80,6 +80,48 @@ dependencies:
|
|
|
80
80
|
- - "~>"
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
82
|
version: 0.4.2
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: intercom
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - "~>"
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '4.2'
|
|
90
|
+
type: :runtime
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - "~>"
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '4.2'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: reverse_markdown
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - "~>"
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '3.0'
|
|
104
|
+
type: :runtime
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - "~>"
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '3.0'
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: kramdown
|
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - "~>"
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '2.4'
|
|
118
|
+
type: :runtime
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - "~>"
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: '2.4'
|
|
83
125
|
description: "FlowChat is a Rails framework for building sophisticated conversational
|
|
84
126
|
interfaces across USSD and WhatsApp platforms. \nCreate interactive flows with menus,
|
|
85
127
|
prompts, validation, media support, and session management. Features include \nmulti-tenancy,
|
|
@@ -92,9 +134,16 @@ extensions: []
|
|
|
92
134
|
extra_rdoc_files: []
|
|
93
135
|
files:
|
|
94
136
|
- ".DS_Store"
|
|
137
|
+
- ".cliff.toml"
|
|
95
138
|
- ".github/workflows/ci.yml"
|
|
139
|
+
- ".github/workflows/pages.yml"
|
|
140
|
+
- ".github/workflows/release.yml"
|
|
96
141
|
- ".gitignore"
|
|
97
142
|
- ".ruby-version"
|
|
143
|
+
- ".standard.yml"
|
|
144
|
+
- CHANGELOG.md
|
|
145
|
+
- CLAUDE.md
|
|
146
|
+
- CONTRIBUTING.md
|
|
98
147
|
- Gemfile
|
|
99
148
|
- LICENSE.txt
|
|
100
149
|
- README.md
|
|
@@ -102,71 +151,129 @@ files:
|
|
|
102
151
|
- SECURITY.md
|
|
103
152
|
- bin/console
|
|
104
153
|
- bin/setup
|
|
154
|
+
- docs/architecture.md
|
|
155
|
+
- docs/async-background-processing.md
|
|
105
156
|
- docs/configuration.md
|
|
106
|
-
- docs/
|
|
107
|
-
- docs/
|
|
108
|
-
- docs/
|
|
157
|
+
- docs/factory-pattern.md
|
|
158
|
+
- docs/gateway-context-variables.md
|
|
159
|
+
- docs/gateway-development.md
|
|
160
|
+
- docs/getting-started.md
|
|
109
161
|
- docs/instrumentation.md
|
|
110
|
-
- docs/
|
|
111
|
-
- docs/
|
|
162
|
+
- docs/platforms/instagram.md
|
|
163
|
+
- docs/platforms/messenger.md
|
|
164
|
+
- docs/platforms/telegram.md
|
|
165
|
+
- docs/platforms/ussd.md
|
|
166
|
+
- docs/platforms/whatsapp.md
|
|
167
|
+
- docs/superpowers/plans/2026-07-09-inbound-media-support.md
|
|
168
|
+
- docs/superpowers/plans/2026-07-09-inbound-media-support.md.tasks.json
|
|
169
|
+
- docs/superpowers/plans/2026-08-10-messenger-instagram.md
|
|
170
|
+
- docs/superpowers/plans/2026-08-10-messenger-instagram.md.tasks.json
|
|
171
|
+
- docs/superpowers/plans/2026-08-16-unified-choice-resolution.md
|
|
172
|
+
- docs/superpowers/plans/2026-08-16-unified-choice-resolution.md.tasks.json
|
|
173
|
+
- docs/superpowers/specs/2026-07-09-inbound-media-support-design.md
|
|
174
|
+
- docs/superpowers/specs/2026-08-10-messenger-instagram-design.md
|
|
112
175
|
- docs/testing.md
|
|
113
|
-
-
|
|
114
|
-
- docs/whatsapp-setup.md
|
|
176
|
+
- examples/custom_session_id_example.rb
|
|
115
177
|
- examples/http_controller.rb
|
|
178
|
+
- examples/intercom_configuration_example.rb
|
|
179
|
+
- examples/intercom_controller.rb
|
|
116
180
|
- examples/multi_tenant_whatsapp_controller.rb
|
|
117
181
|
- examples/simulator_controller.rb
|
|
118
182
|
- examples/ussd_controller.rb
|
|
119
183
|
- examples/whatsapp_controller.rb
|
|
120
184
|
- examples/whatsapp_media_examples.rb
|
|
121
|
-
- examples/whatsapp_message_job.rb
|
|
122
185
|
- flow_chat.gemspec
|
|
123
186
|
- lib/flow_chat.rb
|
|
124
|
-
- lib/flow_chat/
|
|
125
|
-
- lib/flow_chat/
|
|
126
|
-
- lib/flow_chat/
|
|
187
|
+
- lib/flow_chat/app.rb
|
|
188
|
+
- lib/flow_chat/async_job.rb
|
|
189
|
+
- lib/flow_chat/choice_titles.rb
|
|
127
190
|
- lib/flow_chat/config.rb
|
|
128
191
|
- lib/flow_chat/context.rb
|
|
192
|
+
- lib/flow_chat/delivery_error.rb
|
|
193
|
+
- lib/flow_chat/executor.rb
|
|
194
|
+
- lib/flow_chat/factory.rb
|
|
129
195
|
- lib/flow_chat/flow.rb
|
|
130
|
-
- lib/flow_chat/
|
|
196
|
+
- lib/flow_chat/gateway_async_support.rb
|
|
197
|
+
- lib/flow_chat/generic_async_job.rb
|
|
198
|
+
- lib/flow_chat/http/configuration_error.rb
|
|
131
199
|
- lib/flow_chat/http/gateway/simple.rb
|
|
132
|
-
- lib/flow_chat/http/middleware/
|
|
133
|
-
- lib/flow_chat/http/processor.rb
|
|
200
|
+
- lib/flow_chat/http/middleware/choice_mapper.rb
|
|
134
201
|
- lib/flow_chat/http/renderer.rb
|
|
202
|
+
- lib/flow_chat/input.rb
|
|
203
|
+
- lib/flow_chat/instagram/client.rb
|
|
204
|
+
- lib/flow_chat/instagram/configuration.rb
|
|
205
|
+
- lib/flow_chat/instagram/configuration_error.rb
|
|
206
|
+
- lib/flow_chat/instagram/gateway/send_api.rb
|
|
207
|
+
- lib/flow_chat/instagram/middleware/choice_mapper.rb
|
|
208
|
+
- lib/flow_chat/instagram/renderer.rb
|
|
135
209
|
- lib/flow_chat/instrumentation.rb
|
|
136
210
|
- lib/flow_chat/instrumentation/log_subscriber.rb
|
|
137
211
|
- lib/flow_chat/instrumentation/metrics_collector.rb
|
|
138
212
|
- lib/flow_chat/instrumentation/setup.rb
|
|
213
|
+
- lib/flow_chat/intercom/client.rb
|
|
214
|
+
- lib/flow_chat/intercom/configuration.rb
|
|
215
|
+
- lib/flow_chat/intercom/configuration_error.rb
|
|
216
|
+
- lib/flow_chat/intercom/gateway/intercom_api.rb
|
|
217
|
+
- lib/flow_chat/intercom/middleware/choice_mapper.rb
|
|
218
|
+
- lib/flow_chat/intercom/renderer.rb
|
|
139
219
|
- lib/flow_chat/interrupt.rb
|
|
220
|
+
- lib/flow_chat/media.rb
|
|
221
|
+
- lib/flow_chat/messenger/client.rb
|
|
222
|
+
- lib/flow_chat/messenger/configuration.rb
|
|
223
|
+
- lib/flow_chat/messenger/configuration_error.rb
|
|
224
|
+
- lib/flow_chat/messenger/gateway/send_api.rb
|
|
225
|
+
- lib/flow_chat/messenger/middleware/choice_mapper.rb
|
|
226
|
+
- lib/flow_chat/messenger/renderer.rb
|
|
227
|
+
- lib/flow_chat/meta/challenge.rb
|
|
228
|
+
- lib/flow_chat/meta/choice_ladder.rb
|
|
229
|
+
- lib/flow_chat/meta/configuration_error.rb
|
|
230
|
+
- lib/flow_chat/meta/gateway_identity.rb
|
|
231
|
+
- lib/flow_chat/meta/messaging_gateway.rb
|
|
232
|
+
- lib/flow_chat/meta/signature.rb
|
|
233
|
+
- lib/flow_chat/meta/signature_validation.rb
|
|
234
|
+
- lib/flow_chat/meta/webhook_verification.rb
|
|
235
|
+
- lib/flow_chat/named_configuration.rb
|
|
140
236
|
- lib/flow_chat/phone_number_util.rb
|
|
237
|
+
- lib/flow_chat/processor.rb
|
|
141
238
|
- lib/flow_chat/prompt.rb
|
|
239
|
+
- lib/flow_chat/renderers/markdown_support.rb
|
|
240
|
+
- lib/flow_chat/security.rb
|
|
142
241
|
- lib/flow_chat/session/cache_session_store.rb
|
|
143
242
|
- lib/flow_chat/session/middleware.rb
|
|
144
|
-
- lib/flow_chat/session/rails_session_store.rb
|
|
145
243
|
- lib/flow_chat/simulator/controller.rb
|
|
146
244
|
- lib/flow_chat/simulator/views/simulator.html.erb
|
|
147
|
-
- lib/flow_chat/
|
|
245
|
+
- lib/flow_chat/telegram.rb
|
|
246
|
+
- lib/flow_chat/telegram/client.rb
|
|
247
|
+
- lib/flow_chat/telegram/configuration.rb
|
|
248
|
+
- lib/flow_chat/telegram/configuration_error.rb
|
|
249
|
+
- lib/flow_chat/telegram/gateway/bot_api.rb
|
|
250
|
+
- lib/flow_chat/telegram/middleware/choice_mapper.rb
|
|
251
|
+
- lib/flow_chat/telegram/renderer.rb
|
|
252
|
+
- lib/flow_chat/text_truncator.rb
|
|
148
253
|
- lib/flow_chat/ussd/gateway/nalo.rb
|
|
149
|
-
- lib/flow_chat/ussd/gateway/nsano.rb
|
|
150
254
|
- lib/flow_chat/ussd/middleware/choice_mapper.rb
|
|
151
|
-
- lib/flow_chat/ussd/middleware/executor.rb
|
|
152
255
|
- lib/flow_chat/ussd/middleware/pagination.rb
|
|
153
|
-
- lib/flow_chat/ussd/processor.rb
|
|
154
256
|
- lib/flow_chat/ussd/renderer.rb
|
|
155
257
|
- lib/flow_chat/version.rb
|
|
156
|
-
- lib/flow_chat/whatsapp/app.rb
|
|
157
258
|
- lib/flow_chat/whatsapp/client.rb
|
|
158
259
|
- lib/flow_chat/whatsapp/configuration.rb
|
|
260
|
+
- lib/flow_chat/whatsapp/configuration_error.rb
|
|
159
261
|
- lib/flow_chat/whatsapp/gateway/cloud_api.rb
|
|
160
|
-
- lib/flow_chat/whatsapp/middleware/
|
|
161
|
-
- lib/flow_chat/whatsapp/processor.rb
|
|
262
|
+
- lib/flow_chat/whatsapp/middleware/choice_mapper.rb
|
|
162
263
|
- lib/flow_chat/whatsapp/renderer.rb
|
|
163
|
-
- lib/flow_chat/whatsapp/send_job_support.rb
|
|
164
264
|
- lib/flow_chat/whatsapp/template_manager.rb
|
|
265
|
+
- lib/tasks/release.rake
|
|
266
|
+
- site/.nojekyll
|
|
267
|
+
- site/.og-card.html
|
|
268
|
+
- site/favicon.svg
|
|
269
|
+
- site/index.html
|
|
270
|
+
- site/og.png
|
|
165
271
|
homepage: https://github.com/radioactive-labs/flow_chat
|
|
166
272
|
licenses:
|
|
167
273
|
- MIT
|
|
168
274
|
metadata:
|
|
169
275
|
allowed_push_host: https://rubygems.org
|
|
276
|
+
rubygems_mfa_required: 'true'
|
|
170
277
|
homepage_uri: https://github.com/radioactive-labs/flow_chat
|
|
171
278
|
source_code_uri: https://github.com/radioactive-labs/flow_chat
|
|
172
279
|
changelog_uri: https://github.com/radioactive-labs/flow_chat
|
data/docs/flows.md
DELETED
|
@@ -1,320 +0,0 @@
|
|
|
1
|
-
# Flow Development Guide
|
|
2
|
-
|
|
3
|
-
This guide covers advanced flow patterns, validation techniques, and best practices for building sophisticated conversational workflows.
|
|
4
|
-
|
|
5
|
-
## Flow Architecture
|
|
6
|
-
|
|
7
|
-
### Flow Lifecycle
|
|
8
|
-
|
|
9
|
-
Every flow method must result in user interaction:
|
|
10
|
-
|
|
11
|
-
```ruby
|
|
12
|
-
class ExampleFlow < FlowChat::Flow
|
|
13
|
-
def main_page
|
|
14
|
-
# ✅ Always end with user interaction
|
|
15
|
-
choice = app.screen(:choice) { |p| p.select "Choose:", ["A", "B"] }
|
|
16
|
-
|
|
17
|
-
case choice
|
|
18
|
-
when "A"
|
|
19
|
-
handle_option_a
|
|
20
|
-
app.say "Option A completed!" # Required interaction
|
|
21
|
-
when "B"
|
|
22
|
-
handle_option_b
|
|
23
|
-
app.say "Option B completed!" # Required interaction
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
### Session Management
|
|
30
|
-
|
|
31
|
-
FlowChat automatically persists screen results:
|
|
32
|
-
|
|
33
|
-
```ruby
|
|
34
|
-
class RegistrationFlow < FlowChat::Flow
|
|
35
|
-
def main_page
|
|
36
|
-
# These values persist across requests
|
|
37
|
-
name = app.screen(:name) { |p| p.ask "Name?" }
|
|
38
|
-
email = app.screen(:email) { |p| p.ask "Email?" }
|
|
39
|
-
|
|
40
|
-
# Show summary using cached values
|
|
41
|
-
confirmed = app.screen(:confirm) do |prompt|
|
|
42
|
-
prompt.yes? "Create account for #{name} (#{email})?"
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
if confirmed
|
|
46
|
-
create_user(name: name, email: email)
|
|
47
|
-
app.say "Account created!"
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
## Input Validation Patterns
|
|
54
|
-
|
|
55
|
-
### Basic Validation
|
|
56
|
-
|
|
57
|
-
```ruby
|
|
58
|
-
age = app.screen(:age) do |prompt|
|
|
59
|
-
prompt.ask "Enter your age:",
|
|
60
|
-
validate: ->(input) {
|
|
61
|
-
return "Age must be a number" unless input.match?(/^\d+$/)
|
|
62
|
-
return "Must be 18 or older" unless input.to_i >= 18
|
|
63
|
-
nil # Return nil for valid input
|
|
64
|
-
},
|
|
65
|
-
transform: ->(input) { input.to_i }
|
|
66
|
-
end
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
### Complex Validation
|
|
70
|
-
|
|
71
|
-
```ruby
|
|
72
|
-
phone = app.screen(:phone) do |prompt|
|
|
73
|
-
prompt.ask "Enter phone number:",
|
|
74
|
-
validate: ->(input) {
|
|
75
|
-
clean = input.gsub(/[\s\-\(\)]/, '')
|
|
76
|
-
return "Invalid format" unless clean.match?(/^\+?[\d]{10,15}$/)
|
|
77
|
-
return "Must start with country code" unless clean.start_with?('+')
|
|
78
|
-
nil
|
|
79
|
-
},
|
|
80
|
-
transform: ->(input) { input.gsub(/[\s\-\(\)]/, '') }
|
|
81
|
-
end
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
### Conditional Validation
|
|
85
|
-
|
|
86
|
-
```ruby
|
|
87
|
-
class PaymentFlow < FlowChat::Flow
|
|
88
|
-
def collect_payment_method
|
|
89
|
-
method = app.screen(:method) do |prompt|
|
|
90
|
-
prompt.select "Payment method:", ["card", "mobile_money"]
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
if method == "card"
|
|
94
|
-
collect_card_details
|
|
95
|
-
else
|
|
96
|
-
collect_mobile_money_details
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
private
|
|
101
|
-
|
|
102
|
-
def collect_card_details
|
|
103
|
-
card = app.screen(:card) do |prompt|
|
|
104
|
-
prompt.ask "Card number (16 digits):",
|
|
105
|
-
validate: ->(input) {
|
|
106
|
-
clean = input.gsub(/\s/, '')
|
|
107
|
-
return "Must be 16 digits" unless clean.length == 16
|
|
108
|
-
return "Invalid card number" unless luhn_valid?(clean)
|
|
109
|
-
nil
|
|
110
|
-
}
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
app.say "Card ending in #{card[-4..-1]} saved."
|
|
114
|
-
end
|
|
115
|
-
end
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
## Menu Patterns
|
|
119
|
-
|
|
120
|
-
### Dynamic Menus
|
|
121
|
-
|
|
122
|
-
```ruby
|
|
123
|
-
def show_products
|
|
124
|
-
products = fetch_available_products
|
|
125
|
-
|
|
126
|
-
choice = app.screen(:product) do |prompt|
|
|
127
|
-
prompt.select "Choose product:", products.map(&:name)
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
selected_product = products.find { |p| p.name == choice }
|
|
131
|
-
show_product_details(selected_product)
|
|
132
|
-
end
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
### Nested Menus
|
|
136
|
-
|
|
137
|
-
```ruby
|
|
138
|
-
def main_menu
|
|
139
|
-
choice = app.screen(:main) do |prompt|
|
|
140
|
-
prompt.select "Main Menu:", {
|
|
141
|
-
"products" => "View Products",
|
|
142
|
-
"orders" => "My Orders",
|
|
143
|
-
"support" => "Customer Support"
|
|
144
|
-
}
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
case choice
|
|
148
|
-
when "products"
|
|
149
|
-
products_menu
|
|
150
|
-
when "orders"
|
|
151
|
-
orders_menu
|
|
152
|
-
when "support"
|
|
153
|
-
support_menu
|
|
154
|
-
end
|
|
155
|
-
end
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
## Advanced Patterns
|
|
159
|
-
|
|
160
|
-
### Multi-Step Forms
|
|
161
|
-
|
|
162
|
-
```ruby
|
|
163
|
-
class CompleteProfileFlow < FlowChat::Flow
|
|
164
|
-
def main_page
|
|
165
|
-
collect_basic_info
|
|
166
|
-
collect_preferences
|
|
167
|
-
confirm_and_save
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
private
|
|
171
|
-
|
|
172
|
-
def collect_basic_info
|
|
173
|
-
app.screen(:name) { |p| p.ask "Full name:" }
|
|
174
|
-
app.screen(:email) { |p| p.ask "Email:" }
|
|
175
|
-
app.screen(:phone) { |p| p.ask "Phone:" }
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
def collect_preferences
|
|
179
|
-
app.screen(:language) { |p| p.select "Language:", ["English", "French"] }
|
|
180
|
-
app.screen(:notifications) { |p| p.yes? "Enable notifications?" }
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
def confirm_and_save
|
|
184
|
-
summary = build_summary
|
|
185
|
-
confirmed = app.screen(:confirm) { |p| p.yes? "Save profile?\n\n#{summary}" }
|
|
186
|
-
|
|
187
|
-
if confirmed
|
|
188
|
-
save_profile
|
|
189
|
-
app.say "Profile saved successfully!"
|
|
190
|
-
else
|
|
191
|
-
app.say "Profile not saved."
|
|
192
|
-
end
|
|
193
|
-
end
|
|
194
|
-
end
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
### Error Recovery
|
|
198
|
-
|
|
199
|
-
```ruby
|
|
200
|
-
def process_payment
|
|
201
|
-
begin
|
|
202
|
-
amount = app.screen(:amount) do |prompt|
|
|
203
|
-
prompt.ask "Amount to pay:",
|
|
204
|
-
validate: ->(input) {
|
|
205
|
-
return "Invalid amount" unless input.match?(/^\d+(\.\d{2})?$/)
|
|
206
|
-
return "Minimum $1.00" unless input.to_f >= 1.0
|
|
207
|
-
nil
|
|
208
|
-
},
|
|
209
|
-
transform: ->(input) { input.to_f }
|
|
210
|
-
end
|
|
211
|
-
|
|
212
|
-
process_transaction(amount)
|
|
213
|
-
app.say "Payment of $#{amount} processed successfully!"
|
|
214
|
-
|
|
215
|
-
rescue PaymentError => e
|
|
216
|
-
app.say "Payment failed: #{e.message}. Please try again."
|
|
217
|
-
process_payment # Retry
|
|
218
|
-
end
|
|
219
|
-
end
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
## Cross-Platform Considerations
|
|
223
|
-
|
|
224
|
-
### Platform Detection
|
|
225
|
-
|
|
226
|
-
```ruby
|
|
227
|
-
def show_help
|
|
228
|
-
if app.context["request.gateway"] == :whatsapp_cloud_api
|
|
229
|
-
# WhatsApp users get rich media
|
|
230
|
-
app.say "Here's how to use our service:",
|
|
231
|
-
media: { type: :image, url: "https://example.com/help.jpg" }
|
|
232
|
-
else
|
|
233
|
-
# USSD users get text with link
|
|
234
|
-
app.say "Help guide: https://example.com/help"
|
|
235
|
-
end
|
|
236
|
-
end
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
### Progressive Enhancement
|
|
240
|
-
|
|
241
|
-
```ruby
|
|
242
|
-
def collect_feedback
|
|
243
|
-
rating = app.screen(:rating) do |prompt|
|
|
244
|
-
if whatsapp?
|
|
245
|
-
# Rich interactive buttons for WhatsApp
|
|
246
|
-
prompt.select "Rate our service:", {
|
|
247
|
-
"5" => "⭐⭐⭐⭐⭐ Excellent",
|
|
248
|
-
"4" => "⭐⭐⭐⭐ Good",
|
|
249
|
-
"3" => "⭐⭐⭐ Average",
|
|
250
|
-
"2" => "⭐⭐ Poor",
|
|
251
|
-
"1" => "⭐ Very Poor"
|
|
252
|
-
}
|
|
253
|
-
else
|
|
254
|
-
# Simple numbered list for USSD
|
|
255
|
-
prompt.select "Rate our service (1-5):", ["1", "2", "3", "4", "5"]
|
|
256
|
-
end
|
|
257
|
-
end
|
|
258
|
-
|
|
259
|
-
app.say "Thank you for rating us #{rating} stars!"
|
|
260
|
-
end
|
|
261
|
-
|
|
262
|
-
private
|
|
263
|
-
|
|
264
|
-
def whatsapp?
|
|
265
|
-
app.context["request.gateway"] == :whatsapp_cloud_api
|
|
266
|
-
end
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
## Best Practices
|
|
270
|
-
|
|
271
|
-
### Keep Methods Focused
|
|
272
|
-
|
|
273
|
-
```ruby
|
|
274
|
-
# ✅ Good: Single responsibility
|
|
275
|
-
def collect_contact_info
|
|
276
|
-
name = app.screen(:name) { |p| p.ask "Name:" }
|
|
277
|
-
email = app.screen(:email) { |p| p.ask "Email:" }
|
|
278
|
-
{ name: name, email: email }
|
|
279
|
-
end
|
|
280
|
-
|
|
281
|
-
# ❌ Avoid: Too much in one method
|
|
282
|
-
def handle_everything
|
|
283
|
-
# 50+ lines of mixed logic
|
|
284
|
-
end
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
### Use Meaningful Screen Names
|
|
288
|
-
|
|
289
|
-
```ruby
|
|
290
|
-
# ✅ Good: Descriptive names
|
|
291
|
-
app.screen(:billing_address) { |p| p.ask "Billing address:" }
|
|
292
|
-
app.screen(:confirm_payment) { |p| p.yes? "Confirm $#{amount}?" }
|
|
293
|
-
|
|
294
|
-
# ❌ Avoid: Generic names
|
|
295
|
-
app.screen(:input1) { |p| p.ask "Address:" }
|
|
296
|
-
app.screen(:confirm) { |p| p.yes? "OK?" }
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
### Handle Edge Cases
|
|
300
|
-
|
|
301
|
-
```ruby
|
|
302
|
-
def show_order_history
|
|
303
|
-
orders = fetch_user_orders
|
|
304
|
-
|
|
305
|
-
if orders.empty?
|
|
306
|
-
app.say "You have no previous orders."
|
|
307
|
-
return
|
|
308
|
-
end
|
|
309
|
-
|
|
310
|
-
choice = app.screen(:order) do |prompt|
|
|
311
|
-
prompt.select "Select order:", orders.map(&:display_name)
|
|
312
|
-
end
|
|
313
|
-
|
|
314
|
-
show_order_details(orders.find { |o| o.display_name == choice })
|
|
315
|
-
end
|
|
316
|
-
```
|
|
317
|
-
|
|
318
|
-
## Testing Flows
|
|
319
|
-
|
|
320
|
-
See [Testing Guide](testing.md) for comprehensive testing strategies.
|