flow_chat 0.8.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/.cliff.toml +74 -0
  3. data/.github/workflows/ci.yml +2 -3
  4. data/.github/workflows/release.yml +56 -0
  5. data/.standard.yml +4 -0
  6. data/CHANGELOG.md +22 -0
  7. data/CLAUDE.md +327 -0
  8. data/CONTRIBUTING.md +134 -0
  9. data/Gemfile +1 -0
  10. data/README.md +290 -105
  11. data/Rakefile +5 -1
  12. data/SECURITY.md +42 -349
  13. data/docs/architecture.md +510 -0
  14. data/docs/async-background-processing.md +298 -0
  15. data/docs/configuration.md +556 -226
  16. data/docs/factory-pattern.md +355 -0
  17. data/docs/gateway-context-variables.md +171 -0
  18. data/docs/gateway-development.md +723 -0
  19. data/docs/getting-started.md +429 -0
  20. data/docs/instrumentation.md +264 -153
  21. data/docs/platforms/telegram.md +1013 -0
  22. data/docs/platforms/ussd.md +693 -0
  23. data/docs/platforms/whatsapp.md +1395 -0
  24. data/docs/testing.md +243 -365
  25. data/examples/custom_session_id_example.rb +119 -0
  26. data/examples/http_controller.rb +154 -0
  27. data/examples/intercom_configuration_example.rb +118 -0
  28. data/examples/intercom_controller.rb +194 -0
  29. data/examples/multi_tenant_whatsapp_controller.rb +4 -4
  30. data/examples/simulator_controller.rb +21 -1
  31. data/examples/ussd_controller.rb +10 -10
  32. data/examples/whatsapp_controller.rb +2 -2
  33. data/flow_chat.gemspec +4 -0
  34. data/lib/flow_chat/{base_app.rb → app.rb} +28 -9
  35. data/lib/flow_chat/async_job.rb +176 -0
  36. data/lib/flow_chat/config.rb +23 -28
  37. data/lib/flow_chat/{base_executor.rb → executor.rb} +7 -12
  38. data/lib/flow_chat/factory.rb +94 -0
  39. data/lib/flow_chat/gateway_async_support.rb +106 -0
  40. data/lib/flow_chat/generic_async_job.rb +30 -0
  41. data/lib/flow_chat/http/gateway/simple.rb +125 -0
  42. data/lib/flow_chat/http/renderer.rb +41 -0
  43. data/lib/flow_chat/instrumentation/setup.rb +1 -1
  44. data/lib/flow_chat/instrumentation.rb +25 -0
  45. data/lib/flow_chat/intercom/client.rb +155 -0
  46. data/lib/flow_chat/intercom/configuration.rb +149 -0
  47. data/lib/flow_chat/intercom/gateway/intercom_api.rb +396 -0
  48. data/lib/flow_chat/intercom/renderer.rb +71 -0
  49. data/lib/flow_chat/phone_number_util.rb +49 -0
  50. data/lib/flow_chat/processor.rb +188 -0
  51. data/lib/flow_chat/renderers/markdown_support.rb +58 -0
  52. data/lib/flow_chat/session/cache_session_store.rb +1 -17
  53. data/lib/flow_chat/session/middleware.rb +43 -26
  54. data/lib/flow_chat/simulator/controller.rb +17 -5
  55. data/lib/flow_chat/simulator/views/simulator.html.erb +220 -8
  56. data/lib/flow_chat/telegram/client.rb +240 -0
  57. data/lib/flow_chat/telegram/configuration.rb +118 -0
  58. data/lib/flow_chat/telegram/gateway/bot_api.rb +300 -0
  59. data/lib/flow_chat/telegram/middleware/choice_mapper.rb +35 -0
  60. data/lib/flow_chat/telegram/renderer.rb +125 -0
  61. data/lib/flow_chat/telegram.rb +7 -0
  62. data/lib/flow_chat/ussd/gateway/nalo.rb +27 -11
  63. data/lib/flow_chat/ussd/middleware/pagination.rb +9 -5
  64. data/lib/flow_chat/ussd/renderer.rb +1 -1
  65. data/lib/flow_chat/version.rb +1 -1
  66. data/lib/flow_chat/whatsapp/client.rb +144 -13
  67. data/lib/flow_chat/whatsapp/configuration.rb +1 -1
  68. data/lib/flow_chat/whatsapp/gateway/cloud_api.rb +144 -106
  69. data/lib/flow_chat/whatsapp/id_generator.rb +124 -0
  70. data/lib/flow_chat/whatsapp/middleware/choice_mapper.rb +147 -0
  71. data/lib/flow_chat/whatsapp/renderer.rb +145 -11
  72. data/lib/flow_chat.rb +11 -1
  73. data/lib/tasks/release.rake +165 -0
  74. metadata +88 -21
  75. data/docs/flows.md +0 -320
  76. data/docs/images/simulator.png +0 -0
  77. data/docs/media.md +0 -153
  78. data/docs/sessions.md +0 -433
  79. data/docs/ussd-setup.md +0 -322
  80. data/docs/whatsapp-setup.md +0 -162
  81. data/examples/whatsapp_message_job.rb +0 -113
  82. data/lib/flow_chat/base_processor.rb +0 -145
  83. data/lib/flow_chat/session/rails_session_store.rb +0 -68
  84. data/lib/flow_chat/ussd/app.rb +0 -6
  85. data/lib/flow_chat/ussd/gateway/nsano.rb +0 -98
  86. data/lib/flow_chat/ussd/middleware/executor.rb +0 -24
  87. data/lib/flow_chat/ussd/processor.rb +0 -39
  88. data/lib/flow_chat/whatsapp/app.rb +0 -29
  89. data/lib/flow_chat/whatsapp/middleware/executor.rb +0 -24
  90. data/lib/flow_chat/whatsapp/processor.rb +0 -32
  91. data/lib/flow_chat/whatsapp/send_job_support.rb +0 -79
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.8.1
4
+ version: 0.9.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: 2025-06-14 00:00:00.000000000 Z
11
+ date: 2026-07-09 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,15 @@ 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/release.yml"
96
140
  - ".gitignore"
