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.
- checksums.yaml +4 -4
- data/README.md +418 -295
- data/SECURITY.md +365 -0
- data/examples/initializer.rb +56 -1
- data/examples/media_prompts_examples.rb +1 -2
- data/examples/multi_tenant_whatsapp_controller.rb +61 -57
- data/examples/simulator_controller.rb +95 -0
- data/examples/ussd_controller.rb +17 -11
- data/examples/whatsapp_controller.rb +103 -14
- data/examples/whatsapp_media_examples.rb +78 -80
- data/examples/whatsapp_message_job.rb +3 -3
- data/lib/flow_chat/base_processor.rb +3 -2
- data/lib/flow_chat/config.rb +6 -3
- data/lib/flow_chat/session/cache_session_store.rb +5 -5
- data/lib/flow_chat/simulator/controller.rb +34 -5
- data/lib/flow_chat/simulator/views/simulator.html.erb +287 -12
- data/lib/flow_chat/ussd/gateway/nsano.rb +1 -1
- data/lib/flow_chat/ussd/processor.rb +1 -1
- data/lib/flow_chat/ussd/prompt.rb +13 -13
- data/lib/flow_chat/version.rb +1 -1
- data/lib/flow_chat/whatsapp/app.rb +1 -1
- data/lib/flow_chat/whatsapp/client.rb +44 -50
- data/lib/flow_chat/whatsapp/configuration.rb +21 -20
- data/lib/flow_chat/whatsapp/gateway/cloud_api.rb +129 -19
- data/lib/flow_chat/whatsapp/middleware/executor.rb +1 -1
- data/lib/flow_chat/whatsapp/processor.rb +1 -1
- data/lib/flow_chat/whatsapp/prompt.rb +27 -31
- data/lib/flow_chat/whatsapp/send_job_support.rb +7 -7
- data/lib/flow_chat/whatsapp/template_manager.rb +10 -10
- metadata +4 -2
@@ -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
|
data/examples/ussd_controller.rb
CHANGED
@@ -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[
|
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
|
-
|
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[
|
26
|
-
custom_config.phone_number_id = ENV[
|
27
|
-
custom_config.verify_token = ENV[
|
28
|
-
custom_config.app_id = ENV[
|
29
|
-
custom_config.app_secret = ENV[
|
30
|
-
custom_config.business_account_id = ENV[
|
31
|
-
|
32
|
-
|
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'
|