flow_chat 0.4.1 → 0.5.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.
@@ -0,0 +1,95 @@
1
+ # Example Simulator Controller
2
+ # Add this to your Rails application as app/controllers/simulator_controller.rb
3
+
4
+ class SimulatorController < ApplicationController
5
+ include FlowChat::Simulator::Controller
6
+
7
+ def index
8
+ flowchat_simulator
9
+ end
10
+
11
+ protected
12
+
13
+ # Define different local endpoints to test on the same server
14
+ def configurations
15
+ {
16
+ ussd_main: {
17
+ name: "Main USSD Endpoint",
18
+ description: "Primary USSD integration endpoint",
19
+ processor_type: "ussd",
20
+ provider: "nalo",
21
+ endpoint: "/ussd",
22
+ icon: "📱",
23
+ color: "#28a745"
24
+ },
25
+ whatsapp_main: {
26
+ name: "Main WhatsApp Endpoint",
27
+ description: "Primary WhatsApp webhook endpoint",
28
+ processor_type: "whatsapp",
29
+ provider: "cloud_api",
30
+ endpoint: "/whatsapp/webhook",
31
+ icon: "💬",
32
+ color: "#25D366"
33
+ },
34
+ whatsapp_v2: {
35
+ name: "WhatsApp API v2",
36
+ description: "Alternative WhatsApp endpoint (v2 API)",
37
+ processor_type: "whatsapp",
38
+ provider: "cloud_api",
39
+ endpoint: "/api/v2/whatsapp/webhook",
40
+ icon: "🔄",
41
+ color: "#17a2b8"
42
+ },
43
+ whatsapp_tenant_a: {
44
+ name: "Tenant A WhatsApp",
45
+ description: "Multi-tenant WhatsApp endpoint for Tenant A",
46
+ processor_type: "whatsapp",
47
+ provider: "cloud_api",
48
+ endpoint: "/tenants/a/whatsapp/webhook",
49
+ icon: "🏢",
50
+ color: "#fd7e14"
51
+ },
52
+ whatsapp_legacy: {
53
+ name: "Legacy WhatsApp",
54
+ description: "Legacy WhatsApp endpoint for backward compatibility",
55
+ processor_type: "whatsapp",
56
+ provider: "cloud_api",
57
+ endpoint: "/legacy/whatsapp",
58
+ icon: "📦",
59
+ color: "#6c757d"
60
+ }
61
+ }
62
+ end
63
+
64
+ # Default configuration to start with
65
+ def default_config_key
66
+ :whatsapp_main
67
+ end
68
+
69
+ # Default test phone number
70
+ def default_phone_number
71
+ "+1234567890"
72
+ end
73
+
74
+ # Default test contact name
75
+ def default_contact_name
76
+ "Test User"
77
+ end
78
+ end
79
+
80
+ # Add this route to your config/routes.rb:
81
+ # get '/simulator' => 'simulator#index'
82
+
83
+ # Usage:
84
+ # 1. Start your Rails server: rails server
85
+ # 2. Visit http://localhost:3000/simulator
86
+ # 3. Select different endpoints from the dropdown to test
87
+ # 4. Send test messages to see how each endpoint responds
88
+ # 5. View request/response logs in real-time
89
+
90
+ # This allows you to test:
91
+ # - Different controller implementations on the same server
92
+ # - Different API versions (v1, v2, etc.)
93
+ # - Multi-tenant endpoints with different configurations
94
+ # - Legacy endpoints alongside new ones
95
+ # - Different flow implementations for different endpoints
@@ -9,7 +9,7 @@ class UssdController < ApplicationController
9
9
  config.use_gateway FlowChat::Ussd::Gateway::Nalo
10
10
  # Use Rails session for USSD (shorter sessions)
11
11
  config.use_session_store FlowChat::Session::RailsSessionStore
12
-
12
+
13
13
  # Enable resumable sessions (optional)
14
14
  config.use_resumable_sessions
15
15
  end
@@ -33,7 +33,7 @@ class WelcomeFlow < FlowChat::Flow
33
33
  choice = app.screen(:main_menu) do |prompt|
34
34
  prompt.select "Hi #{name}! Choose an option:", {
35
35
  "1" => "Account Info",
36
- "2" => "Make Payment",
36
+ "2" => "Make Payment",
37
37
  "3" => "Get Balance",
38
38
  "4" => "Customer Support"
39
39
  }
