flow_chat 0.8.1 → 0.8.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/docs/configuration.md +2 -2
- data/docs/http-gateway-protocol.md +432 -0
- data/docs/sessions.md +7 -7
- data/docs/ussd-setup.md +1 -1
- data/examples/http_controller.rb +154 -0
- data/examples/simulator_controller.rb +21 -1
- data/examples/ussd_controller.rb +1 -1
- data/lib/flow_chat/base_app.rb +13 -1
- data/lib/flow_chat/base_executor.rb +1 -1
- data/lib/flow_chat/base_processor.rb +7 -6
- data/lib/flow_chat/config.rb +17 -2
- data/lib/flow_chat/http/app.rb +6 -0
- data/lib/flow_chat/http/gateway/simple.rb +77 -0
- data/lib/flow_chat/http/middleware/executor.rb +24 -0
- data/lib/flow_chat/http/processor.rb +33 -0
- data/lib/flow_chat/http/renderer.rb +41 -0
- data/lib/flow_chat/instrumentation.rb +2 -0
- data/lib/flow_chat/phone_number_util.rb +47 -0
- data/lib/flow_chat/session/cache_session_store.rb +1 -17
- data/lib/flow_chat/session/middleware.rb +19 -18
- data/lib/flow_chat/simulator/controller.rb +17 -5
- data/lib/flow_chat/simulator/views/simulator.html.erb +220 -8
- data/lib/flow_chat/ussd/gateway/nalo.rb +3 -7
- data/lib/flow_chat/ussd/gateway/nsano.rb +0 -2
- data/lib/flow_chat/version.rb +1 -1
- data/lib/flow_chat/whatsapp/gateway/cloud_api.rb +16 -14
- metadata +10 -2
@@ -31,6 +31,15 @@ class SimulatorController < ApplicationController
|
|
31
31
|
icon: "💬",
|
32
32
|
color: "#25D366"
|
33
33
|
},
|
34
|
+
http_main: {
|
35
|
+
name: "Main HTTP API",
|
36
|
+
description: "JSON HTTP API endpoint",
|
37
|
+
processor_type: "http",
|
38
|
+
gateway: "http_simple",
|
39
|
+
endpoint: "/http/webhook",
|
40
|
+
icon: "🌐",
|
41
|
+
color: "#0066cc"
|
42
|
+
},
|
34
43
|
whatsapp_tenant_a: {
|
35
44
|
name: "Tenant A WhatsApp",
|
36
45
|
description: "Multi-tenant endpoint for Tenant A",
|
@@ -40,6 +49,15 @@ class SimulatorController < ApplicationController
|
|
40
49
|
icon: "🏢",
|
41
50
|
color: "#fd7e14"
|
42
51
|
},
|
52
|
+
http_external: {
|
53
|
+
name: "External HTTP Test",
|
54
|
+
description: "Test with external HTTP server",
|
55
|
+
processor_type: "http",
|
56
|
+
gateway: "http_simple",
|
57
|
+
endpoint: "http://localhost:4567/http/webhook",
|
58
|
+
icon: "🔗",
|
59
|
+
color: "#17a2b8"
|
60
|
+
},
|
43
61
|
whatsapp_legacy: {
|
44
62
|
name: "Legacy WhatsApp",
|
45
63
|
description: "Legacy endpoint for compatibility",
|
@@ -54,7 +72,7 @@ class SimulatorController < ApplicationController
|
|
54
72
|
|
55
73
|
# Default configuration to start with
|
56
74
|
def default_config_key
|
57
|
-
:
|
75
|
+
:http_main
|
58
76
|
end
|
59
77
|
|
60
78
|
# Default test phone number
|
@@ -79,8 +97,10 @@ end
|
|
79
97
|
# 5. View request/response logs in real-time
|
80
98
|
|
81
99
|
# This allows you to test:
|
100
|
+
# - Different protocol types (USSD, WhatsApp, HTTP)
|
82
101
|
# - Different controller implementations on the same server
|
83
102
|
# - Different API versions (v1, v2, etc.)
|
84
103
|
# - Multi-tenant endpoints with different configurations
|
85
104
|
# - Legacy endpoints alongside new ones
|
105
|
+
# - External HTTP servers (run examples/http_simulator_test.rb)
|
86
106
|
# - Different flow implementations for different endpoints
|
data/examples/ussd_controller.rb
CHANGED
@@ -237,7 +237,7 @@ class UssdController < ApplicationController
|
|
237
237
|
# Or configure session boundaries explicitly:
|
238
238
|
# config.use_session_config(
|
239
239
|
# boundaries: [:flow, :platform], # which boundaries to enforce
|
240
|
-
#
|
240
|
+
# hash_identifiers: true # hash phone numbers for privacy
|
241
241
|
# )
|
242
242
|
end
|
243
243
|
|
data/lib/flow_chat/base_app.rb
CHANGED
@@ -40,7 +40,19 @@ module FlowChat
|
|
40
40
|
raise FlowChat::Interrupt::Terminate.new(msg, media: media)
|
41
41
|
end
|
42
42
|
|
43
|
-
def
|
43
|
+
def platform
|
44
|
+
context["request.platform"]
|
45
|
+
end
|
46
|
+
|
47
|
+
def gateway
|
48
|
+
context["request.gateway"]
|
49
|
+
end
|
50
|
+
|
51
|
+
def user_id
|
52
|
+
context["request.user_id"]
|
53
|
+
end
|
54
|
+
|
55
|
+
def msisdn
|
44
56
|
context["request.msisdn"]
|
45
57
|
end
|
46
58
|
|
@@ -32,7 +32,7 @@ module FlowChat
|
|
32
32
|
FlowChat.logger.info { "#{log_prefix}: Flow terminated - Session: #{session_id}, Message: '#{e.prompt&.truncate(100)}'" }
|
33
33
|
FlowChat.logger.debug { "#{log_prefix}: Destroying session #{session_id}" }
|
34
34
|
context.session.destroy
|
35
|
-
[:
|
35
|
+
[:terminal, e.prompt, nil, e.media]
|
36
36
|
rescue => error
|
37
37
|
FlowChat.logger.error { "#{log_prefix}: Flow execution failed - #{flow_class.name}##{action}, Session: #{session_id}, Error: #{error.class.name}: #{error.message}" }
|
38
38
|
FlowChat.logger.debug { "#{log_prefix}: Stack trace: #{error.backtrace.join("\n")}" }
|
@@ -36,14 +36,14 @@ module FlowChat
|
|
36
36
|
self
|
37
37
|
end
|
38
38
|
|
39
|
-
def use_session_config(boundaries: nil,
|
40
|
-
FlowChat.logger.debug { "BaseProcessor: Configuring session config: boundaries=#{boundaries.inspect},
|
39
|
+
def use_session_config(boundaries: nil, hash_identifiers: nil, identifier: nil)
|
40
|
+
FlowChat.logger.debug { "BaseProcessor: Configuring session config: boundaries=#{boundaries.inspect}, hash_identifiers=#{hash_identifiers}, identifier=#{identifier}" }
|
41
41
|
|
42
42
|
# Update the session options directly
|
43
43
|
@session_options = @session_options.dup
|
44
|
-
@session_options.boundaries = Array(boundaries) if boundaries
|
45
|
-
@session_options.
|
46
|
-
@session_options.identifier = identifier if identifier
|
44
|
+
@session_options.boundaries = Array(boundaries) if boundaries != nil
|
45
|
+
@session_options.hash_identifiers = hash_identifiers if hash_identifiers != nil
|
46
|
+
@session_options.identifier = identifier if identifier != nil
|
47
47
|
|
48
48
|
self
|
49
49
|
end
|
@@ -70,7 +70,7 @@ module FlowChat
|
|
70
70
|
use_session_config(boundaries: current_boundaries)
|
71
71
|
end
|
72
72
|
|
73
|
-
def run(flow_class, action)
|
73
|
+
def run(flow_class, action, **options)
|
74
74
|
# Instrument flow execution (this will log via LogSubscriber)
|
75
75
|
instrument(Events::FLOW_EXECUTION_START, {
|
76
76
|
flow_name: flow_class.name.underscore,
|
@@ -81,6 +81,7 @@ module FlowChat
|
|
81
81
|
@context["flow.name"] = flow_class.name.underscore
|
82
82
|
@context["flow.class"] = flow_class
|
83
83
|
@context["flow.action"] = action
|
84
|
+
@context["flow.options"] = options
|
84
85
|
|
85
86
|
FlowChat.logger.debug { "BaseProcessor: Context prepared for flow #{flow_class.name}" }
|
86
87
|
|
data/lib/flow_chat/config.rb
CHANGED
@@ -23,8 +23,13 @@ module FlowChat
|
|
23
23
|
@whatsapp ||= WhatsappConfig.new
|
24
24
|
end
|
25
25
|
|
26
|
+
# HTTP-specific configuration object
|
27
|
+
def self.http
|
28
|
+
@http ||= HttpConfig.new
|
29
|
+
end
|
30
|
+
|
26
31
|
class SessionConfig
|
27
|
-
attr_accessor :boundaries, :
|
32
|
+
attr_accessor :boundaries, :hash_identifiers, :identifier
|
28
33
|
|
29
34
|
def initialize
|
30
35
|
# Session boundaries control how session IDs are constructed
|
@@ -34,7 +39,7 @@ module FlowChat
|
|
34
39
|
@boundaries = [:flow, :gateway, :platform]
|
35
40
|
|
36
41
|
# Always hash phone numbers for privacy
|
37
|
-
@
|
42
|
+
@hash_identifiers = true
|
38
43
|
|
39
44
|
# Session identifier type (nil = let platforms choose their default)
|
40
45
|
# :msisdn = durable sessions (durable across timeouts)
|
@@ -88,6 +93,16 @@ module FlowChat
|
|
88
93
|
@message_handling_mode == :simulator
|
89
94
|
end
|
90
95
|
end
|
96
|
+
|
97
|
+
class HttpConfig
|
98
|
+
attr_accessor :default_gateway, :request_timeout, :response_format
|
99
|
+
|
100
|
+
def initialize
|
101
|
+
@default_gateway = :simple
|
102
|
+
@request_timeout = 30
|
103
|
+
@response_format = :json
|
104
|
+
end
|
105
|
+
end
|
91
106
|
end
|
92
107
|
|
93
108
|
# Shorthand for accessing the logger throughout the application
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module FlowChat
|
2
|
+
module Http
|
3
|
+
module Gateway
|
4
|
+
class Simple
|
5
|
+
include FlowChat::Instrumentation
|
6
|
+
|
7
|
+
attr_reader :context
|
8
|
+
|
9
|
+
def initialize(app)
|
10
|
+
@app = app
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(context)
|
14
|
+
@context = context
|
15
|
+
params = context.controller.request.params
|
16
|
+
request = context.controller.request
|
17
|
+
|
18
|
+
# Extract basic request information
|
19
|
+
context["request.id"] = params["session_id"] || SecureRandom.uuid
|
20
|
+
context["request.msisdn"] = FlowChat::PhoneNumberUtil.to_e164(params["msisdn"])
|
21
|
+
context["request.user_id"] = params["user_id"] || context["request.msisdn"] || context["request.id"]
|
22
|
+
context["request.message_id"] = params["message_id"] || SecureRandom.uuid
|
23
|
+
context["request.timestamp"] = Time.current.iso8601
|
24
|
+
context["request.gateway"] = :http_simple
|
25
|
+
context["request.platform"] = :http
|
26
|
+
context["request.network"] = nil
|
27
|
+
context["request.method"] = request.method
|
28
|
+
context["request.path"] = request.path
|
29
|
+
context["request.user_agent"] = request.user_agent
|
30
|
+
context.input = params["input"] || params["message"]
|
31
|
+
|
32
|
+
# Instrument message received when user provides input
|
33
|
+
if context.input.present?
|
34
|
+
instrument(Events::MESSAGE_RECEIVED, {
|
35
|
+
from: context["request.user_id"],
|
36
|
+
message: context.input,
|
37
|
+
timestamp: context["request.timestamp"]
|
38
|
+
})
|
39
|
+
end
|
40
|
+
|
41
|
+
# Process the request
|
42
|
+
type, prompt, choices, media = @app.call(context)
|
43
|
+
|
44
|
+
# Instrument message sent
|
45
|
+
instrument(Events::MESSAGE_SENT, {
|
46
|
+
to: context["request.user_id"],
|
47
|
+
session_id: context["request.id"],
|
48
|
+
message: context.input || "",
|
49
|
+
message_type: (type == :prompt) ? "prompt" : "terminal",
|
50
|
+
gateway: :http_simple,
|
51
|
+
platform: :http,
|
52
|
+
content_length: prompt.to_s.length,
|
53
|
+
timestamp: context["request.timestamp"]
|
54
|
+
})
|
55
|
+
|
56
|
+
# Render response as JSON
|
57
|
+
response_data = render_response(type, prompt, choices, media)
|
58
|
+
context.controller.render json: response_data
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def render_response(type, prompt, choices, media)
|
64
|
+
rendered = FlowChat::Http::Renderer.new(prompt, choices: choices, media: media).render
|
65
|
+
|
66
|
+
{
|
67
|
+
type: type,
|
68
|
+
session_id: context["request.id"],
|
69
|
+
user_id: context["request.user_id"],
|
70
|
+
timestamp: context["request.timestamp"],
|
71
|
+
**rendered
|
72
|
+
}
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require_relative "../../base_executor"
|
2
|
+
|
3
|
+
module FlowChat
|
4
|
+
module Http
|
5
|
+
module Middleware
|
6
|
+
class Executor < FlowChat::BaseExecutor
|
7
|
+
protected
|
8
|
+
|
9
|
+
def platform_name
|
10
|
+
"HTTP"
|
11
|
+
end
|
12
|
+
|
13
|
+
def log_prefix
|
14
|
+
"Http::Executor"
|
15
|
+
end
|
16
|
+
|
17
|
+
def build_platform_app(context)
|
18
|
+
FlowChat.logger.debug { "#{log_prefix}: Building HTTP app instance" }
|
19
|
+
FlowChat::Http::App.new(context)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module FlowChat
|
2
|
+
module Http
|
3
|
+
class Processor < FlowChat::BaseProcessor
|
4
|
+
def use_durable_sessions(cross_gateway: false)
|
5
|
+
FlowChat.logger.debug { "Http::Processor: Enabling durable sessions via session configuration" }
|
6
|
+
use_session_config(
|
7
|
+
identifier: :user_id
|
8
|
+
)
|
9
|
+
end
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
def middleware_name
|
14
|
+
"http.middleware"
|
15
|
+
end
|
16
|
+
|
17
|
+
def build_middleware_stack
|
18
|
+
FlowChat.logger.debug { "Http::Processor: Building HTTP middleware stack" }
|
19
|
+
create_middleware_stack("http")
|
20
|
+
end
|
21
|
+
|
22
|
+
def configure_middleware_stack(builder)
|
23
|
+
FlowChat.logger.debug { "Http::Processor: Configuring HTTP middleware stack" }
|
24
|
+
|
25
|
+
builder.use middleware
|
26
|
+
FlowChat.logger.debug { "Http::Processor: Added custom middleware" }
|
27
|
+
|
28
|
+
builder.use FlowChat::Http::Middleware::Executor
|
29
|
+
FlowChat.logger.debug { "Http::Processor: Added Http::Middleware::Executor" }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module FlowChat
|
2
|
+
module Http
|
3
|
+
class Renderer
|
4
|
+
attr_reader :prompt, :choices, :media
|
5
|
+
|
6
|
+
def initialize(prompt, choices: nil, media: nil)
|
7
|
+
@prompt = prompt
|
8
|
+
@choices = choices
|
9
|
+
@media = media
|
10
|
+
end
|
11
|
+
|
12
|
+
def render = build_response
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def build_response
|
17
|
+
{
|
18
|
+
message: prompt,
|
19
|
+
choices: format_choices,
|
20
|
+
media: format_media
|
21
|
+
}.compact
|
22
|
+
end
|
23
|
+
|
24
|
+
def format_choices
|
25
|
+
return unless choices.present?
|
26
|
+
|
27
|
+
choices.map { |key, value| { key: key, value: value } }
|
28
|
+
end
|
29
|
+
|
30
|
+
def format_media
|
31
|
+
return unless media.present?
|
32
|
+
|
33
|
+
{
|
34
|
+
url: media[:url] || media[:path],
|
35
|
+
type: media[:type] || :image,
|
36
|
+
caption: media[:caption]
|
37
|
+
}.compact
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -8,9 +8,11 @@ module FlowChat
|
|
8
8
|
def instrument(event_name, payload = {}, &block)
|
9
9
|
enriched_payload = payload&.dup || {}
|
10
10
|
if respond_to?(:context) && context
|
11
|
+
enriched_payload[:request_id] = context["request.id"] if context["request.id"]
|
11
12
|
enriched_payload[:session_id] = context["session.id"] if context["session.id"]
|
12
13
|
enriched_payload[:flow_name] = context["flow.name"] if context["flow.name"]
|
13
14
|
enriched_payload[:gateway] = context["request.gateway"] if context["request.gateway"]
|
15
|
+
enriched_payload[:platform] = context["request.platform"] if context["request.platform"]
|
14
16
|
end
|
15
17
|
|
16
18
|
self.class.instrument(event_name, enriched_payload, &block)
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module FlowChat
|
2
|
+
module PhoneNumberUtil
|
3
|
+
def self.to_e164(phone_number)
|
4
|
+
return phone_number if phone_number.nil? || phone_number.empty?
|
5
|
+
|
6
|
+
begin
|
7
|
+
# Try to load phonelib without Rails dependency
|
8
|
+
require_phonelib_safely
|
9
|
+
Phonelib.parse(phone_number).e164
|
10
|
+
rescue => e
|
11
|
+
FlowChat.logger.warn { "PhoneNumberUtil: Failed to parse phone number '#{phone_number}': #{e.message}" }
|
12
|
+
# Fallback to simple formatting if phonelib fails
|
13
|
+
fallback_e164_format(phone_number)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def self.require_phonelib_safely
|
20
|
+
return if defined?(Phonelib)
|
21
|
+
|
22
|
+
# Temporarily stub Rails if it doesn't exist
|
23
|
+
unless defined?(Rails)
|
24
|
+
stub_rails = Module.new do
|
25
|
+
def self.const_missing(name)
|
26
|
+
if name == :Railtie
|
27
|
+
Class.new
|
28
|
+
else
|
29
|
+
super
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
Object.const_set(:Rails, stub_rails)
|
34
|
+
require "phonelib"
|
35
|
+
Object.send(:remove_const, :Rails)
|
36
|
+
else
|
37
|
+
require "phonelib"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.fallback_e164_format(phone_number)
|
42
|
+
# Simple fallback - ensure it starts with + and looks like a phone number
|
43
|
+
cleaned = phone_number.to_s.gsub(/[^\d+]/, '')
|
44
|
+
cleaned.start_with?('+') ? cleaned : "+#{cleaned}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -111,23 +111,7 @@ module FlowChat
|
|
111
111
|
private
|
112
112
|
|
113
113
|
def session_key
|
114
|
-
|
115
|
-
|
116
|
-
gateway = @context["request.gateway"]
|
117
|
-
msisdn = @context["request.msisdn"]
|
118
|
-
|
119
|
-
key = case gateway
|
120
|
-
when :whatsapp_cloud_api
|
121
|
-
"flow_chat:session:whatsapp:#{msisdn}"
|
122
|
-
when :nalo, :nsano
|
123
|
-
session_id = @context["request.id"]
|
124
|
-
"flow_chat:session:ussd:#{session_id}:#{msisdn}"
|
125
|
-
else
|
126
|
-
"flow_chat:session:unknown:#{msisdn}"
|
127
|
-
end
|
128
|
-
|
129
|
-
FlowChat.logger.debug { "CacheSessionStore: Generated session key: #{key}" }
|
130
|
-
key
|
114
|
+
"flow_chat:cached_session:#{@context["session.id"]}"
|
131
115
|
end
|
132
116
|
|
133
117
|
def session_ttl
|
@@ -64,32 +64,33 @@ module FlowChat
|
|
64
64
|
end
|
65
65
|
|
66
66
|
def get_session_identifier(context)
|
67
|
-
identifier_type = @session_options.identifier
|
68
|
-
|
69
|
-
# If no identifier specified, use platform defaults
|
70
|
-
if identifier_type.nil?
|
71
|
-
platform = context["request.platform"]
|
72
|
-
identifier_type = case platform
|
73
|
-
when :ussd
|
74
|
-
:request_id # USSD defaults to ephemeral sessions
|
75
|
-
when :whatsapp
|
76
|
-
:msisdn # WhatsApp defaults to durable sessions
|
77
|
-
else
|
78
|
-
:msisdn # Default fallback to durable
|
79
|
-
end
|
80
|
-
end
|
67
|
+
identifier_type = @session_options.identifier || platform_default_identifier(context)
|
81
68
|
|
82
69
|
case identifier_type
|
83
70
|
when :request_id
|
84
71
|
context["request.id"]
|
72
|
+
when :user_id
|
73
|
+
user_id = context["request.user_id"]
|
74
|
+
@session_options.hash_identifiers ? hash_identifier(user_id) : user_id
|
85
75
|
when :msisdn
|
86
|
-
|
87
|
-
@session_options.
|
76
|
+
msisdn = context["request.msisdn"]
|
77
|
+
@session_options.hash_identifiers ? hash_identifier(msisdn) : msisdn
|
88
78
|
else
|
89
79
|
raise "Invalid session identifier type: #{identifier_type}"
|
90
80
|
end
|
91
81
|
end
|
92
82
|
|
83
|
+
def platform_default_identifier(context)
|
84
|
+
platform = context["request.platform"]
|
85
|
+
|
86
|
+
case platform
|
87
|
+
when :whatsapp
|
88
|
+
:msisdn
|
89
|
+
else
|
90
|
+
:request_id
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
93
94
|
def build_session_id(flow_name, platform, gateway, identifier)
|
94
95
|
parts = []
|
95
96
|
|
@@ -142,10 +143,10 @@ module FlowChat
|
|
142
143
|
url_identifier
|
143
144
|
end
|
144
145
|
|
145
|
-
def
|
146
|
+
def hash_identifier(identifier)
|
146
147
|
# Use SHA256 but only take first 8 characters for reasonable session IDs
|
147
148
|
require 'digest'
|
148
|
-
Digest::SHA256.hexdigest(
|
149
|
+
Digest::SHA256.hexdigest(identifier.to_s)[0, 8]
|
149
150
|
end
|
150
151
|
end
|
151
152
|
end
|
@@ -23,12 +23,12 @@ module FlowChat
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def default_config_key
|
26
|
-
|
26
|
+
:ussd
|
27
27
|
end
|
28
28
|
|
29
|
-
def
|
29
|
+
def configurations
|
30
30
|
{
|
31
|
-
|
31
|
+
ussd: {
|
32
32
|
name: "USSD (Nalo)",
|
33
33
|
description: "USSD integration using Nalo",
|
34
34
|
processor_type: "ussd",
|
@@ -41,7 +41,7 @@ module FlowChat
|
|
41
41
|
session_timeout: 300
|
42
42
|
}
|
43
43
|
},
|
44
|
-
|
44
|
+
whatsapp: {
|
45
45
|
name: "WhatsApp (Cloud API)",
|
46
46
|
description: "WhatsApp integration using Cloud API",
|
47
47
|
processor_type: "whatsapp",
|
@@ -53,6 +53,18 @@ module FlowChat
|
|
53
53
|
phone_number: default_phone_number,
|
54
54
|
contact_name: default_contact_name
|
55
55
|
}
|
56
|
+
},
|
57
|
+
http: {
|
58
|
+
name: "HTTP API",
|
59
|
+
description: "HTTP integration with JSON request/response",
|
60
|
+
processor_type: "http",
|
61
|
+
gateway: "http_simple",
|
62
|
+
endpoint: "/http/webhook",
|
63
|
+
icon: "🌐",
|
64
|
+
color: "#0066cc",
|
65
|
+
settings: {
|
66
|
+
user_id: default_phone_number
|
67
|
+
}
|
56
68
|
}
|
57
69
|
}
|
58
70
|
end
|
@@ -71,7 +83,7 @@ module FlowChat
|
|
71
83
|
default_phone_number: default_phone_number,
|
72
84
|
default_contact_name: default_contact_name,
|
73
85
|
default_config_key: default_config_key,
|
74
|
-
configurations:
|
86
|
+
configurations: configurations
|
75
87
|
}
|
76
88
|
end
|
77
89
|
|