flow_chat 0.4.0 → 0.4.2
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/Gemfile +1 -0
- data/README.md +408 -102
- data/examples/initializer.rb +1 -1
- data/examples/media_prompts_examples.rb +27 -0
- data/examples/multi_tenant_whatsapp_controller.rb +60 -64
- data/examples/ussd_controller.rb +17 -11
- data/examples/whatsapp_controller.rb +11 -12
- data/examples/whatsapp_media_examples.rb +404 -0
- data/examples/whatsapp_message_job.rb +111 -0
- data/lib/flow_chat/base_processor.rb +8 -4
- data/lib/flow_chat/config.rb +37 -0
- data/lib/flow_chat/session/cache_session_store.rb +5 -5
- data/lib/flow_chat/simulator/controller.rb +78 -0
- data/lib/flow_chat/simulator/views/simulator.html.erb +1982 -0
- data/lib/flow_chat/ussd/gateway/nsano.rb +1 -1
- data/lib/flow_chat/ussd/processor.rb +1 -2
- data/lib/flow_chat/ussd/prompt.rb +39 -5
- data/lib/flow_chat/version.rb +1 -1
- data/lib/flow_chat/whatsapp/app.rb +8 -2
- data/lib/flow_chat/whatsapp/client.rb +435 -0
- data/lib/flow_chat/whatsapp/configuration.rb +50 -12
- data/lib/flow_chat/whatsapp/gateway/cloud_api.rb +113 -115
- data/lib/flow_chat/whatsapp/middleware/executor.rb +1 -1
- data/lib/flow_chat/whatsapp/processor.rb +1 -11
- data/lib/flow_chat/whatsapp/prompt.rb +125 -84
- data/lib/flow_chat/whatsapp/send_job_support.rb +79 -0
- data/lib/flow_chat/whatsapp/template_manager.rb +7 -7
- metadata +8 -3
- data/lib/flow_chat/ussd/simulator/controller.rb +0 -51
- data/lib/flow_chat/ussd/simulator/views/simulator.html.erb +0 -239
@@ -7,13 +7,12 @@ class MultiTenantWhatsappController < ApplicationController
|
|
7
7
|
def webhook
|
8
8
|
# Determine tenant from subdomain, path, or other logic
|
9
9
|
tenant = determine_tenant(request)
|
10
|
-
|
10
|
+
|
11
11
|
# Get tenant-specific WhatsApp configuration
|
12
12
|
whatsapp_config = get_whatsapp_config_for_tenant(tenant)
|
13
|
-
|
13
|
+
|
14
14
|
processor = FlowChat::Whatsapp::Processor.new(self) do |config|
|
15
|
-
config.
|
16
|
-
config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi
|
15
|
+
config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi, whatsapp_config
|
17
16
|
config.use_session_store FlowChat::Session::CacheSessionStore
|
18
17
|
end
|
19
18
|
|
@@ -27,41 +26,41 @@ class MultiTenantWhatsappController < ApplicationController
|
|
27
26
|
def determine_tenant(request)
|
28
27
|
# Option 1: From subdomain
|
29
28
|
return request.subdomain if request.subdomain.present?
|
30
|
-
|
29
|
+
|
31
30
|
# Option 2: From path
|
32
31
|
tenant_from_path = request.path.match(%r{^/whatsapp/(\w+)/})&.captures&.first
|
33
32
|
return tenant_from_path if tenant_from_path
|
34
|
-
|
33
|
+
|
35
34
|
# Option 3: From custom header
|
36
|
-
return request.headers[
|
37
|
-
|
35
|
+
return request.headers["X-Tenant-ID"] if request.headers["X-Tenant-ID"]
|
36
|
+
|
38
37
|
# Fallback to default
|
39
|
-
|
38
|
+
"default"
|
40
39
|
end
|
41
40
|
|
42
41
|
def get_whatsapp_config_for_tenant(tenant)
|
43
42
|
case tenant
|
44
|
-
when
|
43
|
+
when "acme_corp"
|
45
44
|
FlowChat::Whatsapp::Configuration.new.tap do |config|
|
46
|
-
config.access_token = ENV[
|
47
|
-
config.phone_number_id = ENV[
|
48
|
-
config.verify_token = ENV[
|
49
|
-
config.app_id = ENV[
|
50
|
-
config.app_secret = ENV[
|
51
|
-
config.business_account_id = ENV[
|
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"]
|
52
51
|
end
|
53
|
-
|
54
|
-
when
|
52
|
+
|
53
|
+
when "tech_startup"
|
55
54
|
FlowChat::Whatsapp::Configuration.new.tap do |config|
|
56
|
-
config.access_token = ENV[
|
57
|
-
config.phone_number_id = ENV[
|
58
|
-
config.verify_token = ENV[
|
59
|
-
config.app_id = ENV[
|
60
|
-
config.app_secret = ENV[
|
61
|
-
config.business_account_id = ENV[
|
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"]
|
62
61
|
end
|
63
|
-
|
64
|
-
when
|
62
|
+
|
63
|
+
when "retail_store"
|
65
64
|
# Load from database
|
66
65
|
tenant_config = WhatsappConfiguration.find_by(tenant: tenant)
|
67
66
|
FlowChat::Whatsapp::Configuration.new.tap do |config|
|
@@ -72,7 +71,7 @@ class MultiTenantWhatsappController < ApplicationController
|
|
72
71
|
config.app_secret = tenant_config.app_secret
|
73
72
|
config.business_account_id = tenant_config.business_account_id
|
74
73
|
end
|
75
|
-
|
74
|
+
|
76
75
|
else
|
77
76
|
# Use default/global configuration
|
78
77
|
FlowChat::Whatsapp::Configuration.from_credentials
|
@@ -81,11 +80,11 @@ class MultiTenantWhatsappController < ApplicationController
|
|
81
80
|
|
82
81
|
def get_flow_for_tenant(tenant)
|
83
82
|
case tenant
|
84
|
-
when
|
83
|
+
when "acme_corp"
|
85
84
|
AcmeCorpFlow
|
86
|
-
when
|
85
|
+
when "tech_startup"
|
87
86
|
TechStartupFlow
|
88
|
-
when
|
87
|
+
when "retail_store"
|
89
88
|
RetailStoreFlow
|
90
89
|
else
|
91
90
|
WelcomeFlow # Default flow
|
@@ -100,7 +99,7 @@ class DatabaseWhatsappController < ApplicationController
|
|
100
99
|
def webhook
|
101
100
|
# Get account from business phone number or other identifier
|
102
101
|
business_account = find_business_account(params)
|
103
|
-
|
102
|
+
|
104
103
|
if business_account.nil?
|
105
104
|
return head :not_found
|
106
105
|
end
|
@@ -116,8 +115,7 @@ class DatabaseWhatsappController < ApplicationController
|
|
116
115
|
end
|
117
116
|
|
118
117
|
processor = FlowChat::Whatsapp::Processor.new(self) do |config|
|
119
|
-
config.
|
120
|
-
config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi
|
118
|
+
config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi, whatsapp_config
|
121
119
|
config.use_session_store FlowChat::Session::CacheSessionStore
|
122
120
|
end
|
123
121
|
|
@@ -131,7 +129,7 @@ class DatabaseWhatsappController < ApplicationController
|
|
131
129
|
# 1. Phone number ID from webhook
|
132
130
|
# 2. Business account ID from webhook
|
133
131
|
# 3. Custom routing parameter
|
134
|
-
|
132
|
+
|
135
133
|
# Example: Find by phone number ID in webhook
|
136
134
|
phone_number_id = extract_phone_number_id_from_webhook(params)
|
137
135
|
BusinessAccount.find_by(whatsapp_phone_number_id: phone_number_id)
|
@@ -151,19 +149,18 @@ class EnvironmentWhatsappController < ApplicationController
|
|
151
149
|
def webhook
|
152
150
|
# Different configurations for different environments
|
153
151
|
whatsapp_config = case Rails.env
|
154
|
-
when
|
152
|
+
when "production"
|
155
153
|
production_whatsapp_config
|
156
|
-
when
|
154
|
+
when "staging"
|
157
155
|
staging_whatsapp_config
|
158
|
-
when
|
156
|
+
when "development"
|
159
157
|
development_whatsapp_config
|
160
158
|
else
|
161
159
|
FlowChat::Whatsapp::Configuration.from_credentials
|
162
160
|
end
|
163
161
|
|
164
162
|
processor = FlowChat::Whatsapp::Processor.new(self) do |config|
|
165
|
-
config.
|
166
|
-
config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi
|
163
|
+
config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi, whatsapp_config
|
167
164
|
config.use_session_store FlowChat::Session::CacheSessionStore
|
168
165
|
end
|
169
166
|
|
@@ -174,34 +171,34 @@ class EnvironmentWhatsappController < ApplicationController
|
|
174
171
|
|
175
172
|
def production_whatsapp_config
|
176
173
|
FlowChat::Whatsapp::Configuration.new.tap do |config|
|
177
|
-
config.access_token = ENV[
|
178
|
-
config.phone_number_id = ENV[
|
179
|
-
config.verify_token = ENV[
|
180
|
-
config.app_id = ENV[
|
181
|
-
config.app_secret = ENV[
|
182
|
-
config.business_account_id = ENV[
|
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"]
|
183
180
|
end
|
184
181
|
end
|
185
182
|
|
186
183
|
def staging_whatsapp_config
|
187
184
|
FlowChat::Whatsapp::Configuration.new.tap do |config|
|
188
|
-
config.access_token = ENV[
|
189
|
-
config.phone_number_id = ENV[
|
190
|
-
config.verify_token = ENV[
|
191
|
-
config.app_id = ENV[
|
192
|
-
config.app_secret = ENV[
|
193
|
-
config.business_account_id = ENV[
|
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"]
|
194
191
|
end
|
195
192
|
end
|
196
193
|
|
197
194
|
def development_whatsapp_config
|
198
195
|
FlowChat::Whatsapp::Configuration.new.tap do |config|
|
199
|
-
config.access_token = ENV[
|
200
|
-
config.phone_number_id = ENV[
|
201
|
-
config.verify_token = ENV[
|
202
|
-
config.app_id = ENV[
|
203
|
-
config.app_secret = ENV[
|
204
|
-
config.business_account_id = ENV[
|
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"]
|
205
202
|
end
|
206
203
|
end
|
207
204
|
end
|
@@ -221,8 +218,7 @@ class CustomWhatsappController < ApplicationController
|
|
221
218
|
my_config.business_account_id = "your_business_account_id"
|
222
219
|
|
223
220
|
processor = FlowChat::Whatsapp::Processor.new(self) do |config|
|
224
|
-
config.
|
225
|
-
config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi
|
221
|
+
config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi, my_config
|
226
222
|
config.use_session_store FlowChat::Session::CacheSessionStore
|
227
223
|
end
|
228
224
|
|
@@ -236,13 +232,13 @@ end
|
|
236
232
|
# constraints subdomain: /\w+/ do
|
237
233
|
# post '/whatsapp/webhook', to: 'multi_tenant_whatsapp#webhook'
|
238
234
|
# end
|
239
|
-
#
|
240
|
-
# # Path-based routing
|
235
|
+
#
|
236
|
+
# # Path-based routing
|
241
237
|
# post '/whatsapp/:tenant/webhook', to: 'multi_tenant_whatsapp#webhook'
|
242
|
-
#
|
238
|
+
#
|
243
239
|
# # Environment-specific
|
244
240
|
# post '/whatsapp/env/webhook', to: 'environment_whatsapp#webhook'
|
245
|
-
#
|
241
|
+
#
|
246
242
|
# # Custom endpoint
|
247
243
|
# post '/whatsapp/custom/webhook', to: 'custom_whatsapp#webhook'
|
248
|
-
# end
|
244
|
+
# end
|
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
|
@@ -22,16 +22,15 @@ class CustomWhatsappController < ApplicationController
|
|
22
22
|
def webhook
|
23
23
|
# Create custom WhatsApp configuration for this endpoint
|
24
24
|
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[
|
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
31
|
|
32
32
|
processor = FlowChat::Whatsapp::Processor.new(self) do |config|
|
33
|
-
config.
|
34
|
-
config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi
|
33
|
+
config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi, custom_config
|
35
34
|
config.use_session_store FlowChat::Session::CacheSessionStore
|
36
35
|
end
|
37
36
|
|
@@ -54,7 +53,7 @@ class WelcomeFlow < FlowChat::Flow
|
|
54
53
|
choice = app.screen(:main_menu) do |prompt|
|
55
54
|
prompt.select "Hi #{name}! What can I help you with today?", {
|
56
55
|
"info" => "📋 Get Information",
|
57
|
-
"support" => "🆘 Contact Support",
|
56
|
+
"support" => "🆘 Contact Support",
|
58
57
|
"feedback" => "💬 Give Feedback"
|
59
58
|
}
|
60
59
|
end
|
@@ -95,7 +94,7 @@ class WelcomeFlow < FlowChat::Flow
|
|
95
94
|
contact_method = app.screen(:contact_method) do |prompt|
|
96
95
|
prompt.select "How would you like to contact support?", {
|
97
96
|
"call" => "📞 Call Us",
|
98
|
-
"email" => "📧 Email Us",
|
97
|
+
"email" => "📧 Email Us",
|
99
98
|
"chat" => "💬 Live Chat"
|
100
99
|
}
|
101
100
|
end
|
@@ -114,7 +113,7 @@ class WelcomeFlow < FlowChat::Flow
|
|
114
113
|
rating = app.screen(:rating) do |prompt|
|
115
114
|
prompt.select "How would you rate our service?", {
|
116
115
|
"5" => "⭐⭐⭐⭐⭐ Excellent",
|
117
|
-
"4" => "⭐⭐⭐⭐ Good",
|
116
|
+
"4" => "⭐⭐⭐⭐ Good",
|
118
117
|
"3" => "⭐⭐⭐ Average",
|
119
118
|
"2" => "⭐⭐ Poor",
|
120
119
|
"1" => "⭐ Very Poor"
|
@@ -138,4 +137,4 @@ class WelcomeFlow < FlowChat::Flow
|
|
138
137
|
end
|
139
138
|
|
140
139
|
# Add this route to your config/routes.rb:
|
141
|
-
# post '/whatsapp/webhook', to: 'whatsapp#webhook'
|
140
|
+
# post '/whatsapp/webhook', to: 'whatsapp#webhook'
|