97
141
  - ".ruby-version"
142
+ - ".standard.yml"
143
+ - CHANGELOG.md
144
+ - CLAUDE.md
145
+ - CONTRIBUTING.md
98
146
  - Gemfile
99
147
  - LICENSE.txt
100
148
  - README.md
@@ -102,63 +150,82 @@ files:
102
150
  - SECURITY.md
103
151
  - bin/console
104
152
  - bin/setup
153
+ - docs/architecture.md
154
+ - docs/async-background-processing.md
105
155
  - docs/configuration.md
106
- - docs/flows.md
107
- - docs/images/simulator.png
156
+ - docs/factory-pattern.md
157
+ - docs/gateway-context-variables.md
158
+ - docs/gateway-development.md
159
+ - docs/getting-started.md
108
160
  - docs/instrumentation.md
109
- - docs/media.md
110
- - docs/sessions.md
161
+ - docs/platforms/telegram.md
162
+ - docs/platforms/ussd.md
163
+ - docs/platforms/whatsapp.md
111
164
  - docs/testing.md
112
- - docs/ussd-setup.md
113
- - docs/whatsapp-setup.md
165
+ - examples/custom_session_id_example.rb
166
+ - examples/http_controller.rb
167
+ - examples/intercom_configuration_example.rb
168
+ - examples/intercom_controller.rb
114
169
  - examples/multi_tenant_whatsapp_controller.rb
115
170
  - examples/simulator_controller.rb
116
171
  - examples/ussd_controller.rb
117
172
  - examples/whatsapp_controller.rb
118
173
  - examples/whatsapp_media_examples.rb
119
- - examples/whatsapp_message_job.rb
120
174
  - flow_chat.gemspec
121
175
  - lib/flow_chat.rb
122
- - lib/flow_chat/base_app.rb
123
- - lib/flow_chat/base_executor.rb
124
- - lib/flow_chat/base_processor.rb
176
+ - lib/flow_chat/app.rb
177
+ - lib/flow_chat/async_job.rb
125
178
  - lib/flow_chat/config.rb
