flow_chat 0.3.0 → 0.4.1

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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -0
  3. data/README.md +642 -86
  4. data/examples/initializer.rb +31 -0
  5. data/examples/media_prompts_examples.rb +28 -0
  6. data/examples/multi_tenant_whatsapp_controller.rb +244 -0
  7. data/examples/ussd_controller.rb +264 -0
  8. data/examples/whatsapp_controller.rb +140 -0
  9. data/examples/whatsapp_media_examples.rb +406 -0
  10. data/examples/whatsapp_message_job.rb +111 -0
  11. data/lib/flow_chat/base_processor.rb +67 -0
  12. data/lib/flow_chat/config.rb +36 -0
  13. data/lib/flow_chat/session/cache_session_store.rb +84 -0
  14. data/lib/flow_chat/session/middleware.rb +14 -6
  15. data/lib/flow_chat/simulator/controller.rb +78 -0
  16. data/lib/flow_chat/simulator/views/simulator.html.erb +1707 -0
  17. data/lib/flow_chat/ussd/app.rb +25 -0
  18. data/lib/flow_chat/ussd/gateway/nalo.rb +2 -0
  19. data/lib/flow_chat/ussd/gateway/nsano.rb +6 -0
  20. data/lib/flow_chat/ussd/middleware/resumable_session.rb +1 -1
  21. data/lib/flow_chat/ussd/processor.rb +14 -42
  22. data/lib/flow_chat/ussd/prompt.rb +39 -5
  23. data/lib/flow_chat/version.rb +1 -1
  24. data/lib/flow_chat/whatsapp/app.rb +64 -0
  25. data/lib/flow_chat/whatsapp/client.rb +439 -0
  26. data/lib/flow_chat/whatsapp/configuration.rb +113 -0
  27. data/lib/flow_chat/whatsapp/gateway/cloud_api.rb +213 -0
  28. data/lib/flow_chat/whatsapp/middleware/executor.rb +30 -0
  29. data/lib/flow_chat/whatsapp/processor.rb +26 -0
  30. data/lib/flow_chat/whatsapp/prompt.rb +251 -0
  31. data/lib/flow_chat/whatsapp/send_job_support.rb +79 -0
  32. data/lib/flow_chat/whatsapp/template_manager.rb +162 -0
  33. data/lib/flow_chat.rb +1 -0
  34. metadata +21 -3
  35. data/lib/flow_chat/ussd/simulator/controller.rb +0 -51
  36. data/lib/flow_chat/ussd/simulator/views/simulator.html.erb +0 -239
