flow_chat 0.2.1 ā 0.4.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/.ruby-version +1 -0
- data/Gemfile +3 -1
- data/README.md +784 -63
- data/Rakefile +7 -3
- data/examples/initializer.rb +31 -0
- data/examples/multi_tenant_whatsapp_controller.rb +248 -0
- data/examples/ussd_controller.rb +264 -0
- data/examples/whatsapp_controller.rb +141 -0
- data/lib/flow_chat/base_processor.rb +63 -0
- data/lib/flow_chat/config.rb +21 -8
- data/lib/flow_chat/context.rb +8 -0
- data/lib/flow_chat/interrupt.rb +2 -0
- data/lib/flow_chat/session/cache_session_store.rb +84 -0
- data/lib/flow_chat/session/middleware.rb +15 -7
- data/lib/flow_chat/ussd/app.rb +25 -0
- data/lib/flow_chat/ussd/gateway/nalo.rb +3 -1
- data/lib/flow_chat/ussd/gateway/nsano.rb +7 -1
- data/lib/flow_chat/ussd/middleware/pagination.rb +14 -17
- data/lib/flow_chat/ussd/middleware/resumable_session.rb +18 -31
- data/lib/flow_chat/ussd/processor.rb +15 -42
- data/lib/flow_chat/ussd/prompt.rb +1 -1
- data/lib/flow_chat/ussd/simulator/controller.rb +1 -1
- data/lib/flow_chat/version.rb +1 -1
- data/lib/flow_chat/whatsapp/app.rb +58 -0
- data/lib/flow_chat/whatsapp/configuration.rb +75 -0
- data/lib/flow_chat/whatsapp/gateway/cloud_api.rb +213 -0
- data/lib/flow_chat/whatsapp/middleware/executor.rb +30 -0
- data/lib/flow_chat/whatsapp/processor.rb +36 -0
- data/lib/flow_chat/whatsapp/prompt.rb +206 -0
- data/lib/flow_chat/whatsapp/template_manager.rb +162 -0
- data/lib/flow_chat.rb +1 -0
- metadata +17 -4
- data/.rspec +0 -3
data/Rakefile
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
|
-
require "
|
2
|
+
require "rake/testtask"
|
3
3
|
|
4
|
-
|
4
|
+
Rake::TestTask.new(:test) do |t|
|
5
|
+
t.libs << "test"
|
6
|
+
t.libs << "lib"
|
7
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
8
|
+
end
|
5
9
|
|
6
|
-
task default: :
|
10
|
+
task default: :test
|
@@ -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,248 @@
|
|
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_whatsapp_config(whatsapp_config)
|
16
|
+
config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi
|
17
|
+
config.use_session_store FlowChat::Session::CacheSessionStore
|
18
|
+
end
|
19
|
+
|
20
|
+
# Use tenant-specific flow
|
21
|
+
flow_class = get_flow_for_tenant(tenant)
|
22
|
+
processor.run flow_class, :main_page
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def determine_tenant(request)
|
28
|
+
# Option 1: From subdomain
|
29
|
+
return request.subdomain if request.subdomain.present?
|
30
|
+
|
31
|
+
# Option 2: From path
|
32
|
+
tenant_from_path = request.path.match(%r{^/whatsapp/(\w+)/})&.captures&.first
|
33
|
+
return tenant_from_path if tenant_from_path
|
34
|
+
|
35
|
+
# Option 3: From custom header
|
36
|
+
return request.headers['X-Tenant-ID'] if request.headers['X-Tenant-ID']
|
37
|
+
|
38
|
+
# Fallback to default
|
39
|
+
'default'
|
40
|
+
end
|
41
|
+
|
42
|
+
def get_whatsapp_config_for_tenant(tenant)
|
43
|
+
case tenant
|
44
|
+
when 'acme_corp'
|
45
|
+
FlowChat::Whatsapp::Configuration.new.tap do |config|
|
46
|
+
config.access_token = ENV['ACME_WHATSAPP_ACCESS_TOKEN']
|
47
|
+
config.phone_number_id = ENV['ACME_WHATSAPP_PHONE_NUMBER_ID']
|
48
|
+
config.verify_token = ENV['ACME_WHATSAPP_VERIFY_TOKEN']
|
49
|
+
config.app_id = ENV['ACME_WHATSAPP_APP_ID']
|
50
|
+
config.app_secret = ENV['ACME_WHATSAPP_APP_SECRET']
|
51
|
+
config.business_account_id = ENV['ACME_WHATSAPP_BUSINESS_ACCOUNT_ID']
|
52
|
+
end
|
53
|
+
|
54
|
+
when 'tech_startup'
|
55
|
+
FlowChat::Whatsapp::Configuration.new.tap do |config|
|
56
|
+
config.access_token = ENV['TECHSTARTUP_WHATSAPP_ACCESS_TOKEN']
|
57
|
+
config.phone_number_id = ENV['TECHSTARTUP_WHATSAPP_PHONE_NUMBER_ID']
|
58
|
+
config.verify_token = ENV['TECHSTARTUP_WHATSAPP_VERIFY_TOKEN']
|
59
|
+
config.app_id = ENV['TECHSTARTUP_WHATSAPP_APP_ID']
|
60
|
+
config.app_secret = ENV['TECHSTARTUP_WHATSAPP_APP_SECRET']
|
61
|
+
config.business_account_id = ENV['TECHSTARTUP_WHATSAPP_BUSINESS_ACCOUNT_ID']
|
62
|
+
end
|
63
|
+
|
64
|
+
when 'retail_store'
|
65
|
+
# Load from database
|
66
|
+
tenant_config = WhatsappConfiguration.find_by(tenant: tenant)
|
67
|
+
FlowChat::Whatsapp::Configuration.new.tap do |config|
|
68
|
+
config.access_token = tenant_config.access_token
|
69
|
+
config.phone_number_id = tenant_config.phone_number_id
|
70
|
+
config.verify_token = tenant_config.verify_token
|
71
|
+
config.app_id = tenant_config.app_id
|
72
|
+
config.app_secret = tenant_config.app_secret
|
73
|
+
config.business_account_id = tenant_config.business_account_id
|
74
|
+
end
|
75
|
+
|
76
|
+
else
|
77
|
+
# Use default/global configuration
|
78
|
+
FlowChat::Whatsapp::Configuration.from_credentials
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def get_flow_for_tenant(tenant)
|
83
|
+
case tenant
|
84
|
+
when 'acme_corp'
|
85
|
+
AcmeCorpFlow
|
86
|
+
when 'tech_startup'
|
87
|
+
TechStartupFlow
|
88
|
+
when 'retail_store'
|
89
|
+
RetailStoreFlow
|
90
|
+
else
|
91
|
+
WelcomeFlow # Default flow
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Example: Dynamic Configuration from Database
|
97
|
+
class DatabaseWhatsappController < ApplicationController
|
98
|
+
skip_forgery_protection
|
99
|
+
|
100
|
+
def webhook
|
101
|
+
# Get account from business phone number or other identifier
|
102
|
+
business_account = find_business_account(params)
|
103
|
+
|
104
|
+
if business_account.nil?
|
105
|
+
return head :not_found
|
106
|
+
end
|
107
|
+
|
108
|
+
# Create configuration from database record
|
109
|
+
whatsapp_config = FlowChat::Whatsapp::Configuration.new.tap do |config|
|
110
|
+
config.access_token = business_account.whatsapp_access_token
|
111
|
+
config.phone_number_id = business_account.whatsapp_phone_number_id
|
112
|
+
config.verify_token = business_account.whatsapp_verify_token
|
113
|
+
config.app_id = business_account.whatsapp_app_id
|
114
|
+
config.app_secret = business_account.whatsapp_app_secret
|
115
|
+
config.business_account_id = business_account.whatsapp_business_account_id
|
116
|
+
end
|
117
|
+
|
118
|
+
processor = FlowChat::Whatsapp::Processor.new(self) do |config|
|
119
|
+
config.use_whatsapp_config(whatsapp_config)
|
120
|
+
config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi
|
121
|
+
config.use_session_store FlowChat::Session::CacheSessionStore
|
122
|
+
end
|
123
|
+
|
124
|
+
processor.run business_account.flow_class.constantize, :main_page
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
def find_business_account(params)
|
130
|
+
# You could identify the account by:
|
131
|
+
# 1. Phone number ID from webhook
|
132
|
+
# 2. Business account ID from webhook
|
133
|
+
# 3. Custom routing parameter
|
134
|
+
|
135
|
+
# Example: Find by phone number ID in webhook
|
136
|
+
phone_number_id = extract_phone_number_id_from_webhook(params)
|
137
|
+
BusinessAccount.find_by(whatsapp_phone_number_id: phone_number_id)
|
138
|
+
end
|
139
|
+
|
140
|
+
def extract_phone_number_id_from_webhook(params)
|
141
|
+
# Extract from webhook payload structure
|
142
|
+
# This would need to be implemented based on your webhook structure
|
143
|
+
nil
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Example: Environment-based Configuration
|
148
|
+
class EnvironmentWhatsappController < ApplicationController
|
149
|
+
skip_forgery_protection
|
150
|
+
|
151
|
+
def webhook
|
152
|
+
# Different configurations for different environments
|
153
|
+
whatsapp_config = case Rails.env
|
154
|
+
when 'production'
|
155
|
+
production_whatsapp_config
|
156
|
+
when 'staging'
|
157
|
+
staging_whatsapp_config
|
158
|
+
when 'development'
|
159
|
+
development_whatsapp_config
|
160
|
+
else
|
161
|
+
FlowChat::Whatsapp::Configuration.from_credentials
|
162
|
+
end
|
163
|
+
|
164
|
+
processor = FlowChat::Whatsapp::Processor.new(self) do |config|
|
165
|
+
config.use_whatsapp_config(whatsapp_config)
|
166
|
+
config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi
|
167
|
+
config.use_session_store FlowChat::Session::CacheSessionStore
|
168
|
+
end
|
169
|
+
|
170
|
+
processor.run WelcomeFlow, :main_page
|
171
|
+
end
|
172
|
+
|
173
|
+
private
|
174
|
+
|
175
|
+
def production_whatsapp_config
|
176
|
+
FlowChat::Whatsapp::Configuration.new.tap do |config|
|
177
|
+
config.access_token = ENV['PROD_WHATSAPP_ACCESS_TOKEN']
|
178
|
+
config.phone_number_id = ENV['PROD_WHATSAPP_PHONE_NUMBER_ID']
|
179
|
+
config.verify_token = ENV['PROD_WHATSAPP_VERIFY_TOKEN']
|
180
|
+
config.app_id = ENV['PROD_WHATSAPP_APP_ID']
|
181
|
+
config.app_secret = ENV['PROD_WHATSAPP_APP_SECRET']
|
182
|
+
config.business_account_id = ENV['PROD_WHATSAPP_BUSINESS_ACCOUNT_ID']
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def staging_whatsapp_config
|
187
|
+
FlowChat::Whatsapp::Configuration.new.tap do |config|
|
188
|
+
config.access_token = ENV['STAGING_WHATSAPP_ACCESS_TOKEN']
|
189
|
+
config.phone_number_id = ENV['STAGING_WHATSAPP_PHONE_NUMBER_ID']
|
190
|
+
config.verify_token = ENV['STAGING_WHATSAPP_VERIFY_TOKEN']
|
191
|
+
config.app_id = ENV['STAGING_WHATSAPP_APP_ID']
|
192
|
+
config.app_secret = ENV['STAGING_WHATSAPP_APP_SECRET']
|
193
|
+
config.business_account_id = ENV['STAGING_WHATSAPP_BUSINESS_ACCOUNT_ID']
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def development_whatsapp_config
|
198
|
+
FlowChat::Whatsapp::Configuration.new.tap do |config|
|
199
|
+
config.access_token = ENV['DEV_WHATSAPP_ACCESS_TOKEN']
|
200
|
+
config.phone_number_id = ENV['DEV_WHATSAPP_PHONE_NUMBER_ID']
|
201
|
+
config.verify_token = ENV['DEV_WHATSAPP_VERIFY_TOKEN']
|
202
|
+
config.app_id = ENV['DEV_WHATSAPP_APP_ID']
|
203
|
+
config.app_secret = ENV['DEV_WHATSAPP_APP_SECRET']
|
204
|
+
config.business_account_id = ENV['DEV_WHATSAPP_BUSINESS_ACCOUNT_ID']
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# Example: Simple Custom Configuration
|
210
|
+
class CustomWhatsappController < ApplicationController
|
211
|
+
skip_forgery_protection
|
212
|
+
|
213
|
+
def webhook
|
214
|
+
# Create custom configuration for this specific endpoint
|
215
|
+
my_config = FlowChat::Whatsapp::Configuration.new
|
216
|
+
my_config.access_token = "EAABs..." # Your specific access token
|
217
|
+
my_config.phone_number_id = "123456789"
|
218
|
+
my_config.verify_token = "my_verify_token"
|
219
|
+
my_config.app_id = "your_app_id"
|
220
|
+
my_config.app_secret = "your_app_secret"
|
221
|
+
my_config.business_account_id = "your_business_account_id"
|
222
|
+
|
223
|
+
processor = FlowChat::Whatsapp::Processor.new(self) do |config|
|
224
|
+
config.use_whatsapp_config(my_config)
|
225
|
+
config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi
|
226
|
+
config.use_session_store FlowChat::Session::CacheSessionStore
|
227
|
+
end
|
228
|
+
|
229
|
+
processor.run CustomFlow, :main_page
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
# Add routes for different tenants:
|
234
|
+
# Rails.application.routes.draw do
|
235
|
+
# # Subdomain-based routing
|
236
|
+
# constraints subdomain: /\w+/ do
|
237
|
+
# post '/whatsapp/webhook', to: 'multi_tenant_whatsapp#webhook'
|
238
|
+
# end
|
239
|
+
#
|
240
|
+
# # Path-based routing
|
241
|
+
# post '/whatsapp/:tenant/webhook', to: 'multi_tenant_whatsapp#webhook'
|
242
|
+
#
|
243
|
+
# # Environment-specific
|
244
|
+
# post '/whatsapp/env/webhook', to: 'environment_whatsapp#webhook'
|
245
|
+
#
|
246
|
+
# # Custom endpoint
|
247
|
+
# post '/whatsapp/custom/webhook', to: 'custom_whatsapp#webhook'
|
248
|
+
# 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
|
@@ -0,0 +1,141 @@
|
|
1
|
+
# Example WhatsApp Controller
|
2
|
+
# Add this to your Rails application as app/controllers/whatsapp_controller.rb
|
3
|
+
|
4
|
+
class WhatsappController < ApplicationController
|
5
|
+
skip_forgery_protection
|
6
|
+
|
7
|
+
def webhook
|
8
|
+
processor = FlowChat::Whatsapp::Processor.new(self) do |config|
|
9
|
+
config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi
|
10
|
+
# Use cache-based session store for longer WhatsApp conversations
|
11
|
+
config.use_session_store FlowChat::Session::CacheSessionStore
|
12
|
+
end
|
13
|
+
|
14
|
+
processor.run WelcomeFlow, :main_page
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Example with Custom Configuration
|
19
|
+
class CustomWhatsappController < ApplicationController
|
20
|
+
skip_forgery_protection
|
21
|
+
|
22
|
+
def webhook
|
23
|
+
# Create custom WhatsApp configuration for this endpoint
|
24
|
+
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|
|
33
|
+
config.use_whatsapp_config(custom_config) # Use custom config
|
34
|
+
config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi
|
35
|
+
config.use_session_store FlowChat::Session::CacheSessionStore
|
36
|
+
end
|
37
|
+
|
38
|
+
processor.run WelcomeFlow, :main_page
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Example Flow for WhatsApp
|
43
|
+
# Add this to your Rails application as app/flow_chat/welcome_flow.rb
|
44
|
+
|
45
|
+
class WelcomeFlow < FlowChat::Flow
|
46
|
+
def main_page
|
47
|
+
# Welcome the user
|
48
|
+
name = app.screen(:name) do |prompt|
|
49
|
+
prompt.ask "Hello! Welcome to our WhatsApp service. What's your name?",
|
50
|
+
transform: ->(input) { input.strip.titleize }
|
51
|
+
end
|
52
|
+
|
53
|
+
# Show main menu
|
54
|
+
choice = app.screen(:main_menu) do |prompt|
|
55
|
+
prompt.select "Hi #{name}! What can I help you with today?", {
|
56
|
+
"info" => "š Get Information",
|
57
|
+
"support" => "š Contact Support",
|
58
|
+
"feedback" => "š¬ Give Feedback"
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
case choice
|
63
|
+
when "info"
|
64
|
+
show_information_menu
|
65
|
+
when "support"
|
66
|
+
contact_support
|
67
|
+
when "feedback"
|
68
|
+
collect_feedback
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def show_information_menu
|
75
|
+
info_choice = app.screen(:info_menu) do |prompt|
|
76
|
+
prompt.select "What information do you need?", {
|
77
|
+
"hours" => "š Business Hours",
|
78
|
+
"location" => "š Our Location",
|
79
|
+
"services" => "š Our Services"
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
case info_choice
|
84
|
+
when "hours"
|
85
|
+
app.say "We're open Monday-Friday 9AM-6PM, Saturday 9AM-2PM. Closed Sundays."
|
86
|
+
when "location"
|
87
|
+
app.say "š We're located at 123 Main Street, City, State 12345"
|
88
|
+
when "services"
|
89
|
+
app.say "Here are our main services:\\n\\nš Web Development - Custom websites and applications\\nš± Mobile Apps - iOS and Android development\\nš§ Consulting - Technical consulting services"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def contact_support
|
94
|
+
# Use standard select menu instead of send_buttons
|
95
|
+
contact_method = app.screen(:contact_method) do |prompt|
|
96
|
+
prompt.select "How would you like to contact support?", {
|
97
|
+
"call" => "š Call Us",
|
98
|
+
"email" => "š§ Email Us",
|
99
|
+
"chat" => "š¬ Live Chat"
|
100
|
+
}
|
101
|
+
end
|
102
|
+
|
103
|
+
case contact_method
|
104
|
+
when "call"
|
105
|
+
app.say "š You can call us at (555) 123-4567"
|
106
|
+
when "email"
|
107
|
+
app.say "š§ Send us an email at support@example.com"
|
108
|
+
when "chat"
|
109
|
+
app.say "š¬ Our live chat is available on our website: www.example.com"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def collect_feedback
|
114
|
+
rating = app.screen(:rating) do |prompt|
|
115
|
+
prompt.select "How would you rate our service?", {
|
116
|
+
"5" => "āāāāā Excellent",
|
117
|
+
"4" => "āāāā Good",
|
118
|
+
"3" => "āāā Average",
|
119
|
+
"2" => "āā Poor",
|
120
|
+
"1" => "ā Very Poor"
|
121
|
+
}
|
122
|
+
end
|
123
|
+
|
124
|
+
feedback = app.screen(:feedback_text) do |prompt|
|
125
|
+
prompt.ask "Thank you for the #{rating}-star rating! Please share any additional feedback:"
|
126
|
+
end
|
127
|
+
|
128
|
+
# Save feedback (implement your logic here)
|
129
|
+
save_feedback(app.phone_number, rating, feedback)
|
130
|
+
|
131
|
+
app.say "Thank you for your feedback! We really appreciate it. š"
|
132
|
+
end
|
133
|
+
|
134
|
+
def save_feedback(phone, rating, feedback)
|
135
|
+
# Implement your feedback saving logic here
|
136
|
+
Rails.logger.info "Feedback from #{phone}: #{rating} stars - #{feedback}"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Add this route to your config/routes.rb:
|
141
|
+
# post '/whatsapp/webhook', to: 'whatsapp#webhook'
|