126
179
  - lib/flow_chat/context.rb
180
+ - lib/flow_chat/executor.rb
181
+ - lib/flow_chat/factory.rb
127
182
  - lib/flow_chat/flow.rb
183
+ - lib/flow_chat/gateway_async_support.rb
184
+ - lib/flow_chat/generic_async_job.rb
185
+ - lib/flow_chat/http/gateway/simple.rb
186
+ - lib/flow_chat/http/renderer.rb
128
187
  - lib/flow_chat/instrumentation.rb
129
188
  - lib/flow_chat/instrumentation/log_subscriber.rb
130
189
  - lib/flow_chat/instrumentation/metrics_collector.rb
131
190
  - lib/flow_chat/instrumentation/setup.rb
191
+ - lib/flow_chat/intercom/client.rb
192
+ - lib/flow_chat/intercom/configuration.rb
193
+ - lib/flow_chat/intercom/gateway/intercom_api.rb
194
+ - lib/flow_chat/intercom/renderer.rb
132
195
  - lib/flow_chat/interrupt.rb
196
+ - lib/flow_chat/phone_number_util.rb
197
+ - lib/flow_chat/processor.rb
133
198
  - lib/flow_chat/prompt.rb
199
+ - lib/flow_chat/renderers/markdown_support.rb
134
200
  - lib/flow_chat/session/cache_session_store.rb
135
201
  - lib/flow_chat/session/middleware.rb
136
- - lib/flow_chat/session/rails_session_store.rb
137
202
  - lib/flow_chat/simulator/controller.rb
138
203
  - lib/flow_chat/simulator/views/simulator.html.erb
139
- - lib/flow_chat/ussd/app.rb
204
+ - lib/flow_chat/telegram.rb
205
+ - lib/flow_chat/telegram/client.rb
206
+ - lib/flow_chat/telegram/configuration.rb
207
+ - lib/flow_chat/telegram/gateway/bot_api.rb
208
+ - lib/flow_chat/telegram/middleware/choice_mapper.rb
209
+ - lib/flow_chat/telegram/renderer.rb
140
210
  - lib/flow_chat/ussd/gateway/nalo.rb
141
- - lib/flow_chat/ussd/gateway/nsano.rb
142
211
  - lib/flow_chat/ussd/middleware/choice_mapper.rb
143
- - lib/flow_chat/ussd/middleware/executor.rb
144
212
  - lib/flow_chat/ussd/middleware/pagination.rb
145
- - lib/flow_chat/ussd/processor.rb
146
213
  - lib/flow_chat/ussd/renderer.rb
147
214
  - lib/flow_chat/version.rb
148
- - lib/flow_chat/whatsapp/app.rb
149
215
  - lib/flow_chat/whatsapp/client.rb
150
216
  - lib/flow_chat/whatsapp/configuration.rb
151
217
  - lib/flow_chat/whatsapp/gateway/cloud_api.rb
152
- - lib/flow_chat/whatsapp/middleware/executor.rb
153
- - lib/flow_chat/whatsapp/processor.rb
218
+ - lib/flow_chat/whatsapp/id_generator.rb
219
+ - lib/flow_chat/whatsapp/middleware/choice_mapper.rb
154
220
  - lib/flow_chat/whatsapp/renderer.rb
155
- - lib/flow_chat/whatsapp/send_job_support.rb
156
221
  - lib/flow_chat/whatsapp/template_manager.rb
222
+ - lib/tasks/release.rake
157
223
  homepage: https://github.com/radioactive-labs/flow_chat
158
224
  licenses:
159
225
  - MIT
160
226
  metadata:
161
227
  allowed_push_host: https://rubygems.org
228
+ rubygems_mfa_required: 'true'
162
229
  homepage_uri: https://github.com/radioactive-labs/flow_chat
163
230
  source_code_uri: https://github.com/radioactive-labs/flow_chat
164
231
  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.
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.