@@ -0,0 +1,31 @@
1
+ # Example FlowChat Initializer
2
+ # Add this to your Rails application as config/initializers/flow_chat.rb
3
+
4
+ # Configure cache for session storage
5
+ # This is required when using FlowChat::Session::CacheSessionStore
6
+ FlowChat::Config.cache = Rails.cache
7
+
8
+ # Alternative cache configurations:
9
+
10
+ # Use a specific cache store
11
+ # FlowChat::Config.cache = ActiveSupport::Cache::MemoryStore.new
12
+
13
+ # Use Redis (requires redis gem)
14
+ # FlowChat::Config.cache = ActiveSupport::Cache::RedisCacheStore.new(url: "redis://localhost:6379/1")
15
+
16
+ # Use Memcached (requires dalli gem)
17
+ # FlowChat::Config.cache = ActiveSupport::Cache::MemCacheStore.new("localhost:11211")
18
+
19
+ # Configure logger (optional)
20
+ FlowChat::Config.logger = Rails.logger
21
+
22
+ # Configure USSD pagination (optional)
23
+ FlowChat::Config.ussd.pagination_page_size = 140
24
+ FlowChat::Config.ussd.pagination_back_option = "0"
25
+ FlowChat::Config.ussd.pagination_back_text = "Back"
26
+ FlowChat::Config.ussd.pagination_next_option = "#"
27
+ FlowChat::Config.ussd.pagination_next_text = "More"
28
+
29
+ # Configure resumable sessions (optional)
30
+ FlowChat::Config.ussd.resumable_sessions_enabled = true
31
+ FlowChat::Config.ussd.resumable_sessions_timeout_seconds = 300 # 5 minutes
@@ -0,0 +1,28 @@
1
+ # Media Prompts Examples
2
+ # This file demonstrates how to attach media to prompts in FlowChat
3
+
4
+ # ============================================================================
5
+ # BASIC MEDIA PROMPTS
6
+ # ============================================================================
7
+
8
+ class MediaPromptFlow < FlowChat::Flow
9
+ def main_page
10
+ # ✅ Simple text input with attached image
11
+ # The prompt text becomes the image caption
12
+ feedback = app.screen(:feedback) do |prompt|
13
+ prompt.ask "What do you think of our new product?",
14
+ media: {
15
+ type: :image,
16
+ url: "https://cdn.example.com/products/new_product.jpg"
17
+ }
18
+ end
19
+
20
+ # ✅ Send media message with say
21
+ app.say "Thank you for your feedback! Here's what's coming next:",
22
+ media: {
23
+ type: :video,
24
+ url: "https://videos.example.com/roadmap.mp4"
25
+ }
26
+ end
27
+ end
28
+
@@ -0,0 +1,244 @@
1
+ # Example Multi-Tenant WhatsApp Controller
2
+ # This shows how to configure different WhatsApp accounts per tenant/client
3
+
4
+ class MultiTenantWhatsappController < ApplicationController
5
+ skip_forgery_protection
6
+
7
+ def webhook
8
+ # Determine tenant from subdomain, path, or other logic
9
+ tenant = determine_tenant(request)
10
+
11
+ # Get tenant-specific WhatsApp configuration
12
+ whatsapp_config = get_whatsapp_config_for_tenant(tenant)
13
+
14
+ processor = FlowChat::Whatsapp::Processor.new(self) do |config|
15
+ config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi, whatsapp_config
16
+ config.use_session_store FlowChat::Session::CacheSessionStore
17
+ end
18
+
19
+ # Use tenant-specific flow
20
+ flow_class = get_flow_for_tenant(tenant)
21
+ processor.run flow_class, :main_page
22
+ end
23
+
24
+ private
25
+
26
+ def determine_tenant(request)
27
+ # Option 1: From subdomain
28
+ return request.subdomain if request.subdomain.present?
29
+
30
+ # Option 2: From path
31
+ tenant_from_path = request.path.match(%r{^/whatsapp/(\w+)/})&.captures&.first
32
+ return tenant_from_path if tenant_from_path
33
+
34
+ # Option 3: From custom header
35
+ return request.headers['X-Tenant-ID'] if request.headers['X-Tenant-ID']
36
+
37
+ # Fallback to default
38
+ 'default'
39
+ end
40
+
41
+ def get_whatsapp_config_for_tenant(tenant)
42
+ case tenant
43
+ when 'acme_corp'
44
+ FlowChat::Whatsapp::Configuration.new.tap do |config|
45
+ config.access_token = ENV['ACME_WHATSAPP_ACCESS_TOKEN']
46
+ config.phone_number_id = ENV['ACME_WHATSAPP_PHONE_NUMBER_ID']
47
+ config.verify_token = ENV['ACME_WHATSAPP_VERIFY_TOKEN']
48
+ config.app_id = ENV['ACME_WHATSAPP_APP_ID']
49
+ config.app_secret = ENV['ACME_WHATSAPP_APP_SECRET']
50
+ config.business_account_id = ENV['ACME_WHATSAPP_BUSINESS_ACCOUNT_ID']
51
+ end
52
+
53
+ when 'tech_startup'
54
+ FlowChat::Whatsapp::Configuration.new.tap do |config|
55
+ config.access_token = ENV['TECHSTARTUP_WHATSAPP_ACCESS_TOKEN']
56
+ config.phone_number_id = ENV['TECHSTARTUP_WHATSAPP_PHONE_NUMBER_ID']
57
+ config.verify_token = ENV['TECHSTARTUP_WHATSAPP_VERIFY_TOKEN']
58
+ config.app_id = ENV['TECHSTARTUP_WHATSAPP_APP_ID']
59
+ config.app_secret = ENV['TECHSTARTUP_WHATSAPP_APP_SECRET']
60
+ config.business_account_id = ENV['TECHSTARTUP_WHATSAPP_BUSINESS_ACCOUNT_ID']
61
+ end
62
+
63
+ when 'retail_store'
64
+ # Load from database
65
+ tenant_config = WhatsappConfiguration.find_by(tenant: tenant)
66
+ FlowChat::Whatsapp::Configuration.new.tap do |config|
67
+ config.access_token = tenant_config.access_token
68
+ config.phone_number_id = tenant_config.phone_number_id
69
+ config.verify_token = tenant_config.verify_token
70
+ config.app_id = tenant_config.app_id
71
+ config.app_secret = tenant_config.app_secret
72
+ config.business_account_id = tenant_config.business_account_id
73
+ end
74
+
75
+ else
76
+ # Use default/global configuration
77
+ FlowChat::Whatsapp::Configuration.from_credentials
78
+ end
79
+ end
80
+
81
+ def get_flow_for_tenant(tenant)
82
+ case tenant
83
+ when 'acme_corp'
84
+ AcmeCorpFlow
85
+ when 'tech_startup'
86
+ TechStartupFlow
87
+ when 'retail_store'
88
+ RetailStoreFlow
89
+ else
90
+ WelcomeFlow # Default flow
91
+ end
92
+ end
93
+ end
94
+
95
+ # Example: Dynamic Configuration from Database
96
+ class DatabaseWhatsappController < ApplicationController
97
+ skip_forgery_protection
98
+
99
+ def webhook
100
+ # Get account from business phone number or other identifier
101
+ business_account = find_business_account(params)
102
+
103
+ if business_account.nil?
104
+ return head :not_found
105
+ end
106
+
107
+ # Create configuration from database record
108
+ whatsapp_config = FlowChat::Whatsapp::Configuration.new.tap do |config|
109
+ config.access_token = business_account.whatsapp_access_token
110
+ config.phone_number_id = business_account.whatsapp_phone_number_id
111
+ config.verify_token = business_account.whatsapp_verify_token
112
+ config.app_id = business_account.whatsapp_app_id
113
+ config.app_secret = business_account.whatsapp_app_secret
114
+ config.business_account_id = business_account.whatsapp_business_account_id
115
+ end
116
+
117
+ processor = FlowChat::Whatsapp::Processor.new(self) do |config|
118
+ config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi, whatsapp_config
119
+ config.use_session_store FlowChat::Session::CacheSessionStore
120
+ end
121
+
122
+ processor.run business_account.flow_class.constantize, :main_page
123
+ end
124
+
125
+ private
126
+
127
+ def find_business_account(params)
128
+ # You could identify the account by:
129
+ # 1. Phone number ID from webhook
130
+ # 2. Business account ID from webhook
131
+ # 3. Custom routing parameter
132
+
133
+ # Example: Find by phone number ID in webhook
134
+ phone_number_id = extract_phone_number_id_from_webhook(params)
135
+ BusinessAccount.find_by(whatsapp_phone_number_id: phone_number_id)
136
+ end
137
+
138
+ def extract_phone_number_id_from_webhook(params)
139
+ # Extract from webhook payload structure
140
+ # This would need to be implemented based on your webhook structure
141
+ nil
142
+ end
143
+ end
144
+
145
+ # Example: Environment-based Configuration
146
+ class EnvironmentWhatsappController < ApplicationController
147
+ skip_forgery_protection
148
+
149
+ def webhook
150
+ # Different configurations for different environments
151
+ whatsapp_config = case Rails.env
152
+ when 'production'
153
+ production_whatsapp_config
154
+ when 'staging'
155
+ staging_whatsapp_config
156
+ when 'development'
157
+ development_whatsapp_config
158
+ else
159
+ FlowChat::Whatsapp::Configuration.from_credentials
160
+ end
161
+
162
+ processor = FlowChat::Whatsapp::Processor.new(self) do |config|
163
+ config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi, whatsapp_config
164
+ config.use_session_store FlowChat::Session::CacheSessionStore
165
+ end
166
+
167
+ processor.run WelcomeFlow, :main_page
168
+ end
169
+
170
+ private
171
+
172
+ def production_whatsapp_config
173
+ FlowChat::Whatsapp::Configuration.new.tap do |config|
174
+ config.access_token = ENV['PROD_WHATSAPP_ACCESS_TOKEN']
175
+ config.phone_number_id = ENV['PROD_WHATSAPP_PHONE_NUMBER_ID']
176
+ config.verify_token = ENV['PROD_WHATSAPP_VERIFY_TOKEN']
177
+ config.app_id = ENV['PROD_WHATSAPP_APP_ID']
178
+ config.app_secret = ENV['PROD_WHATSAPP_APP_SECRET']
179
+ config.business_account_id = ENV['PROD_WHATSAPP_BUSINESS_ACCOUNT_ID']
180
+ end
181
+ end
182
+
183
+ def staging_whatsapp_config
184
+ FlowChat::Whatsapp::Configuration.new.tap do |config|
185
+ config.access_token = ENV['STAGING_WHATSAPP_ACCESS_TOKEN']
186
+ config.phone_number_id = ENV['STAGING_WHATSAPP_PHONE_NUMBER_ID']
187
+ config.verify_token = ENV['STAGING_WHATSAPP_VERIFY_TOKEN']
188
+ config.app_id = ENV['STAGING_WHATSAPP_APP_ID']
189
+ config.app_secret = ENV['STAGING_WHATSAPP_APP_SECRET']
190
+ config.business_account_id = ENV['STAGING_WHATSAPP_BUSINESS_ACCOUNT_ID']
191
+ end
192
+ end
193
+
194
+ def development_whatsapp_config
195
+ FlowChat::Whatsapp::Configuration.new.tap do |config|
196
+ config.access_token = ENV['DEV_WHATSAPP_ACCESS_TOKEN']
197
+ config.phone_number_id = ENV['DEV_WHATSAPP_PHONE_NUMBER_ID']
198
+ config.verify_token = ENV['DEV_WHATSAPP_VERIFY_TOKEN']
199
+ config.app_id = ENV['DEV_WHATSAPP_APP_ID']
200
+ config.app_secret = ENV['DEV_WHATSAPP_APP_SECRET']
201
+ config.business_account_id = ENV['DEV_WHATSAPP_BUSINESS_ACCOUNT_ID']
202
+ end
203
+ end
204
+ end
205
+
206
+ # Example: Simple Custom Configuration
207
+ class CustomWhatsappController < ApplicationController
208
+ skip_forgery_protection
209
+
210
+ def webhook
211
+ # Create custom configuration for this specific endpoint
212
+ my_config = FlowChat::Whatsapp::Configuration.new
213
+ my_config.access_token = "EAABs..." # Your specific access token
214
+ my_config.phone_number_id = "123456789"
215
+ my_config.verify_token = "my_verify_token"
216
+ my_config.app_id = "your_app_id"
217
+ my_config.app_secret = "your_app_secret"
218
+ my_config.business_account_id = "your_business_account_id"
219
+
220
+ processor = FlowChat::Whatsapp::Processor.new(self) do |config|
221
+ config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi, my_config
222
+ config.use_session_store FlowChat::Session::CacheSessionStore
223
+ end
224
+
225
+ processor.run CustomFlow, :main_page
226
+ end
227
+ end
228
+
229
+ # Add routes for different tenants:
230
+ # Rails.application.routes.draw do
231
+ # # Subdomain-based routing
232
+ # constraints subdomain: /\w+/ do
233
+ # post '/whatsapp/webhook', to: 'multi_tenant_whatsapp#webhook'
234
+ # end
235
+ #
236
+ # # Path-based routing
237
+ # post '/whatsapp/:tenant/webhook', to: 'multi_tenant_whatsapp#webhook'
238
+ #
239
+ # # Environment-specific
240
+ # post '/whatsapp/env/webhook', to: 'environment_whatsapp#webhook'
241
+ #
242
+ # # Custom endpoint
243
+ # post '/whatsapp/custom/webhook', to: 'custom_whatsapp#webhook'
244
+ # end
@@ -0,0 +1,264 @@
1
+ # Example USSD Controller
2
+ # Add this to your Rails application as app/controllers/ussd_controller.rb
3
+
4
+ class UssdController < ApplicationController
5
+ skip_forgery_protection
6
+
7
+ def process_request
8
+ processor = FlowChat::Ussd::Processor.new(self) do |config|
9
+ config.use_gateway FlowChat::Ussd::Gateway::Nalo
10
+ # Use Rails session for USSD (shorter sessions)
11
+ config.use_session_store FlowChat::Session::RailsSessionStore
12
+
13
+ # Enable resumable sessions (optional)
14
+ config.use_resumable_sessions
15
+ end
16
+
17
+ processor.run WelcomeFlow, :main_page
18
+ end
19
+ end
20
+
21
+ # Example Flow for USSD
22
+ # Add this to your Rails application as app/flow_chat/welcome_flow.rb
23
+
24
+ class WelcomeFlow < FlowChat::Flow
25
+ def main_page
26
+ # Welcome the user
27
+ name = app.screen(:name) do |prompt|
28
+ prompt.ask "Welcome to our service! What's your name?",
29
+ transform: ->(input) { input.strip.titleize }
30
+ end
31
+
32
+ # Show main menu with numbered options (USSD style)
33
+ choice = app.screen(:main_menu) do |prompt|
34
+ prompt.select "Hi #{name}! Choose an option:", {
35
+ "1" => "Account Info",
36
+ "2" => "Make Payment",
37
+ "3" => "Get Balance",
38
+ "4" => "Customer Support"
39
+ }
40
+ end
41
+
42
+ case choice
43
+ when "1"
44
+ show_account_info
45
+ when "2"
46
+ make_payment
47
+ when "3"
48
+ get_balance
49
+ when "4"
50
+ customer_support
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def show_account_info
57
+ info_choice = app.screen(:account_info) do |prompt|
58
+ prompt.select "Account Information:", {
59
+ "1" => "Personal Details",
60
+ "2" => "Account Balance",
61
+ "3" => "Transaction History",
62
+ "0" => "Back to Main Menu"
63
+ }
64
+ end
65
+
66
+ case info_choice
67
+ when "1"
68
+ app.say "Name: John Doe\\nPhone: #{app.phone_number}\\nAccount: Active"
69
+ when "2"
70
+ app.say "Current Balance: $150.75\\nAvailable Credit: $1,000.00"
71
+ when "3"
72
+ app.say "Last 3 Transactions:\\n1. +$50.00 - Deposit\\n2. -$25.50 - Purchase\\n3. -$15.00 - Transfer"
73
+ when "0"
74
+ main_page # Go back to main menu
75
+ end
76
+ end
77
+
78
+ def make_payment
79
+ amount = app.screen(:payment_amount) do |prompt|
80
+ prompt.ask "Enter amount to pay:",
81
+ convert: ->(input) { input.to_f },
82
+ validate: ->(amount) {
83
+ return "Amount must be greater than 0" unless amount > 0
84
+ return "Maximum payment is $500" unless amount <= 500
85
+ nil
86
+ }
87
+ end
88
+
89
+ recipient = app.screen(:payment_recipient) do |prompt|
90
+ prompt.ask "Enter recipient phone number:",
91
+ validate: ->(input) {
92
+ return "Phone number must be 10 digits" unless input.match?(/\\A\\d{10}\\z/)
93
+ nil
94
+ }
95
+ end
96
+
97
+ # Confirmation screen
98
+ confirmed = app.screen(:payment_confirmation) do |prompt|
99
+ prompt.yes? "Pay $#{amount} to #{recipient}?\\nConfirm payment?"
100
+ end
101
+
102
+ if confirmed
103
+ # Process payment (your business logic here)
104
+ transaction_id = process_payment(amount, recipient)
105
+ app.say "Payment successful!\\nTransaction ID: #{transaction_id}\\nAmount: $#{amount}\\nTo: #{recipient}"
106
+ else
107
+ app.say "Payment cancelled"
108
+ end
109
+ end
110
+
111
+ def get_balance
112
+ # Simulate balance check
113
+ balance = check_account_balance(app.phone_number)
114
+ app.say "Account Balance\\n\\nAvailable: $#{balance[:available]}\\nPending: $#{balance[:pending]}\\nTotal: $#{balance[:total]}"
115
+ end
116
+
117
+ def customer_support
118
+ support_choice = app.screen(:support_menu) do |prompt|
119
+ prompt.select "Customer Support:", {
120
+ "1" => "Report an Issue",
121
+ "2" => "Account Questions",
122
+ "3" => "Technical Support",
123
+ "4" => "Speak to Agent",
124
+ "0" => "Main Menu"
125
+ }
126
+ end
127
+
128
+ case support_choice
129
+ when "1"
130
+ report_issue
131
+ when "2"
132
+ app.say "For account questions:\\nCall: 123-456-7890\\nEmail: support@company.com\\nHours: 9AM-5PM Mon-Fri"
133
+ when "3"
134
+ app.say "Technical Support:\\nCall: 123-456-7891\\nEmail: tech@company.com\\n24/7 Support Available"
135
+ when "4"
136
+ app.say "Connecting you to an agent...\\nPlease call 123-456-7890\\nOr visit our nearest branch"
137
+ when "0"
138
+ main_page
139
+ end
140
+ end
141
+
142
+ def report_issue
143
+ issue_type = app.screen(:issue_type) do |prompt|
144
+ prompt.select "Select issue type:", {
145
+ "1" => "Payment Problem",
146
+ "2" => "Account Access",
147
+ "3" => "Service Error",
148
+ "4" => "Other"
149
+ }
150
+ end
151
+
152
+ description = app.screen(:issue_description) do |prompt|
153
+ prompt.ask "Briefly describe the issue:",
154
+ validate: ->(input) {
155
+ return "Description must be at least 10 characters" unless input.length >= 10
156
+ nil
157
+ }
158
+ end
159
+
160
+ # Save the issue (your business logic here)
161
+ ticket_id = create_support_ticket(issue_type, description, app.phone_number)
162
+
163
+ app.say "Issue reported successfully!\\n\\nTicket ID: #{ticket_id}\\nWe'll contact you within 24 hours.\\n\\nThank you!"
164
+ end
165
+
166
+ # Helper methods (implement your business logic)
167
+
168
+ def process_payment(amount, recipient)
169
+ # Your payment processing logic here
170
+ # Return transaction ID
171
+ "TXN#{rand(100000..999999)}"
172
+ end
173
+
174
+ def check_account_balance(phone_number)
175
+ # Your balance checking logic here
176
+ {
177
+ available: "150.75",
178
+ pending: "25.00",
179
+ total: "175.75"
180
+ }
181
+ end
182
+
183
+ def create_support_ticket(issue_type, description, phone_number)
184
+ # Your ticket creation logic here
185
+ Rails.logger.info "Support ticket created: #{issue_type} - #{description} from #{phone_number}"
186
+ "TICKET#{rand(10000..99999)}"
187
+ end
188
+ end
189
+
190
+ # Configuration Examples:
191
+
192
+ # 1. Basic configuration with custom pagination
193
+ class UssdController < ApplicationController
194
+ skip_forgery_protection
195
+
196
+ def process_request
197
+ # Configure pagination for shorter messages
198
+ FlowChat::Config.ussd.pagination_page_size = 120
199
+ FlowChat::Config.ussd.pagination_next_option = "#"
200
+ FlowChat::Config.ussd.pagination_back_option = "*"
201
+
202
+ processor = FlowChat::Ussd::Processor.new(self) do |config|
203
+ config.use_gateway FlowChat::Ussd::Gateway::Nalo
204
+ config.use_session_store FlowChat::Session::RailsSessionStore
205
+ end
206
+
207
+ processor.run WelcomeFlow, :main_page
208
+ end
209
+ end
210
+
211
+ # 2. Configuration with custom middleware
212
+ class LoggingMiddleware
213
+ def initialize(app)
214
+ @app = app
215
+ end
216
+
217
+ def call(context)
218
+ Rails.logger.info "USSD Request from #{context['request.msisdn']}: #{context.input}"
219
+ start_time = Time.current
220
+
221
+ result = @app.call(context)
222
+
223
+ duration = Time.current - start_time
224
+ Rails.logger.info "USSD Response (#{duration.round(3)}s): #{result[1]}"
225
+
226
+ result
227
+ end
228
+ end
229
+
230
+ class UssdController < ApplicationController
231
+ skip_forgery_protection
232
+
233
+ def process_request
234
+ processor = FlowChat::Ussd::Processor.new(self) do |config|
235
+ config.use_gateway FlowChat::Ussd::Gateway::Nalo
236
+ config.use_session_store FlowChat::Session::RailsSessionStore
237
+ config.use_middleware LoggingMiddleware # Add custom logging
238
+ config.use_resumable_sessions # Enable resumable sessions
239
+ end
240
+
241
+ processor.run WelcomeFlow, :main_page
242
+ end
243
+ end
244
+
245
+ # 3. Configuration with cache-based sessions for longer persistence
246
+ class UssdController < ApplicationController
247
+ skip_forgery_protection
248
+
249
+ def process_request
250
+ processor = FlowChat::Ussd::Processor.new(self) do |config|
251
+ config.use_gateway FlowChat::Ussd::Gateway::Nalo
252
+ # Use cache store for longer session persistence
253
+ config.use_session_store FlowChat::Session::CacheSessionStore
254
+ end
255
+
256
+ processor.run WelcomeFlow, :main_page
257
+ end
258
+ end
259
+
260
+ # Add this route to your config/routes.rb:
261
+ # post '/ussd', to: 'ussd#process_request'
262
+
263
+ # For Nsano gateway, use:
264
+ # config.use_gateway FlowChat::Ussd::Gateway::Nsano