@@ -118,7 +118,7 @@ class WelcomeFlow < FlowChat::Flow
118
118
  support_choice = app.screen(:support_menu) do |prompt|
119
119
  prompt.select "Customer Support:", {
120
120
  "1" => "Report an Issue",
121
- "2" => "Account Questions",
121
+ "2" => "Account Questions",
122
122
  "3" => "Technical Support",
123
123
  "4" => "Speak to Agent",
124
124
  "0" => "Main Menu"
@@ -159,12 +159,12 @@ class WelcomeFlow < FlowChat::Flow
159
159
 
160
160
  # Save the issue (your business logic here)
161
161
  ticket_id = create_support_ticket(issue_type, description, app.phone_number)
162
-
162
+
163
163
  app.say "Issue reported successfully!\\n\\nTicket ID: #{ticket_id}\\nWe'll contact you within 24 hours.\\n\\nThank you!"
164
164
  end
165
165
 
166
166
  # Helper methods (implement your business logic)
167
-
167
+
168
168
  def process_payment(amount, recipient)
169
169
  # Your payment processing logic here
170
170
  # Return transaction ID
@@ -175,7 +175,7 @@ class WelcomeFlow < FlowChat::Flow
175
175
  # Your balance checking logic here
176
176
  {
177
177
  available: "150.75",
178
- pending: "25.00",
178
+ pending: "25.00",
179
179
  total: "175.75"
180
180
  }
181
181
  end
@@ -190,6 +190,7 @@ end
190
190
  # Configuration Examples:
191
191
 
192
192
  # 1. Basic configuration with custom pagination
193
+ # rubocop:disable Lint/DuplicateMethods
193
194
  class UssdController < ApplicationController
194
195
  skip_forgery_protection
195
196
 
@@ -207,6 +208,7 @@ class UssdController < ApplicationController
207
208
  processor.run WelcomeFlow, :main_page
208
209
  end
209
210
  end
211
+ # rubocop:enable Lint/DuplicateMethods
210
212
 
211
213
  # 2. Configuration with custom middleware
212
214
  class LoggingMiddleware
@@ -215,18 +217,19 @@ class LoggingMiddleware
215
217
  end
216
218
 
217
219
  def call(context)
218
- Rails.logger.info "USSD Request from #{context['request.msisdn']}: #{context.input}"
220
+ Rails.logger.info "USSD Request from #{context["request.msisdn"]}: #{context.input}"
219
221
  start_time = Time.current
220
-
222
+
221
223
  result = @app.call(context)
222
-
224
+
223
225
  duration = Time.current - start_time
224
226
  Rails.logger.info "USSD Response (#{duration.round(3)}s): #{result[1]}"
225
-
227
+
226
228
  result
227
229
  end
228
230
  end
229
231
 
232
+ # rubocop:disable Lint/DuplicateMethods
230
233
  class UssdController < ApplicationController
231
234
  skip_forgery_protection
232
235
 
@@ -241,8 +244,10 @@ class UssdController < ApplicationController
241
244
  processor.run WelcomeFlow, :main_page
242
245
  end
243
246
  end
247
+ # rubocop:enable Lint/DuplicateMethods
244
248
 
245
249
  # 3. Configuration with cache-based sessions for longer persistence
250
+ # rubocop:disable Lint/DuplicateMethods
246
251
  class UssdController < ApplicationController
247
252
  skip_forgery_protection
248
253
 
@@ -256,9 +261,10 @@ class UssdController < ApplicationController
256
261
  processor.run WelcomeFlow, :main_page
257
262
  end
258
263
  end
264
+ # rubocop:enable Lint/DuplicateMethods
259
265
 
260
266
  # Add this route to your config/routes.rb:
261
267
  # post '/ussd', to: 'ussd#process_request'
262
268
 
263
269
  # For Nsano gateway, use:
264
- # config.use_gateway FlowChat::Ussd::Gateway::Nsano
270
+ # config.use_gateway FlowChat::Ussd::Gateway::Nsano
@@ -5,36 +5,125 @@ class WhatsappController < ApplicationController
5
5
  skip_forgery_protection
6
6
 
7
7
  def webhook
8
- processor = FlowChat::Whatsapp::Processor.new(self) do |config|
8
+ # Enable simulator mode for local endpoint testing in development
9
+ processor = FlowChat::Whatsapp::Processor.new(self, enable_simulator: Rails.env.development?) do |config|
9
10
  config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi
10
11
  # Use cache-based session store for longer WhatsApp conversations
11
12
  config.use_session_store FlowChat::Session::CacheSessionStore
12
13
  end
13
14
 
14
15
  processor.run WelcomeFlow, :main_page
16
+ rescue FlowChat::Whatsapp::ConfigurationError => e
17
+ Rails.logger.error "WhatsApp configuration error: #{e.message}"
18
+ head :internal_server_error
19
+ rescue => e
20
+ Rails.logger.error "Unexpected error processing WhatsApp webhook: #{e.message}"
21
+ head :internal_server_error
15
22
  end
16
23
  end
17
24
 
18
- # Example with Custom Configuration
25
+ # Example with Custom Configuration and Security
19
26
  class CustomWhatsappController < ApplicationController
20
27
  skip_forgery_protection
21
28
 
22
29
  def webhook
23
30
  # Create custom WhatsApp configuration for this endpoint
24
31
  custom_config = FlowChat::Whatsapp::Configuration.new
25
- custom_config.access_token = ENV['MY_WHATSAPP_ACCESS_TOKEN']
26
- custom_config.phone_number_id = ENV['MY_WHATSAPP_PHONE_NUMBER_ID']
27
- custom_config.verify_token = ENV['MY_WHATSAPP_VERIFY_TOKEN']
28
- custom_config.app_id = ENV['MY_WHATSAPP_APP_ID']
29
- custom_config.app_secret = ENV['MY_WHATSAPP_APP_SECRET']
30
- custom_config.business_account_id = ENV['MY_WHATSAPP_BUSINESS_ACCOUNT_ID']
31
-
32
- processor = FlowChat::Whatsapp::Processor.new(self) do |config|
32
+ custom_config.access_token = ENV["MY_WHATSAPP_ACCESS_TOKEN"]
33
+ custom_config.phone_number_id = ENV["MY_WHATSAPP_PHONE_NUMBER_ID"]
34
+ custom_config.verify_token = ENV["MY_WHATSAPP_VERIFY_TOKEN"]
35
+ custom_config.app_id = ENV["MY_WHATSAPP_APP_ID"]
36
+ custom_config.app_secret = ENV["MY_WHATSAPP_APP_SECRET"]
37
+ custom_config.business_account_id = ENV["MY_WHATSAPP_BUSINESS_ACCOUNT_ID"]
38
+
39
+ # Security configuration
40
+ custom_config.skip_signature_validation = !Rails.env.production? # Only skip in non-production
41
+
42
+ # Enable simulator for local endpoint testing in non-production environments
43
+ processor = FlowChat::Whatsapp::Processor.new(self, enable_simulator: !Rails.env.production?) do |config|
33
44
  config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi, custom_config
34
45
  config.use_session_store FlowChat::Session::CacheSessionStore
35
46
  end
36
47
 
37
48
  processor.run WelcomeFlow, :main_page
49
+ rescue FlowChat::Whatsapp::ConfigurationError => e
50
+ Rails.logger.error "WhatsApp configuration error: #{e.message}"
51
+ head :internal_server_error
52
+ rescue => e
53
+ Rails.logger.error "Unexpected error processing WhatsApp webhook: #{e.message}"
54
+ head :internal_server_error
55
+ end
56
+ end
57
+
58
+ # Example with Environment-Specific Security
59
+ class EnvironmentAwareWhatsappController < ApplicationController
60
+ skip_forgery_protection
61
+
62
+ def webhook
63
+ # Configure security based on environment
64
+ custom_config = build_whatsapp_config
65
+
66
+ # Enable simulator for local endpoint testing in development/staging
67
+ enable_simulator = Rails.env.development? || Rails.env.staging?
68
+
69
+ processor = FlowChat::Whatsapp::Processor.new(self, enable_simulator: enable_simulator) do |config|
70
+ config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi, custom_config
71
+ config.use_session_store FlowChat::Session::CacheSessionStore
72
+ end
73
+
74
+ processor.run WelcomeFlow, :main_page
75
+ rescue FlowChat::Whatsapp::ConfigurationError => e
76
+ Rails.logger.error "WhatsApp configuration error: #{e.message}"
77
+ head :internal_server_error
78
+ rescue => e
79
+ Rails.logger.error "Unexpected error processing WhatsApp webhook: #{e.message}"
80
+ head :internal_server_error
81
+ end
82
+
83
+ private
84
+
85
+ def build_whatsapp_config
86
+ config = FlowChat::Whatsapp::Configuration.new
87
+
88
+ case Rails.env
89
+ when 'development'
90
+ # Development: More relaxed security for easier testing
91
+ config.access_token = ENV["WHATSAPP_ACCESS_TOKEN"]
92
+ config.phone_number_id = ENV["WHATSAPP_PHONE_NUMBER_ID"]
93
+ config.verify_token = ENV["WHATSAPP_VERIFY_TOKEN"]
94
+ config.app_id = ENV["WHATSAPP_APP_ID"]
95
+ config.app_secret = ENV["WHATSAPP_APP_SECRET"] # Optional in development
96
+ config.business_account_id = ENV["WHATSAPP_BUSINESS_ACCOUNT_ID"]
97
+ config.skip_signature_validation = true # Skip validation for easier development
98
+
99
+ when 'test'
100
+ # Test: Use test credentials
101
+ config.access_token = "test_token"
102
+ config.phone_number_id = "test_phone_id"
103
+ config.verify_token = "test_verify_token"
104
+ config.app_id = "test_app_id"
105
+ config.app_secret = "test_app_secret"
106
+ config.business_account_id = "test_business_id"
107
+ config.skip_signature_validation = true # Skip validation in tests
108
+
109
+ when 'staging', 'production'
110
+ # Production: Full security enabled
111
+ config.access_token = ENV["WHATSAPP_ACCESS_TOKEN"]
112
+ config.phone_number_id = ENV["WHATSAPP_PHONE_NUMBER_ID"]
113
+ config.verify_token = ENV["WHATSAPP_VERIFY_TOKEN"]
114
+ config.app_id = ENV["WHATSAPP_APP_ID"]
115
+ config.app_secret = ENV["WHATSAPP_APP_SECRET"] # Required for security
116
+ config.business_account_id = ENV["WHATSAPP_BUSINESS_ACCOUNT_ID"]
117
+ config.skip_signature_validation = false # Always validate in production
118
+
119
+ # Ensure required security configuration is present
120
+ if config.app_secret.blank?
121
+ raise FlowChat::Whatsapp::ConfigurationError,
122
+ "WHATSAPP_APP_SECRET is required for webhook signature validation in #{Rails.env}"
123
+ end
124
+ end
125
+
126
+ config
38
127
  end
39
128
  end
40
129
 
@@ -53,7 +142,7 @@ class WelcomeFlow < FlowChat::Flow
53
142
  choice = app.screen(:main_menu) do |prompt|
54
143
  prompt.select "Hi #{name}! What can I help you with today?", {
55
144
  "info" => "📋 Get Information",
56
- "support" => "🆘 Contact Support",
145
+ "support" => "🆘 Contact Support",
57
146
  "feedback" => "💬 Give Feedback"
58
147
  }
59
148
  end
@@ -94,7 +183,7 @@ class WelcomeFlow < FlowChat::Flow
94
183
  contact_method = app.screen(:contact_method) do |prompt|
95
184
  prompt.select "How would you like to contact support?", {
96
185
  "call" => "📞 Call Us",
97
- "email" => "📧 Email Us",
186
+ "email" => "📧 Email Us",
98
187
  "chat" => "💬 Live Chat"
99
188
  }
100
189
  end
@@ -113,7 +202,7 @@ class WelcomeFlow < FlowChat::Flow
113
202
  rating = app.screen(:rating) do |prompt|
114
203
  prompt.select "How would you rate our service?", {
115
204
  "5" => "⭐⭐⭐⭐⭐ Excellent",
116
- "4" => "⭐⭐⭐⭐ Good",
205
+ "4" => "⭐⭐⭐⭐ Good",
117
206
  "3" => "⭐⭐⭐ Average",
118
207
  "2" => "⭐⭐ Poor",
119
208
  "1" => "⭐ Very Poor"
@@ -137,4 +226,4 @@ class WelcomeFlow < FlowChat::Flow
137
226
  end
138
227
 
139
228
  # Add this route to your config/routes.rb:
140
- # post '/whatsapp/webhook', to: 'whatsapp#webhook'
229
+ # post '/whatsapp/webhook', to: 'whatsapp#webhook'