-
Notifications
You must be signed in to change notification settings - Fork 57
Controllers
Controllers are responsible for handling incoming requests and getting a response back to the user (replies).
The controller's methods, also referred to as actions, must be named after the flow's states. So for example, given the flow:
flow :onboard do
state :say_welcome
state :ask_for_phone
state :get_phone, fails_to: :ask_for_phone
end
The corresponding controller would be:
class OnboardsController < BotController
def say_welcome
end
def ask_for_phone
end
def get_phone
end
end
Every Stealth project comes with a default bot_controller.rb
:
class BotController < Stealth::Controller
before_action :current_user
def route
if current_session.present?
step_to session: current_session
else
step_to flow: 'hello', state: 'say_hello'
end
end
end
All of your controllers will inherit from BotController
:
class QuotesController < BotController
end
You can implement any method in BotController
. Typically you will implement methods like current_user
and methods for handling message payloads. The one method that BotController
must implement is the route
method.
The route
method is called for every incoming message. In its default implementation, the route
method checks whether the user already has a session, and if so routes them to that controller and action. If the user does not yet have a session, it will route them to the hello
flow and say_hello
action.
Stealth provides a few built-in methods to help you route a user through your bot.
The step_to
method is used to update the session and immediately move the user to the specified flow and/or state. step_to
can accept a flow, a state, or both. step_to
is often used after a say
action where the next action typically doesn't require user input.
Some examples of the different parameters:
step_to flow: 'hello'
- Sets the session's flow to hello
and the state will be set to the first state in that flow (as defined by the flow_map.rb
file). The corresponding controller action in the HellosController
would also be called.
step_to state: 'say_hello'
- Sets the session's state to say_hello
and keeps the flow the same. The say_hello
controller action would also be called.
step_to flow: 'hello', state: 'say_hello'
- Sets the session's flow to hello
and the state to say_hello
. The say_hello
controller action of the HellosController
controller would also be called.
Similar to step_to
, update_session_to
is used to update the user's session to a flow and/or state. It accepts the same parameters. However, update_session_to
does not immediately call the respective controller action. update_session_to
is typically used after an ask
action where the next action is waiting for user input. So by asking a user for input, then updating the session, it ensures the response the user sends back can be handled by the get
action.
Some examples of the different parameters:
update_session_to flow: 'quote'
- Sets the session's flow to quote
and the state will be set to the first state in that flow (as defined by the flow_map.rb
file).
update_session_to state: 'ask_zip_code'
- Sets the session's state to ask_zip_code
and keeps the flow the same.
step_to flow: 'quote', state: 'ask_zip_code'
- Sets the session's flow to quote
and the state to ask_zip_code
.
send_replies
will instruct the Reply
to construct the reply and transmit them. Not all of your controller actions will send replies. Typically in get
action, you'll get a user's response, perform some action, and then send a user to a new state without replying.
The send_replies
method does not take any parameters:
class ContactsController < BotController
def say_contact_us
send_replies
end
end
This would render the reply contained in replies/contacts/say_contact_us.yml
. See Reply Variants for additional naming options.
In addition to calling send_replies
without arguments, starting with Stealth 2.2.0, send_replies
supports to additional arguments: inline
and custom_reply
.
Inline replies allow you to specify the reply to send inline, within the controller. This can be useful if, for example, you store your replies in a database rather than the filesystem. The replies should mimic the structure of the replies YAML file, which is to say it should be an Array of replies like this:
reply = [
{ 'reply_type' => 'text', 'text' => 'Hi, Morty. Welcome to Stealth bot...' },
{ 'reply_type' => 'delay', 'duration' => 2 },
{ 'reply_type' => 'text', 'text' => 'We offer users an awesome Ruby framework for building chat bots.' }
]
Inline replies do not support preprocessing (ERB). You'll have to preprocess any strings before sending the inline reply. To send the inline reply from above:
send_replies inline: reply
If you'd like to specify a different reply file to send rather than the one that Stealth will select by default, you can do so with the custom_reply
argument:
def say_custom_reply
send_replies custom_reply: 'messages/say_offer'
end
This will tell Stealth to look in the bot/replies/messages
directory for the reply. Inside of that directory, it will look for say_offer.yml
, say_offer.yml.erb
and also any service specific variants. Stealth will select the right reply using the same heuristic as it does for normal replies.
The step_to_in
method is similar to step_to
. The only difference is that instead of calling the respective controller action immediately, it calls it after a specified duration. It can also take a flow, state, or both.
For example:
step_to_in 8.hours, flow: 'hello', state: 'say_hello'
This will set the user's session to the hello
flow and say_hello
state in 8 hours after being called. It will then immediately call that responsible controller action.
The step_to_at
method is similar to step_to
. The only difference is that instead of calling the respective controller action immediately, it calls it at a specific date and time. It can also take a flow, state, or both.
For example:
step_to_at DateTime.strptime("01/23/23 20:23", "%m/%d/%y %H:%M"), flow: 'hello', state: 'say_hello'
This will set the user's session to the hello
flow and say_hello
state on Jan 23, 2023 @ 20:23
. It will then immediately call that responsible controller action.
set_back_to
serves a similar purpose as previous_session
, however, instead of being automatically set by Stealth set_back_to
is completely user controlled. It's useful when paired with step_back
.
It takes the same parameters as step_to
:
set_back_to state: :say_hello
set_back_to flow: :hello, state: :say_hello
set_back_to flow: session: previous_session
All three commands above are valid and will store the back_to
session as it is called. This will allow you to reference it later like in cases where a user has been sent to a multi-state flow and needs to "return back" to the state specified by set_back_to
.
Used in conjunction with set_back_to
, step_back
will step the user to the flow and state specified by set_back_to
. If a back_to
session has not been set before step_back
is called, Stealth will raise a Stealth::Errors::InvalidStateTransition
exception.
This method is available as a convenience for when you do not wish to send a reply nor send the user to another state. It's useful for states that trap the user (like a reminder or the last level of your catch_all
flow).
For example:
def level3
do_nothing
end
Within each controller action, you have access to a few objects containing information about the session and the message the being processed.
The user's session is available to you at anytime using current_session
. This is a Stealth::Session
object. It has a few notable methods:
flow_string
: Returns the name of the flow.
state_string
: Returns the name of the state.
current_session + 2.states
: Returns a new session object 2 states after the current state. If we've passed the last state, the last state is returned.
current_session - 2.states
: Returns a new session object 2 states before the current state. If we've passed the first state, the first state is returned.
The current message being processed is available to you at anytime using current_message
. This is a Stealth::ServiceMessage
object. It has a few notable methods:
sender_id
: The ID of the user sending the message. This will vary based on the service integration.
target_id
: The ID of the target. This will vary based on the service sending the message, but for Facebook it will be the page_id
of the Facebook page receiving the message and for SMS will be the number receiving the SMS message. For other services, this may be nil
.
timestamp
: Ruby DateTime
object of when the message was transmitted.
service
: String indicating the service from where the message originated (i.e., 'facebook').
message
: String of the message contents.
payload
: This will vary by service integration.
location
: This will vary by service integration.
attachments
: This will vary by service integration.
referral
: This will vary by service integration.
This will be a string indicating the service from where the message originated (i.e., 'facebook' or 'twilio')
Returns true
or false
depending on whether or not the current_message
contains location data.
Returns true
or false
depending on whether or not the current_message
contains attachments.
A convenience method for accessing the session's ID.