lib/socrates/core/dispatcher.rb in socrates-0.1.12 vs lib/socrates/core/dispatcher.rb in socrates-0.1.13
- old
+ new
@@ -21,38 +21,42 @@
end
def dispatch(message, context: {})
client_id = @adapter.client_id_from(context: context)
channel = @adapter.channel_from(context: context)
+ user = @adapter.user_from(context: context)
- do_dispatch(message, client_id, channel)
+ do_dispatch(message, client_id, channel, user)
end
- def start_conversation(user, state_id)
+ def start_conversation(user, state_id, message: nil)
client_id = @adapter.client_id_from(user: user)
channel = @adapter.channel_from(user: user)
- return false unless conversation_state(user).nil?
+ # Now, we assume the user of this code does this check on their own...
+ # return false unless conversation_state(user).nil?
# Create state data to match the request.
state_data = Socrates::Core::StateData.new(state_id: state_id, state_action: :ask)
persist_state_data(client_id, state_data)
- do_dispatch(nil, client_id, channel)
- true
+ # Send our initial message if one was passed to us.
+ @adapter.send_direct_message(message, user) if message.present?
+
+ do_dispatch(nil, client_id, channel, user)
end
def conversation_state(user)
client_id = @adapter.client_id_from(user: user)
return nil unless @storage.has_key?(client_id)
begin
snapshot = @storage.get(client_id)
state_data = StateData.deserialize(snapshot)
- state_data = nil if state_data_expired?(state_data)
+ state_data = nil if state_data.expired? || state_data.finished?
rescue => e
@logger.warn "Error while fetching state_data for client id '#{client_id}'."
@logger.warn e
state_data = nil
end
@@ -62,20 +66,20 @@
private
DEFAULT_ERROR_MESSAGE = "Sorry, an error occurred. We'll have to start over..."
- def do_dispatch(message, client_id, channel)
+ def do_dispatch(message, client_id, channel, user)
message = message&.strip
@logger.info %Q(#{client_id} recv: "#{message}")
# In many cases, a two actions will run in this loop: :listen => :ask, but it's possible that a chain of 2 or
# more :ask actions could run, before stopping at a :listen (and waiting for the next input).
loop do
state_data = fetch_state_data(client_id)
- state = instantiate_state(state_data, channel)
+ state = instantiate_state(state_data, channel, user)
args = [state.data.state_action]
args << message if state.data.state_action == :listen
msg = "#{client_id} processing :#{state.data.state_id} / :#{args.first}"
@@ -98,10 +102,12 @@
persist_state_data(client_id, state.data)
# Break from the loop if there's nothing left to do, i.e. no more state transitions.
break if done_transitioning?(state)
end
+
+ true
end
# rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
def fetch_state_data(client_id)
if @storage.has_key?(client_id)
@@ -116,18 +122,18 @@
state_data ||= StateData.new
# If the current state is nil or END_OF_CONVERSATION, set it to the default state, which is typically a state
# that waits for an initial command or input from the user (e.g. help, start, etc).
- if state_data.state_id.nil? || state_data.state_id == State::END_OF_CONVERSATION
+ if state_data.state_id.nil? || state_data.state_id == StateData::END_OF_CONVERSATION
default_state, default_action = @state_factory.default
state_data.state_id = default_state
state_data.state_action = default_action || :listen
# Check to see if the last interation was too long ago.
- elsif state_data_expired?(state_data) && @state_factory.expired(state_data).present?
+ elsif state_data.expired? && @state_factory.expired(state_data).present?
expired_state, expired_action = @state_factory.expired(state_data)
state_data.state_id = expired_state
state_data.state_action = expired_action || :ask
end
@@ -139,25 +145,19 @@
def persist_state_data(client_id, state_data)
state_data.reset_elapsed_time
@storage.put(client_id, state_data.serialize)
end
- def state_data_expired?(state_data)
- return false unless state_data.last_interaction_timestamp.present?
-
- state_data.elapsed_time > (Socrates.config.expired_timeout || 30.minutes)
+ def instantiate_state(state_data, channel, user)
+ @state_factory.build(state_data: state_data, adapter: @adapter, channel: channel, user: user)
end
- def instantiate_state(state_data, channel)
- @state_factory.build(state_data: state_data, adapter: @adapter, channel: channel)
- end
-
def done_transitioning?(state)
# Stop transitioning if we're waiting for the user to respond (i.e. we're listening).
return true if state.data.state_action == :listen
# Stop transitioning if there's no state to transition to, or the conversation has ended.
- state.data.state_id.nil? || state.data.state_id == State::END_OF_CONVERSATION
+ state.data.state_id.nil? || state.data.state_id == StateData::END_OF_CONVERSATION
end
def handle_action_error(e, client_id, state, channel)
@logger.warn "Error while processing action #{state.data.state_id}/#{state.data.state_action}: #{e.message}"
@logger.warn e