flow_chat 0.7.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -0
- data/docs/configuration.md +29 -6
- data/docs/sessions.md +433 -0
- data/docs/testing.md +2 -2
- data/docs/ussd-setup.md +20 -4
- data/examples/simulator_controller.rb +4 -4
- data/examples/ussd_controller.rb +9 -3
- data/lib/flow_chat/base_app.rb +74 -0
- data/lib/flow_chat/base_executor.rb +57 -0
- data/lib/flow_chat/base_processor.rb +33 -2
- data/lib/flow_chat/config.rb +26 -5
- data/lib/flow_chat/instrumentation/setup.rb +0 -2
- data/lib/flow_chat/interrupt.rb +6 -0
- data/lib/flow_chat/session/middleware.rb +102 -17
- data/lib/flow_chat/simulator/controller.rb +2 -2
- data/lib/flow_chat/simulator/views/simulator.html.erb +5 -5
- data/lib/flow_chat/ussd/app.rb +1 -53
- data/lib/flow_chat/ussd/gateway/nalo.rb +1 -0
- data/lib/flow_chat/ussd/gateway/nsano.rb +3 -2
- data/lib/flow_chat/ussd/middleware/executor.rb +11 -37
- data/lib/flow_chat/ussd/processor.rb +5 -7
- data/lib/flow_chat/version.rb +1 -1
- data/lib/flow_chat/whatsapp/app.rb +11 -46
- data/lib/flow_chat/whatsapp/gateway/cloud_api.rb +1 -0
- data/lib/flow_chat/whatsapp/middleware/executor.rb +11 -39
- data/lib/flow_chat/whatsapp/processor.rb +0 -2
- data/lib/flow_chat.rb +1 -11
- metadata +5 -3
- data/lib/flow_chat/ussd/middleware/resumable_session.rb +0 -39
@@ -160,6 +160,7 @@ module FlowChat
|
|
160
160
|
|
161
161
|
context["request.id"] = phone_number
|
162
162
|
context["request.gateway"] = :whatsapp_cloud_api
|
163
|
+
context["request.platform"] = :whatsapp
|
163
164
|
context["request.message_id"] = message_id
|
164
165
|
context["request.msisdn"] = Phonelib.parse(phone_number).e164
|
165
166
|
context["request.contact_name"] = contact_name
|
@@ -1,49 +1,21 @@
|
|
1
|
+
require_relative "../../base_executor"
|
2
|
+
|
1
3
|
module FlowChat
|
2
4
|
module Whatsapp
|
3
5
|
module Middleware
|
4
|
-
class Executor
|
5
|
-
|
6
|
-
@app = app
|
7
|
-
FlowChat.logger.debug { "Whatsapp::Executor: Initialized WhatsApp executor middleware" }
|
8
|
-
end
|
9
|
-
|
10
|
-
def call(context)
|
11
|
-
flow_class = context.flow
|
12
|
-
action = context["flow.action"]
|
13
|
-
session_id = context["session.id"]
|
14
|
-
|
15
|
-
FlowChat.logger.info { "Whatsapp::Executor: Executing flow #{flow_class.name}##{action} for session #{session_id}" }
|
6
|
+
class Executor < FlowChat::BaseExecutor
|
7
|
+
protected
|
16
8
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
flow = flow_class.new whatsapp_app
|
21
|
-
FlowChat.logger.debug { "Whatsapp::Executor: Flow instance created, invoking #{action} method" }
|
22
|
-
|
23
|
-
flow.send action
|
24
|
-
FlowChat.logger.warn { "Whatsapp::Executor: Flow execution failed to interact with user for #{flow_class.name}##{action}" }
|
25
|
-
raise FlowChat::Interrupt::Terminate, "Unexpected end of flow."
|
26
|
-
rescue FlowChat::Interrupt::Prompt => e
|
27
|
-
FlowChat.logger.info { "Whatsapp::Executor: Flow prompted user - Session: #{session_id}, Prompt: '#{e.prompt.truncate(100)}'" }
|
28
|
-
FlowChat.logger.debug { "Whatsapp::Executor: Prompt details - Choices: #{e.choices&.size || 0}, Has media: #{!e.media.nil?}" }
|
29
|
-
# Return the same triplet format as USSD for consistency
|
30
|
-
[:prompt, e.prompt, e.choices, e.media]
|
31
|
-
rescue FlowChat::Interrupt::Terminate => e
|
32
|
-
FlowChat.logger.info { "Whatsapp::Executor: Flow terminated - Session: #{session_id}, Message: '#{e.prompt.truncate(100)}'" }
|
33
|
-
FlowChat.logger.debug { "Whatsapp::Executor: Destroying session #{session_id}" }
|
34
|
-
# Clean up session and return terminal message
|
35
|
-
context.session.destroy
|
36
|
-
[:terminate, e.prompt, nil, e.media]
|
37
|
-
rescue => error
|
38
|
-
FlowChat.logger.error { "Whatsapp::Executor: Flow execution failed - #{flow_class.name}##{action}, Session: #{session_id}, Error: #{error.class.name}: #{error.message}" }
|
39
|
-
FlowChat.logger.debug { "Whatsapp::Executor: Stack trace: #{error.backtrace.join("\n")}" }
|
40
|
-
raise
|
9
|
+
def platform_name
|
10
|
+
"WhatsApp"
|
41
11
|
end
|
42
12
|
|
43
|
-
|
13
|
+
def log_prefix
|
14
|
+
"Whatsapp::Executor"
|
15
|
+
end
|
44
16
|
|
45
|
-
def
|
46
|
-
FlowChat.logger.debug { "
|
17
|
+
def build_platform_app(context)
|
18
|
+
FlowChat.logger.debug { "#{log_prefix}: Building WhatsApp app instance" }
|
47
19
|
FlowChat::Whatsapp::App.new(context)
|
48
20
|
end
|
49
21
|
end
|
@@ -20,8 +20,6 @@ module FlowChat
|
|
20
20
|
|
21
21
|
def configure_middleware_stack(builder)
|
22
22
|
FlowChat.logger.debug { "Whatsapp::Processor: Configuring WhatsApp middleware stack" }
|
23
|
-
builder.use FlowChat::Session::Middleware
|
24
|
-
FlowChat.logger.debug { "Whatsapp::Processor: Added Session::Middleware" }
|
25
23
|
|
26
24
|
builder.use middleware
|
27
25
|
FlowChat.logger.debug { "Whatsapp::Processor: Added custom middleware" }
|
data/lib/flow_chat.rb
CHANGED
@@ -15,7 +15,6 @@ module FlowChat
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def self.setup_instrumentation!
|
18
|
-
require_relative "flow_chat/instrumentation/setup"
|
19
18
|
FlowChat::Instrumentation::Setup.setup_instrumentation!
|
20
19
|
end
|
21
20
|
|
@@ -27,13 +26,4 @@ module FlowChat
|
|
27
26
|
def self.metrics
|
28
27
|
FlowChat::Instrumentation::Setup.metrics_collector
|
29
28
|
end
|
30
|
-
end
|
31
|
-
|
32
|
-
loader.eager_load
|
33
|
-
|
34
|
-
# Auto-setup instrumentation in Rails environments
|
35
|
-
if defined?(Rails)
|
36
|
-
Rails.application.config.after_initialize do
|
37
|
-
FlowChat.setup_instrumentation!
|
38
|
-
end
|
39
|
-
end
|
29
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flow_chat
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stefan Froelich
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-06-
|
11
|
+
date: 2025-06-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: zeitwerk
|
@@ -107,6 +107,7 @@ files:
|
|
107
107
|
- docs/images/simulator.png
|
108
108
|
- docs/instrumentation.md
|
109
109
|
- docs/media.md
|
110
|
+
- docs/sessions.md
|
110
111
|
- docs/testing.md
|
111
112
|
- docs/ussd-setup.md
|
112
113
|
- docs/whatsapp-setup.md
|
@@ -118,6 +119,8 @@ files:
|
|
118
119
|
- examples/whatsapp_message_job.rb
|
119
120
|
- flow_chat.gemspec
|
120
121
|
- lib/flow_chat.rb
|
122
|
+
- lib/flow_chat/base_app.rb
|
123
|
+
- lib/flow_chat/base_executor.rb
|
121
124
|
- lib/flow_chat/base_processor.rb
|
122
125
|
- lib/flow_chat/config.rb
|
123
126
|
- lib/flow_chat/context.rb
|
@@ -139,7 +142,6 @@ files:
|
|
139
142
|
- lib/flow_chat/ussd/middleware/choice_mapper.rb
|
140
143
|
- lib/flow_chat/ussd/middleware/executor.rb
|
141
144
|
- lib/flow_chat/ussd/middleware/pagination.rb
|
142
|
-
- lib/flow_chat/ussd/middleware/resumable_session.rb
|
143
145
|
- lib/flow_chat/ussd/processor.rb
|
144
146
|
- lib/flow_chat/ussd/renderer.rb
|
145
147
|
- lib/flow_chat/version.rb
|
@@ -1,39 +0,0 @@
|
|
1
|
-
module FlowChat
|
2
|
-
module Ussd
|
3
|
-
module Middleware
|
4
|
-
class ResumableSession
|
5
|
-
def initialize(app)
|
6
|
-
@app = app
|
7
|
-
end
|
8
|
-
|
9
|
-
def call(context)
|
10
|
-
if FlowChat::Config.ussd.resumable_sessions_enabled && context["ussd.request"].present?
|
11
|
-
# First, try to find any interruption session.
|
12
|
-
# The session key can be:
|
13
|
-
# - a global session (key: "global")
|
14
|
-
# - a provider-specific session (key: <provider>)
|
15
|
-
session_key = self.class.session_key(context)
|
16
|
-
resumable_session = context["session.store"].get(session_key)
|
17
|
-
|
18
|
-
if resumable_session.present? && valid?(resumable_session)
|
19
|
-
context.merge! resumable_session
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
@app.call(context)
|
24
|
-
end
|
25
|
-
|
26
|
-
private
|
27
|
-
|
28
|
-
def valid?(session)
|
29
|
-
return true unless FlowChat::Config.ussd.resumable_sessions_timeout_seconds
|
30
|
-
|
31
|
-
last_active_at = Time.parse session.dig("context", "last_active_at")
|
32
|
-
(Time.current - FlowChat::Config.ussd.resumable_sessions_timeout_seconds) < last_active_at
|
33
|
-
rescue
|
34
|
-
false
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|