flow_chat 0.3.0 โ 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/README.md +266 -16
- 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/session/cache_session_store.rb +84 -0
- data/lib/flow_chat/session/middleware.rb +14 -6
- data/lib/flow_chat/ussd/app.rb +25 -0
- data/lib/flow_chat/ussd/gateway/nalo.rb +2 -0
- data/lib/flow_chat/ussd/gateway/nsano.rb +6 -0
- data/lib/flow_chat/ussd/middleware/resumable_session.rb +1 -1
- data/lib/flow_chat/ussd/processor.rb +15 -42
- 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 +14 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e4bd1ce536b9d214041fdd2926426449969918262f04829aca7f7e7251b8b4b6
|
4
|
+
data.tar.gz: 989238eac1d423e28d4139d2267200cea86e666992b400e8d2299b0ddc1ed8bf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a26004d9e08aa4244f24f8ea57e6529699f827ca6d009042db0028bba611cbc5077ad39a11d654c9f4e6b3125881ff72893695f00c3d197a786770e484b209ac
|
7
|
+
data.tar.gz: e22bbf5a0ec53174e44b4eb1d5d453a6d9f3e5b2dd8107d1d14039fc315235aa28c3bbfb7877f882795fd3e978ddedf8deddd836a99d1c551f311c6410e55ea8
|
data/README.md
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
# FlowChat
|
2
2
|
|
3
|
-
FlowChat is a Rails framework designed for building sophisticated conversational workflows
|
3
|
+
FlowChat is a Rails framework designed for building sophisticated conversational workflows for both USSD (Unstructured Supplementary Service Data) systems and WhatsApp messaging. It provides an intuitive Ruby DSL for creating multi-step, menu-driven conversations with automatic session management, input validation, and flow control.
|
4
4
|
|
5
5
|
**Key Features:**
|
6
6
|
- ๐ฏ **Declarative Flow Definition** - Define conversation flows as Ruby classes
|
7
7
|
- ๐ **Automatic Session Management** - Persistent state across requests
|
8
8
|
- โ
**Input Validation & Transformation** - Built-in validation and data conversion
|
9
9
|
- ๐ **Middleware Architecture** - Flexible request processing pipeline
|
10
|
-
- ๐ฑ **USSD Gateway Support** - Currently supports Nalo
|
10
|
+
- ๐ฑ **USSD Gateway Support** - Currently supports Nalo gateways
|
11
|
+
- ๐ฌ **WhatsApp Integration** - Full WhatsApp Cloud API support with interactive messages
|
11
12
|
- ๐งช **Built-in Testing Tools** - USSD simulator for local development
|
12
13
|
|
13
14
|
## Architecture Overview
|
@@ -21,9 +22,9 @@ User Input โ Gateway โ Session โ Pagination โ Custom โ Executor โ Fl
|
|
21
22
|
```
|
22
23
|
|
23
24
|
**Middleware Pipeline:**
|
24
|
-
- **Gateway**:
|
25
|
+
- **Gateway**: Communication with providers (USSD: Nalo, WhatsApp: Cloud API)
|
25
26
|
- **Session**: Load/save conversation state
|
26
|
-
- **Pagination**: Split long responses into pages
|
27
|
+
- **Pagination**: Split long responses into pages (USSD only)
|
27
28
|
- **Custom**: Your application middleware (logging, auth, etc.)
|
28
29
|
- **Executor**: Execute flow methods and handle interrupts
|
29
30
|
|
@@ -43,6 +44,10 @@ bundle install
|
|
43
44
|
|
44
45
|
## Quick Start
|
45
46
|
|
47
|
+
FlowChat supports both USSD and WhatsApp. Choose the platform that fits your needs:
|
48
|
+
|
49
|
+
### USSD Setup
|
50
|
+
|
46
51
|
### 1. Create Your First Flow
|
47
52
|
|
48
53
|
Create a flow class in `app/flow_chat/welcome_flow.rb`:
|
@@ -60,7 +65,7 @@ class WelcomeFlow < FlowChat::Flow
|
|
60
65
|
end
|
61
66
|
```
|
62
67
|
|
63
|
-
### 2. Set Up the Controller
|
68
|
+
### 2. Set Up the USSD Controller
|
64
69
|
|
65
70
|
Create a controller to handle USSD requests:
|
66
71
|
|
@@ -89,6 +94,233 @@ Rails.application.routes.draw do
|
|
89
94
|
end
|
90
95
|
```
|
91
96
|
|
97
|
+
๐ก **Tip**: See [examples/ussd_controller.rb](examples/ussd_controller.rb) for a complete USSD controller example with payment flows, customer support, and custom middleware.
|
98
|
+
|
99
|
+
### WhatsApp Setup
|
100
|
+
|
101
|
+
### 1. Configure WhatsApp Credentials
|
102
|
+
|
103
|
+
FlowChat supports two ways to configure WhatsApp credentials:
|
104
|
+
|
105
|
+
**Option A: Using Rails Credentials**
|
106
|
+
|
107
|
+
Add your WhatsApp credentials to Rails credentials:
|
108
|
+
|
109
|
+
```bash
|
110
|
+
rails credentials:edit
|
111
|
+
```
|
112
|
+
|
113
|
+
```yaml
|
114
|
+
whatsapp:
|
115
|
+
access_token: "your_access_token"
|
116
|
+
phone_number_id: "your_phone_number_id"
|
117
|
+
verify_token: "your_verify_token"
|
118
|
+
app_id: "your_app_id"
|
119
|
+
app_secret: "your_app_secret"
|
120
|
+
webhook_url: "your_webhook_url"
|
121
|
+
business_account_id: "your_business_account_id"
|
122
|
+
```
|
123
|
+
|
124
|
+
**Option B: Using Environment Variables**
|
125
|
+
|
126
|
+
Alternatively, you can use environment variables:
|
127
|
+
|
128
|
+
```bash
|
129
|
+
# Add to your .env file or environment
|
130
|
+
export WHATSAPP_ACCESS_TOKEN="your_access_token"
|
131
|
+
export WHATSAPP_PHONE_NUMBER_ID="your_phone_number_id"
|
132
|
+
export WHATSAPP_VERIFY_TOKEN="your_verify_token"
|
133
|
+
export WHATSAPP_APP_ID="your_app_id"
|
134
|
+
export WHATSAPP_APP_SECRET="your_app_secret"
|
135
|
+
export WHATSAPP_WEBHOOK_URL="your_webhook_url"
|
136
|
+
export WHATSAPP_BUSINESS_ACCOUNT_ID="your_business_account_id"
|
137
|
+
```
|
138
|
+
|
139
|
+
FlowChat will automatically use Rails credentials first, falling back to environment variables if credentials are not available.
|
140
|
+
|
141
|
+
**Option C: Per-Setup Configuration**
|
142
|
+
|
143
|
+
For multi-tenant applications or when you need different WhatsApp accounts per endpoint:
|
144
|
+
|
145
|
+
```ruby
|
146
|
+
# Create custom configuration
|
147
|
+
custom_config = FlowChat::Whatsapp::Configuration.new
|
148
|
+
custom_config.access_token = "your_specific_access_token"
|
149
|
+
custom_config.phone_number_id = "your_specific_phone_number_id"
|
150
|
+
custom_config.verify_token = "your_specific_verify_token"
|
151
|
+
custom_config.app_id = "your_specific_app_id"
|
152
|
+
custom_config.app_secret = "your_specific_app_secret"
|
153
|
+
custom_config.business_account_id = "your_specific_business_account_id"
|
154
|
+
|
155
|
+
# Use in processor
|
156
|
+
processor = FlowChat::Whatsapp::Processor.new(self) do |config|
|
157
|
+
config.use_whatsapp_config(custom_config) # Pass custom config
|
158
|
+
config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi
|
159
|
+
config.use_session_store FlowChat::Session::CacheSessionStore
|
160
|
+
end
|
161
|
+
```
|
162
|
+
|
163
|
+
๐ก **Tip**: See [examples/multi_tenant_whatsapp_controller.rb](examples/multi_tenant_whatsapp_controller.rb) for comprehensive multi-tenant and per-setup configuration examples.
|
164
|
+
|
165
|
+
### 2. Create WhatsApp Controller
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
class WhatsappController < ApplicationController
|
169
|
+
skip_forgery_protection
|
170
|
+
|
171
|
+
def webhook
|
172
|
+
processor = FlowChat::Whatsapp::Processor.new(self) do |config|
|
173
|
+
config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi
|
174
|
+
config.use_session_store FlowChat::Session::CacheSessionStore
|
175
|
+
end
|
176
|
+
|
177
|
+
processor.run WelcomeFlow, :main_page
|
178
|
+
end
|
179
|
+
end
|
180
|
+
```
|
181
|
+
|
182
|
+
### 3. Add WhatsApp Route
|
183
|
+
|
184
|
+
```ruby
|
185
|
+
Rails.application.routes.draw do
|
186
|
+
match '/whatsapp/webhook', to: 'whatsapp#webhook', via: [:get, :post]
|
187
|
+
end
|
188
|
+
```
|
189
|
+
|
190
|
+
### 4. Enhanced Features for WhatsApp
|
191
|
+
|
192
|
+
The same flow works for both USSD and WhatsApp, but WhatsApp provides additional data and better interactive features:
|
193
|
+
|
194
|
+
```ruby
|
195
|
+
class WelcomeFlow < FlowChat::Flow
|
196
|
+
def main_page
|
197
|
+
# Access WhatsApp-specific data
|
198
|
+
Rails.logger.info "Contact: #{app.contact_name}, Phone: #{app.phone_number}"
|
199
|
+
Rails.logger.info "Message ID: #{app.message_id}, Timestamp: #{app.timestamp}"
|
200
|
+
|
201
|
+
# Handle location sharing
|
202
|
+
if app.location
|
203
|
+
app.say "Thanks for sharing your location! We see you're at #{app.location['latitude']}, #{app.location['longitude']}"
|
204
|
+
return
|
205
|
+
end
|
206
|
+
|
207
|
+
# Handle media messages
|
208
|
+
if app.media
|
209
|
+
app.say "Thanks for the #{app.media['type']} file! We received: #{app.media['id']}"
|
210
|
+
return
|
211
|
+
end
|
212
|
+
|
213
|
+
name = app.screen(:name) do |prompt|
|
214
|
+
prompt.ask "Hello! Welcome to our WhatsApp service. What's your name?",
|
215
|
+
transform: ->(input) { input.strip.titleize }
|
216
|
+
end
|
217
|
+
|
218
|
+
# WhatsApp supports interactive buttons and lists via prompt.select
|
219
|
+
choice = app.screen(:main_menu) do |prompt|
|
220
|
+
prompt.select "Hi #{name}! How can I help you?", {
|
221
|
+
"info" => "๐ Get Information",
|
222
|
+
"support" => "๐ Contact Support",
|
223
|
+
"feedback" => "๐ฌ Give Feedback"
|
224
|
+
}
|
225
|
+
end
|
226
|
+
|
227
|
+
case choice
|
228
|
+
when "info"
|
229
|
+
show_information_menu
|
230
|
+
when "support"
|
231
|
+
contact_support
|
232
|
+
when "feedback"
|
233
|
+
collect_feedback
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
private
|
238
|
+
|
239
|
+
def show_information_menu
|
240
|
+
info_choice = app.screen(:info_menu) do |prompt|
|
241
|
+
prompt.select "What information do you need?", {
|
242
|
+
"hours" => "๐ Business Hours",
|
243
|
+
"location" => "๐ Our Location",
|
244
|
+
"services" => "๐ผ Our Services"
|
245
|
+
}
|
246
|
+
end
|
247
|
+
|
248
|
+
case info_choice
|
249
|
+
when "hours"
|
250
|
+
app.say "We're open Monday-Friday 9AM-6PM, Saturday 10AM-4PM. Closed Sundays."
|
251
|
+
when "location"
|
252
|
+
app.say "๐ Visit us at 123 Main Street, Downtown. We're next to the coffee shop!"
|
253
|
+
when "services"
|
254
|
+
app.say "๐ผ We offer: Web Development, Mobile Apps, Cloud Services, and IT Consulting."
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
def contact_support
|
259
|
+
support_choice = app.screen(:support_menu) do |prompt|
|
260
|
+
prompt.select "How would you like to contact support?", {
|
261
|
+
"call" => "๐ Call Us",
|
262
|
+
"email" => "๐ง Email Us",
|
263
|
+
"chat" => "๐ฌ Continue Here"
|
264
|
+
}
|
265
|
+
end
|
266
|
+
|
267
|
+
case support_choice
|
268
|
+
when "call"
|
269
|
+
app.say "๐ Call us at: +1-555-HELP (4357)\nAvailable Mon-Fri 9AM-5PM"
|
270
|
+
when "email"
|
271
|
+
app.say "๐ง Email us at: support@company.com\nWe typically respond within 24 hours"
|
272
|
+
when "chat"
|
273
|
+
app.say "๐ฌ Great! Please describe your issue and we'll help you right away."
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
def collect_feedback
|
278
|
+
rating = app.screen(:rating) do |prompt|
|
279
|
+
prompt.select "How would you rate our service?", {
|
280
|
+
"5" => "โญโญโญโญโญ Excellent",
|
281
|
+
"4" => "โญโญโญโญ Good",
|
282
|
+
"3" => "โญโญโญ Average",
|
283
|
+
"2" => "โญโญ Poor",
|
284
|
+
"1" => "โญ Very Poor"
|
285
|
+
}
|
286
|
+
end
|
287
|
+
|
288
|
+
feedback = app.screen(:feedback_text) do |prompt|
|
289
|
+
prompt.ask "Thank you for the #{rating}-star rating! Please share any additional feedback:"
|
290
|
+
end
|
291
|
+
|
292
|
+
# Use WhatsApp-specific data for logging
|
293
|
+
Rails.logger.info "Feedback from #{app.contact_name} (#{app.phone_number}): #{rating} stars - #{feedback}"
|
294
|
+
|
295
|
+
app.say "Thank you for your feedback! We really appreciate it. ๐"
|
296
|
+
end
|
297
|
+
end
|
298
|
+
```
|
299
|
+
|
300
|
+
For detailed WhatsApp setup instructions, see [WhatsApp Integration Guide](docs/whatsapp_setup.md).
|
301
|
+
|
302
|
+
## Cross-Platform Compatibility
|
303
|
+
|
304
|
+
FlowChat provides a unified API that works across both USSD and WhatsApp platforms, with graceful degradation for platform-specific features:
|
305
|
+
|
306
|
+
### Shared Features (Both USSD & WhatsApp)
|
307
|
+
- โ
`app.screen()` - Interactive screens with prompts
|
308
|
+
- โ
`app.say()` - Send messages to users
|
309
|
+
- โ
`prompt.ask()` - Text input collection
|
310
|
+
- โ
`prompt.select()` - Menu selection (renders as numbered list in USSD, interactive buttons/lists in WhatsApp)
|
311
|
+
- โ
`prompt.yes?()` - Yes/no questions
|
312
|
+
- โ
`app.phone_number` - User's phone number
|
313
|
+
- โ
`app.message_id` - Unique message identifier
|
314
|
+
- โ
`app.timestamp` - Message timestamp
|
315
|
+
|
316
|
+
### WhatsApp-Only Features
|
317
|
+
- โ
`app.contact_name` - WhatsApp contact name (returns `nil` in USSD)
|
318
|
+
- โ
`app.location` - Location sharing data (returns `nil` in USSD)
|
319
|
+
- โ
`app.media` - Media file attachments (returns `nil` in USSD)
|
320
|
+
- โ
Rich interactive elements (buttons, lists) automatically generated from `prompt.select()`
|
321
|
+
|
322
|
+
This design allows you to write flows once and deploy them on both platforms, with WhatsApp users getting enhanced interactive features automatically.
|
323
|
+
|
92
324
|
## Core Concepts
|
93
325
|
|
94
326
|
### Flows and Screens
|
@@ -240,7 +472,7 @@ When you run a flow, FlowChat automatically builds this middleware stack:
|
|
240
472
|
User Input โ Gateway โ Session โ Pagination โ Custom Middleware โ Executor โ Flow
|
241
473
|
```
|
242
474
|
|
243
|
-
1. **Gateway Middleware** - Handles USSD provider communication (Nalo
|
475
|
+
1. **Gateway Middleware** - Handles USSD provider communication (Nalo)
|
244
476
|
2. **Session Middleware** - Manages session storage and retrieval
|
245
477
|
3. **Pagination Middleware** - Automatically splits long responses across pages
|
246
478
|
4. **Custom Middleware** - Your application-specific middleware (optional)
|
@@ -338,9 +570,6 @@ FlowChat supports multiple USSD gateways:
|
|
338
570
|
```ruby
|
339
571
|
# Nalo Solutions Gateway
|
340
572
|
config.use_gateway FlowChat::Ussd::Gateway::Nalo
|
341
|
-
|
342
|
-
# Nsano Gateway
|
343
|
-
config.use_gateway FlowChat::Ussd::Gateway::Nsano
|
344
573
|
```
|
345
574
|
|
346
575
|
## Testing
|
@@ -556,12 +785,32 @@ end
|
|
556
785
|
|
557
786
|
## Configuration
|
558
787
|
|
788
|
+
### Cache Configuration
|
789
|
+
|
790
|
+
The `CacheSessionStore` requires a cache to be configured. Set it up in your Rails application:
|
791
|
+
|
792
|
+
```ruby
|
793
|
+
# config/application.rb or config/environments/*.rb
|
794
|
+
FlowChat::Config.cache = Rails.cache
|
795
|
+
|
796
|
+
# Or use a specific cache store
|
797
|
+
FlowChat::Config.cache = ActiveSupport::Cache::MemoryStore.new
|
798
|
+
|
799
|
+
# For Redis (requires redis gem)
|
800
|
+
FlowChat::Config.cache = ActiveSupport::Cache::RedisCacheStore.new(url: "redis://localhost:6379/1")
|
801
|
+
```
|
802
|
+
|
803
|
+
๐ก **Tip**: See [examples/initializer.rb](examples/initializer.rb) for a complete configuration example.
|
804
|
+
|
559
805
|
### Session Storage Options
|
560
806
|
|
561
807
|
Configure different session storage backends:
|
562
808
|
|
563
809
|
```ruby
|
564
|
-
#
|
810
|
+
# Cache session store (default) - uses FlowChat::Config.cache
|
811
|
+
config.use_session_store FlowChat::Session::CacheSessionStore
|
812
|
+
|
813
|
+
# Rails session (for USSD)
|
565
814
|
config.use_session_store FlowChat::Session::RailsSessionStore
|
566
815
|
|
567
816
|
# Custom session store
|
@@ -611,12 +860,13 @@ bundle exec rake test TESTOPTS="--name=test_flow_initialization"
|
|
611
860
|
|
612
861
|
## Roadmap
|
613
862
|
|
614
|
-
- ๐ฑ **WhatsApp Integration** - Support for WhatsApp Business API
|
615
863
|
- ๐ฌ **Telegram Bot Support** - Native Telegram bot integration
|
616
|
-
- ๐ **Sub-flows** - Reusable conversation components
|
617
|
-
- ๐ **Analytics Integration** - Built-in conversation analytics
|
618
|
-
- ๐ **Multi-language Support** - Internationalization features
|
619
|
-
- โก **Performance Optimizations** - Improved middleware performance
|
864
|
+
- ๐ **Sub-flows** - Reusable conversation components and flow composition
|
865
|
+
- ๐ **Analytics Integration** - Built-in conversation analytics and user journey tracking
|
866
|
+
- ๐ **Multi-language Support** - Internationalization and localization features
|
867
|
+
- โก **Performance Optimizations** - Improved middleware performance and caching
|
868
|
+
- ๐ฏ **Advanced Validation** - More validation helpers and custom validators
|
869
|
+
- ๐ **Enhanced Security** - Rate limiting, input sanitization, and fraud detection
|
620
870
|
|
621
871
|
## License
|
622
872
|
|
@@ -626,4 +876,4 @@ FlowChat is available as open source under the terms of the [MIT License](https:
|
|
626
876
|
|
627
877
|
- ๐ **Documentation**: [GitHub Repository](https://github.com/radioactive-labs/flow_chat)
|
628
878
|
- ๐ **Bug Reports**: [GitHub Issues](https://github.com/radioactive-labs/flow_chat/issues)
|
629
|
-
- ๐ฌ **Community**: Join our discussions for help and feature requests
|
879
|
+
- ๐ฌ **Community**: Join our discussions for help and feature requests
|
@@ -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
|