X
X
Xip
Search…
Catch-Alls
Xip Catch-Alls are designed to handle the error cases within bots. Either the bot doesn't understand what a user typed or an error occurred. If this were a webpage, we'd see the dreaded HTTP 500 error page.
Catch-Alls are designed to move beyond simple "I don't understand" messages and help users get back on track. The better your CatchAlls, the better your bot.

Triggering

The catch_all flow is automatically triggered when either of two things happens within a controller action:
  1. 2.
    An Exception is raised.
If an action within CatchAllsController raises an exception, it won't fire another Catch-All to prevent loops.

Multi-Level

Xip keeps track of how many times a Catch-All is triggered for a given session. This allows you to build experiences in which the user is provided different responses for subsequent failures.
So for example, if in the hello flow and say_hello state an exception is raised, then Catch-All Level 1 will be called. If the user is return to that same flow and state and another exception is raised, Catch-All Level 2 will be called. This continues until you either run out of Catch-All states or if the Catch-All counter resets.
The Catch-All counter currently resets after 15 minutes. This is per flow and state. So a user may encounter a Catch-All elsewhere in your bot and it will utilize a separate Catch-All counter.

Retrying

By default, a Xip bot comes with Catch-All Level 1 already defined. Here is the default CatchAllsController and associated reply:
1
class CatchAllsController < BotController
2
3
def level1
4
send_replies
5
6
if fail_session.present?
7
step_to session: fail_session
8
else
9
step_to session: previous_session - 2.states
10
end
11
end
12
13
private
14
15
def fail_session
16
previous_session.flow.current_state.fails_to
17
end
18
19
end
Copied!
1
- reply_type: text
2
text: Oops. It looks like something went wrong. Let's try that again
Copied!
In the controller action, we check if the previous_session (the one that failed) specified a fails_to state. If so, we send the user there. Otherwise, we send the user back 2 states.
Sending a user back two states is a pretty good generic action. Going back 1 state takes us back to the action that failed. Since the actions most likely to fail are get actions, or actions that deal with user responses, going back 2 states usually takes us back to the original "question".
Where possible, it's better to specify a fails_to state so Xip doesn't incorrectly guess where to send your user back.

Adding More Levels

If you would like to extend the experience, add a level2 controller action and associated reply (and update the FlowMap). You can go as far as you want. CatchAlls have no limit, just make sure you increment using the standardized method names of level1, level2, level3, level4, etc.
If a user has encountered the maximum number of CatchAll levels that have been defined, it won't attempt to call any more levels.
For the last Catch-All state, you'll probably want to prompt the user to contact support or send them to a special menu to choose from.

Catch-All Reasons

As mentioned in the Triggering section above, there are two reasons a Catch-All triggers. Xip will provide the CatchAllsController with that reason so you can customize your messages and take the appropriate action.
So if for example your bot just didn't recognize the message sent by the user, you may ask the user to repeat. If however, your database is down, you might other action.
Here is an example usage:
1
class CatchAllsController < BotController
2
3
before_action :set_catch_all_reason
4
5
def level1
6
send_catch_all_replies('level1')
7
8
if fail_session.present?
9
step_to session: fail_session, pos: -1
10
else
11
step_to session: previous_session - 2.states, pos: -1
12
end
13
end
14
15
def level2
16
send_catch_all_replies('level2')
17
end
18
19
def level3
20
send_catch_all_replies('level3')
21
end
22
23
private
24
25
def fail_session
26
previous_session.flow.current_state.fails_to
27
end
28
29
def send_catch_all_replies(level)
30
if @reason == :unrecognized_message
31
send_replies(custom_reply: "catch_alls/#{level}_unrecognized")
32
else
33
send_replies(custom_reply: "catch_alls/#{level}")
34
end
35
end
36
37
def set_catch_all_reason
38
@reason = case current_message.catch_all_reason[:err].to_s
39
when 'Xip::Errors::UnrecognizedMessage'
40
:unrecognized_message
41
else
42
:system_error
43
end
44
end
45
46
end
47
Copied!
In this CatchAllsController we have two sets of Catch-All replies. One for when the message was unrecognized and another for when we've encountered a system error. We dynamically send the appropriate reply based on the @reason instance variable that we set with the before_action in the controller.