stealth 2.0.0.beta5 → 2.0.0.beta7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitbook.yaml +1 -0
- data/.github/dependabot.yml +11 -0
- data/.github/workflows/ci.yml +50 -0
- data/CHANGELOG.md +2 -0
- data/Gemfile.lock +38 -40
- data/MAINTENANCE.md +28 -0
- data/README.md +1 -0
- data/SECURITY.md +13 -0
- data/VERSION +1 -1
- data/docs/.gitbook/assets/2880px-Turnstile_state_machine_colored.svg.png +0 -0
- data/docs/.gitbook/assets/Torniqueterevolution.jpg +0 -0
- data/docs/.gitbook/assets/logo.svg +28 -0
- data/docs/.gitbook/assets/ruby.png +0 -0
- data/docs/README.md +35 -0
- data/docs/SUMMARY.md +99 -0
- data/docs/basics.md +77 -0
- data/docs/building-components/message-services.md +7 -0
- data/docs/building-components/nlp.md +7 -0
- data/docs/config/services.yml.md +2 -0
- data/docs/config/settings.md +2 -0
- data/docs/controllers/available-data.md +77 -0
- data/docs/controllers/catch-alls.md +135 -0
- data/docs/controllers/controller-overview.md +130 -0
- data/docs/controllers/dev-jumps.md +47 -0
- data/docs/controllers/get_match/README.md +2 -0
- data/docs/controllers/get_match/alpha-ordinals.md +2 -0
- data/docs/controllers/get_match/entity-match.md +2 -0
- data/docs/controllers/get_match/exact-match.md +2 -0
- data/docs/controllers/handle_message/README.md +54 -0
- data/docs/controllers/handle_message/alpha-ordinal-matcher.md +40 -0
- data/docs/controllers/handle_message/homophone-detection.md +36 -0
- data/docs/controllers/handle_message/nil-matcher.md +44 -0
- data/docs/controllers/handle_message/nlp-matcher.md +51 -0
- data/docs/controllers/handle_message/regex-matcher.md +41 -0
- data/docs/controllers/handle_message/string-mather.md +23 -0
- data/docs/controllers/interrupt-detection.md +2 -0
- data/docs/controllers/platform-errors.md +2 -0
- data/docs/controllers/route.md +70 -0
- data/docs/controllers/sessions/README.md +2 -0
- data/docs/controllers/sessions/do_nothing.md +17 -0
- data/docs/controllers/sessions/intro.md +37 -0
- data/docs/controllers/sessions/step_back.md +88 -0
- data/docs/controllers/sessions/step_to.md +47 -0
- data/docs/controllers/sessions/step_to_at.md +43 -0
- data/docs/controllers/sessions/step_to_in.md +43 -0
- data/docs/controllers/sessions/update_session_to.md +47 -0
- data/docs/controllers/unrecognized-messages.md +2 -0
- data/docs/deployment/heroku.md +2 -0
- data/docs/deployment/overview.md +2 -0
- data/docs/dev-environment/README.md +2 -0
- data/docs/dev-environment/booting-up.md +33 -0
- data/docs/dev-environment/environment-variables.md +54 -0
- data/docs/dev-environment/hot-code-reloading.md +52 -0
- data/docs/dev-environment/logs.md +74 -0
- data/docs/dev-environment/procfile.md +22 -0
- data/docs/dev-environment/tunnels.md +18 -0
- data/docs/flows/flowmap.md +40 -0
- data/docs/flows/overview.md +43 -0
- data/docs/flows/state-naming.md +53 -0
- data/docs/flows/state-options.md +70 -0
- data/docs/getting-started.md +19 -0
- data/docs/glossary.md +21 -0
- data/docs/models/activerecord.md +2 -0
- data/docs/models/mongoid.md +2 -0
- data/docs/models/overview.md +2 -0
- data/docs/nlp-nlu/microsoft-luis.md +2 -0
- data/docs/nlp-nlu/openai.md +2 -0
- data/docs/nlp-nlu/overview.md +2 -0
- data/docs/platforms/alexa-skills.md +2 -0
- data/docs/platforms/facebook-messenger.md +2 -0
- data/docs/platforms/overview.md +2 -0
- data/docs/platforms/sms-whatsapp.md +2 -0
- data/docs/platforms/voice.md +2 -0
- data/docs/replies/delays.md +2 -0
- data/docs/replies/erb.md +2 -0
- data/docs/replies/inline-replies.md +2 -0
- data/docs/replies/reply-overview.md +2 -0
- data/docs/replies/variants.md +2 -0
- data/docs/replies/yaml-replies.md +2 -0
- data/docs/testing/integration-testing.md +2 -0
- data/docs/testing/untitled.md +2 -0
- data/lib/stealth/base.rb +1 -1
- data/lib/stealth/controller/replies.rb +11 -5
- data/lib/stealth/server.rb +10 -1
- data/spec/controller/replies_spec.rb +48 -0
- data/spec/replies/messages/say_simple_hello.yml +2 -0
- data/stealth.gemspec +2 -3
- metadata +87 -23
- data/.circleci/config.yml +0 -226
@@ -0,0 +1,70 @@
|
|
1
|
+
---
|
2
|
+
description: Describes the route method in BotController.
|
3
|
+
---
|
4
|
+
|
5
|
+
# route
|
6
|
+
|
7
|
+
The `route` method in `BotController` is the primary entry point for messages into your bot. This method is left open for you to edit in order for you to customize that process. Here is the method after having been generated:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
def route
|
11
|
+
if current_message.payload.present?
|
12
|
+
handle_payloads
|
13
|
+
# Clear out the payload to prevent duplicate handling
|
14
|
+
current_message.payload = nil
|
15
|
+
return
|
16
|
+
end
|
17
|
+
|
18
|
+
# Allow devs to jump around flows and states by typing:
|
19
|
+
# /flow_name/state_name or
|
20
|
+
# /flow_name (jumps to first state) or
|
21
|
+
# //state_name (jumps to state in current flow)
|
22
|
+
# (only works for bots in development)
|
23
|
+
return if dev_jump_detected?
|
24
|
+
|
25
|
+
if current_session.present?
|
26
|
+
step_to session: current_session
|
27
|
+
else
|
28
|
+
step_to flow: 'hello', state: 'say_hello'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
```
|
32
|
+
|
33
|
+
The method, by default, performs three tasks:
|
34
|
+
|
35
|
+
1. Handles payloads.
|
36
|
+
2. Handles [Dev Jumps](dev-jumps.md).
|
37
|
+
3. Routes a user to their existing location based on their session or starts a new session if one does not already exist.
|
38
|
+
|
39
|
+
We'll cover 1 and 3 in more detail below. You can learn more about Dev Jumps via the [Dev Jumps docs](dev-jumps.md).
|
40
|
+
|
41
|
+
### Payloads
|
42
|
+
|
43
|
+
Payloads are used to handle things like buttons in Facebook Messenger. On other platforms, like SMS and Whatsapp, payloads might be global keywords your bot is configured to support.
|
44
|
+
|
45
|
+
Payloads have to be handled globally because a button may be tapped (or keyword typed in) at any point during a conversation. Your bot, therefore, needs to be able to handle these in any flow and state.
|
46
|
+
|
47
|
+
Line 2 in the code sample above checks if the payload field of a message is present, and if so, calls the `handle_payloads` method. Here is that method:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
# Handle payloads globally since payload buttons remain in the chat
|
51
|
+
# and we cannot guess in which states they will be tapped.
|
52
|
+
def handle_payloads
|
53
|
+
case current_message.payload
|
54
|
+
when 'developer_restart', 'new_user'
|
55
|
+
step_to flow: 'hello', state: 'say_hello'
|
56
|
+
when 'goodbye'
|
57
|
+
step_to flow: 'goodbye'
|
58
|
+
end
|
59
|
+
end
|
60
|
+
```
|
61
|
+
|
62
|
+
By setting the payload value of a Facebook Messenger button to `developer_restart`, for example, you can trigger the conversation to restart.
|
63
|
+
|
64
|
+
{% hint style="info" %}
|
65
|
+
More information for handling global SMS or Whatsapp keywords and button payloads can be found in the respective documentation for each message platform component.
|
66
|
+
{% endhint %}
|
67
|
+
|
68
|
+
### Routing Based on Session
|
69
|
+
|
70
|
+
In the first code sample, Lines 16-20 handle routing a user to their existing session or starting a new one. For users with an existing session, you'll likely want to keep that code the same. For users without session, you may want to customize the starting flow.
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# do\_nothing
|
2
|
+
|
3
|
+
This method is available for overriding the default behavior of Stealth which fires a [CatchAll](../catch-alls.md) in cases where a controller action fails to the update a session or send replies. See the documentation on [failing to progress a user](../controller-overview.md#failing-to-progress-a-user) for more information.
|
4
|
+
|
5
|
+
It's primarily used in states that "trap" the user (like your bot's last state or the last level of your `catch_all` flow).
|
6
|
+
|
7
|
+
It's usage is straightforward:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
def
|
11
|
+
do_nothing
|
12
|
+
end
|
13
|
+
```
|
14
|
+
|
15
|
+
{% hint style="info" %}
|
16
|
+
You might also use `do_nothing` within an if/else block.
|
17
|
+
{% endhint %}
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# Session Overview
|
2
|
+
|
3
|
+
Sessions in Stealth allow your bot to maintain a state for each user. If you come from the web development world, they are very similar to HTTP sessions. If you don't come from that world, no worries, we'll explain how sessions work without needing to know. If you haven't yet read the primer on [Flows and States](../../flows/overview.md), we recommend you do that first. 
|
4
|
+
|
5
|
+
### How are sessions stored?
|
6
|
+
|
7
|
+
Sessions in Stealth are backed by Redis. Each user interacting with your bot has a unique ID assigned by the messaging platform that identifies them. With Facebook Messenger, it's a page-scoped ID (PSID). With SMS and Whatsapp it's a phone number. These unique IDs are used as the key in Redis.
|
8
|
+
|
9
|
+
Regardless of what it is, it allows Stealth to find and load a user's session each time a message is received.
|
10
|
+
|
11
|
+
#### Session Slugs
|
12
|
+
|
13
|
+
With the unique messaging platform ID used as the Redis key, the value is the session slug. This is a canonical string that represents a user's current flow and state. A session slug looks like this: `flow->state`. So if a user's session is currently pointing to the `hello` flow and the `say_hola` state, then the slug would be `hello->say_hola`.
|
14
|
+
|
15
|
+
{% hint style="info" %}
|
16
|
+
If a user has not interacted with your bot before, the key will therefore be `nil` indicating there is no session for the user.
|
17
|
+
{% endhint %}
|
18
|
+
|
19
|
+
#### Session Expiration
|
20
|
+
|
21
|
+
By default, sessions do not expire. This is however configurable as a [setting](../../config/settings.md).
|
22
|
+
|
23
|
+
{% hint style="warning" %}
|
24
|
+
If your bot sends re-engagements, make sure your session's expiration is set to be _after_ the last re-engagement message is sent.
|
25
|
+
{% endhint %}
|
26
|
+
|
27
|
+
#### Previous Session
|
28
|
+
|
29
|
+
In addition to storing a user's current session, Stealth also automatically maintains a copy of the previous session. This allows you to send a user "back" from a catch-all scenario.
|
30
|
+
|
31
|
+
{% hint style="info" %}
|
32
|
+
Previous sessions also expire at the same time as primary sessions.
|
33
|
+
{% endhint %}
|
34
|
+
|
35
|
+
#### State Transitions
|
36
|
+
|
37
|
+
State transitions are performed via [step\_to](step\_to.md), [step\_to\_in](step\_to\_in.md), [step\_to\_at](step\_to\_at.md), [step\_back](step\_back.md), and [update\_session\_to](update\_session\_to.md). You can also manually manipulate the key in Redis, but these are currently the only ways to alter a user's session via Stealth.
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# step\_back
|
2
|
+
|
3
|
+
Used in conjunction with `set_back_to`, `step_back` will step the user to the flow and state specified by `set_back_to`. This is a useful feature when you're building shared flows. Like for example, if you ask a user for their name in multiple places throughout your bot. This allows you to keep your flows [DRY](https://en.m.wikipedia.org/wiki/Don't\_repeat\_yourself).
|
4
|
+
|
5
|
+
{% hint style="warning" %}
|
6
|
+
If a `back_to` session has not been set before `step_back` is called, Stealth will raise a `Stealth::Errors::InvalidStateTransition` exception.
|
7
|
+
{% endhint %}
|
8
|
+
|
9
|
+
## set\_back\_to
|
10
|
+
|
11
|
+
`set_back_to` serves a similar purpose as [previous\_session](intro.md#previous-session), however, instead of being automatically set by Stealth, `set_back_to` is user configurable.
|
12
|
+
|
13
|
+
It takes the same parameters as `step_to`:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
set_back_to state: 'say_hello'
|
17
|
+
set_back_to flow: 'hello', state: 'say_hello'
|
18
|
+
set_back_to session: previous_session
|
19
|
+
```
|
20
|
+
|
21
|
+
All three commands above are valid and will store the `back_to` session. 
|
22
|
+
|
23
|
+
{% hint style="info" %}
|
24
|
+
`back_to` sessions also expire along with the primary session and previous session (if an expiration has been set).
|
25
|
+
{% endhint %}
|
26
|
+
|
27
|
+
## Example
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
class DataHelpersController < BotController
|
31
|
+
|
32
|
+
def ask_for_email_address
|
33
|
+
send_replies
|
34
|
+
update_session_to state: :get_email_address
|
35
|
+
end
|
36
|
+
|
37
|
+
def get_email_address
|
38
|
+
unless message_is_a_valid_email?
|
39
|
+
step_to state: :say_invalid_email
|
40
|
+
return
|
41
|
+
end
|
42
|
+
|
43
|
+
current_user.store(email: current_message.message)
|
44
|
+
|
45
|
+
step_back
|
46
|
+
end
|
47
|
+
|
48
|
+
def say_invalid_email
|
49
|
+
send_replies
|
50
|
+
update_session_to state: :get_email_address
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
class QuestionsController < BotController
|
57
|
+
|
58
|
+
def ask_if_interested
|
59
|
+
send_replies
|
60
|
+
update_session_to state: :get_interest_response
|
61
|
+
end
|
62
|
+
|
63
|
+
def get_interest_response
|
64
|
+
handle_message(
|
65
|
+
:yes => proc {
|
66
|
+
set_back_to state: :say_thanks
|
67
|
+
step_to flow: :data_helper, state: :ask_for_email_address
|
68
|
+
},
|
69
|
+
:no => proc {
|
70
|
+
step_to state: :say_no_worries
|
71
|
+
}
|
72
|
+
)
|
73
|
+
end
|
74
|
+
|
75
|
+
def say_thanks
|
76
|
+
send_replies
|
77
|
+
end
|
78
|
+
|
79
|
+
def say_no_worries
|
80
|
+
send_replies
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
```
|
85
|
+
|
86
|
+
In the above example, we have two flows/controllers: `data_helper` and `question`. The `DataHelpersController` contains a few states for asking for an email address for the user and verifying that it looks like a legit email address. Setting it up this way allows any of our other controllers to also ask for an email address without having to duplicate these states.
|
87
|
+
|
88
|
+
On Lines 37-38, you see that we set the `back_to` session to be the `say_thanks` state. Then we step to the `data_helper` state directly via `step_to`. From the `DataHelpersController` we can continue to ask questions and update the states as needed like normal. Once we've collected the email address, we send the user "back" to the `back_to` session by calling `step_back` on Line 16.
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# step\_to
|
2
|
+
|
3
|
+
The `step_to` method is used to update the session and immediately move the user to the specified flow and 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.
|
4
|
+
|
5
|
+
{% hint style="warning" %}
|
6
|
+
If the flow and/or action specified in the `step_to` is not declared in the [FlowMap](../../flows/flowmap.md), Stealth will raise an exception.
|
7
|
+
{% endhint %}
|
8
|
+
|
9
|
+
## Flow Example
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
step_to flow: 'hello'
|
13
|
+
```
|
14
|
+
|
15
|
+
Sets the session's flow to `hello` and the state will be set to the **first** state in that flow (as defined by the [FlowMap](../../flows/flowmap.md)). The corresponding controller action in the `HellosController` will also be immediately called.
|
16
|
+
|
17
|
+
{% hint style="info" %}
|
18
|
+
The flow name can also be specified as a symbol.
|
19
|
+
{% endhint %}
|
20
|
+
|
21
|
+
## State Example
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
step_to state: 'say_hello'
|
25
|
+
```
|
26
|
+
|
27
|
+
Sets the session's state to `say_hello` and keeps the flow the same. The `say_hello` controller action will also be immediately called.
|
28
|
+
|
29
|
+
{% hint style="info" %}
|
30
|
+
The state name can also be specified as a symbol.
|
31
|
+
{% endhint %}
|
32
|
+
|
33
|
+
## Flow and State Example
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
step_to flow: :hello, state: :say_hello
|
37
|
+
```
|
38
|
+
|
39
|
+
Sets the session's flow to `hello` and the state to `say_hello`. The `say_hello` controller action of the `HellosController` controller will also be immediately called.
|
40
|
+
|
41
|
+
## Session Example
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
step_to session: previous_session
|
45
|
+
```
|
46
|
+
|
47
|
+
Sets the session to the `previous_session` and immediately calls the respective controller action. This is useful for sending a user "back".
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# step\_to\_at
|
2
|
+
|
3
|
+
The `step_to_at` method is used to update the session and move the user to the specified flow and state **at the specified date and time**. `step_to_at` can accept a _flow_, a _state_, or both. `step_to_at` is often used as a tool for re-engaging a user at a specific time.
|
4
|
+
|
5
|
+
{% hint style="info" %}
|
6
|
+
The session will only be updated and the controller action called **at the time specified**.
|
7
|
+
{% endhint %}
|
8
|
+
|
9
|
+
{% hint style="warning" %}
|
10
|
+
If the flow and/or action specified in the `step_to_at` is not declared in the [FlowMap](../../flows/flowmap.md) (at the specified date and time), Stealth will raise an exception.
|
11
|
+
{% endhint %}
|
12
|
+
|
13
|
+
## Flow Example
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
step_to_at Time.now.next_week, flow: 'hello'
|
17
|
+
```
|
18
|
+
|
19
|
+
At the specified time (next week in this case), Stealth will set the session's flow to `hello` and the state will be set to the **first** state in that flow (as defined by the [FlowMap](../../flows/flowmap.md)). The corresponding controller action in the `HellosController` will also be called.
|
20
|
+
|
21
|
+
{% hint style="info" %}
|
22
|
+
The flow name can also be specified as a symbol.
|
23
|
+
{% endhint %}
|
24
|
+
|
25
|
+
## State Example
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
step_to_at Time.now.next_week, state: 'say_hello'
|
29
|
+
```
|
30
|
+
|
31
|
+
At the specified time (next week in this case), Stealth will set the session's state to `say_hello` and keeps the flow the same. The `say_hello` controller action will also be called.
|
32
|
+
|
33
|
+
{% hint style="info" %}
|
34
|
+
The state name can also be specified as a symbol.
|
35
|
+
{% endhint %}
|
36
|
+
|
37
|
+
## Flow and State Example
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
step_to_at Time.now.next_week, flow: :hello, state: :say_hello
|
41
|
+
```
|
42
|
+
|
43
|
+
At the specified time (next week in this case), Stealth will set the session's flow to `hello` and the state to `say_hello`. The `say_hello` controller action of the `HellosController` controller will also be called.
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# step\_to\_in
|
2
|
+
|
3
|
+
The `step_to_in` method is used to update the session and move the user to the specified flow and state **after a specified duration**. `step_to_in` can accept a _flow_, a _state_, or both. `step_to_in` is often used as a tool for re-engaging a user after a specified duration.
|
4
|
+
|
5
|
+
{% hint style="info" %}
|
6
|
+
The session will only be updated and the controller action called **after the specified duration has elapsed**.
|
7
|
+
{% endhint %}
|
8
|
+
|
9
|
+
{% hint style="warning" %}
|
10
|
+
If the flow and/or action specified in the `step_to_in` is not declared in the [FlowMap](../../flows/flowmap.md) (after the specified duration), Stealth will raise an exception.
|
11
|
+
{% endhint %}
|
12
|
+
|
13
|
+
## Flow Example
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
step_to_in 8.hours, flow: 'hello'
|
17
|
+
```
|
18
|
+
|
19
|
+
After the specified duration (8 hours in this case), Stealth will set the session's flow to `hello` and the state will be set to the **first** state in that flow (as defined by the [FlowMap](../../flows/flowmap.md)). The corresponding controller action in the `HellosController` will also be called.
|
20
|
+
|
21
|
+
{% hint style="info" %}
|
22
|
+
The flow name can also be specified as a symbol.
|
23
|
+
{% endhint %}
|
24
|
+
|
25
|
+
## State Example
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
step_to_in 8.hours, state: 'say_hello'
|
29
|
+
```
|
30
|
+
|
31
|
+
After the specified duration (8 hours in this case), Stealth will set the session's state to `say_hello` and keeps the flow the same. The `say_hello` controller action will also be called.
|
32
|
+
|
33
|
+
{% hint style="info" %}
|
34
|
+
The state name can also be specified as a symbol.
|
35
|
+
{% endhint %}
|
36
|
+
|
37
|
+
## Flow and State Example
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
step_to_in 8.hours, flow: :hello, state: :say_hello
|
41
|
+
```
|
42
|
+
|
43
|
+
After the specified duration (8 hours in this case), Stealth will set the session's flow to `hello` and the state to `say_hello`. The `say_hello` controller action of the `HellosController` controller will also be called.
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# update\_session\_to
|
2
|
+
|
3
|
+
Similar to [step\_to](step\_to.md), `update_session_to` is used to update the user's session to a flow and state. It also accepts the same arguments. 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. It allows you to set the state that will be responsible for handling that user input, like a `get` action.
|
4
|
+
|
5
|
+
{% hint style="warning" %}
|
6
|
+
If the flow and/or action specified in the `update_session_to` is not declared in the [FlowMap](../../flows/flowmap.md), Stealth will raise an exception.
|
7
|
+
{% endhint %}
|
8
|
+
|
9
|
+
## Flow Example
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
update_session_to flow: 'hello'
|
13
|
+
```
|
14
|
+
|
15
|
+
Sets the session's flow to `hello` and the state will be set to the **first** state in that flow (as defined by the [FlowMap](../../flows/flowmap.md)). The corresponding controller action in the `HellosController` will **not** be called.
|
16
|
+
|
17
|
+
{% hint style="info" %}
|
18
|
+
The flow name can also be specified as a symbol.
|
19
|
+
{% endhint %}
|
20
|
+
|
21
|
+
## State Example
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
update_session_to state: 'get_hello_response'
|
25
|
+
```
|
26
|
+
|
27
|
+
Sets the session's state to `get_hello_response` and keeps the flow the same. The `get_hello_response` controller action will **not** called.
|
28
|
+
|
29
|
+
{% hint style="info" %}
|
30
|
+
The state name can also be specified as a symbol.
|
31
|
+
{% endhint %}
|
32
|
+
|
33
|
+
## Flow and State Example
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
step_to flow: :hello, state: :say_hello
|
37
|
+
```
|
38
|
+
|
39
|
+
Sets the session's flow to `hello` and the state to `say_hello`. The `say_hello` controller action of the `HellosController` controller will also be immediately called.
|
40
|
+
|
41
|
+
## Session Example
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
update_session_to session: previous_session
|
45
|
+
```
|
46
|
+
|
47
|
+
Sets the session to the `previous_session` and but does **not** call the respective controller action. This is useful for updating a user's session to the previous value.
|
@@ -0,0 +1,33 @@
|
|
1
|
+
---
|
2
|
+
description: Steps for booting up your bot.
|
3
|
+
---
|
4
|
+
|
5
|
+
# Booting Up
|
6
|
+
|
7
|
+
While you can use Docker or anything else to boot up your bot, there is a built-in command that utilizes [foreman](https://github.com/ddollar/foreman) to ensure your _web server_ and your _background job processor_ both boot up. If you boot only the web server bot the the background job processor, your bot will receive message but will fail to reply.
|
8
|
+
|
9
|
+
For more info about the different process types, check out [Anatomy of a Stealth bot](../basics.md#anatomy-of-a-stealth-bot).
|
10
|
+
|
11
|
+
### Install Gems
|
12
|
+
|
13
|
+
```
|
14
|
+
bundle install
|
15
|
+
```
|
16
|
+
|
17
|
+
### Boot Your Bot
|
18
|
+
|
19
|
+
```
|
20
|
+
stealth s
|
21
|
+
```
|
22
|
+
|
23
|
+
Or for the full command:
|
24
|
+
|
25
|
+
```
|
26
|
+
stealth server
|
27
|
+
```
|
28
|
+
|
29
|
+
### Tunnels to Localhost
|
30
|
+
|
31
|
+
After you boot your server, you'll likely want to use a service to create a tunnel to your localhost. This allows message platform like Facebook Messenger and Whatsapp to deliver messages to your laptop.
|
32
|
+
|
33
|
+
Check out the [docs for creating your own tunnel](tunnels.md).
|
@@ -0,0 +1,54 @@
|
|
1
|
+
---
|
2
|
+
description: Tips for streamlining your environment variable management during development.
|
3
|
+
---
|
4
|
+
|
5
|
+
# Environment Variables
|
6
|
+
|
7
|
+
Environment variables (ENV Vars) will likely be an important part of your configuration. Each message platform component, NLP component, and others will likely require one or more API keys. 
|
8
|
+
|
9
|
+
Most production environments will provide a way for you to specify your production keys. But for development, we recommend using the [dotenv](https://github.com/bkeepers/dotenv) gem. This gem will allow you to specify a `.env` file in your bot repo from where you can set all of your environment variables.
|
10
|
+
|
11
|
+
{% hint style="info" %}
|
12
|
+
Stealth will automatically exclude the `.env` file from git.
|
13
|
+
{% endhint %}
|
14
|
+
|
15
|
+
### Configuring dotenv in Stealth
|
16
|
+
|
17
|
+
Add the [dotenv](https://github.com/bkeepers/dotenv) gem to your `Gemfile`:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
group :development do
|
21
|
+
gem 'foreman'
|
22
|
+
gem 'listen', '~> 3.3'
|
23
|
+
gem 'dotenv'
|
24
|
+
end
|
25
|
+
```
|
26
|
+
|
27
|
+
Install the gem:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
bundle install
|
31
|
+
```
|
32
|
+
|
33
|
+
Load dotenv on boot via `boot.rb`:
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
require 'stealth/base'
|
37
|
+
if %w(development test).include?(Stealth.env)
|
38
|
+
require 'dotenv/load'
|
39
|
+
end
|
40
|
+
require_relative './environment'
|
41
|
+
```
|
42
|
+
|
43
|
+
{% hint style="info" %}
|
44
|
+
You'll be adding Lines 2-4 right below Line 1 which will already be there.
|
45
|
+
{% endhint %}
|
46
|
+
|
47
|
+
That's it! Now you can specify your environment variables via the `.env` file:
|
48
|
+
|
49
|
+
```
|
50
|
+
FACEBOOK_VERIFY_TOKEN=some_value
|
51
|
+
LUIS_APP_ID=1234
|
52
|
+
LUIS_ENDPOINT=your_endpoint.cognitiveservices.azure.com
|
53
|
+
LUIS_SUBSCRIPTION_KEY=xyz1234
|
54
|
+
```
|
@@ -0,0 +1,52 @@
|
|
1
|
+
---
|
2
|
+
description: ✨✨✨
|
3
|
+
---
|
4
|
+
|
5
|
+
# Hot-Code Reloading
|
6
|
+
|
7
|
+
Hot-code reloading is one of the more enjoyable features of using Stealth to create your bots. As you make changes to your bot, your source code is automatically loaded into memory without having to stop and start your bot!
|
8
|
+
|
9
|
+
There is nothing you need to turn on to start using this feature. As long as you are in the `development` [environment](../basics.md#environments). You may however wish to customize which files are watched for changes as you add your own custom directories, service objects, etc.
|
10
|
+
|
11
|
+
## Customizing Hot-Reload Paths
|
12
|
+
|
13
|
+
By default, these are paths and files that Stealth will watch for changes:
|
14
|
+
|
15
|
+
```
|
16
|
+
bot/controllers/conerns/*.*
|
17
|
+
bot/controllers/*.*
|
18
|
+
bot/models/concerns/*.*
|
19
|
+
bot/models/*.*
|
20
|
+
bot/helpers/*.*
|
21
|
+
config/*.*
|
22
|
+
```
|
23
|
+
|
24
|
+
In addition to your Ruby code in these directories, all reply files are automatically included since Stealth reads their contents for each message.
|
25
|
+
|
26
|
+
### Adding a File or Path to Watch
|
27
|
+
|
28
|
+
As you add your own directories to your bot, you'll want to add them to your `autoload_path` so that they can be hot-reloaded while in `development`. 
|
29
|
+
|
30
|
+
{% hint style="info" %}
|
31
|
+
In `production`Stealth will pre-load all files in the `autoload_paths` array to improve performance.
|
32
|
+
{% endhint %}
|
33
|
+
|
34
|
+
#### Adding a Single File
|
35
|
+
|
36
|
+
To add the file `lib/some_file.rb` to the `autoload_path`, add this line to `config/autoload.rb`:
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
Stealth.config.autoload_paths << File.join(Stealth.root, 'lib', 'some_file')
|
40
|
+
```
|
41
|
+
|
42
|
+
{% hint style="warning" %}
|
43
|
+
Don't include the `.rb` extension to the filename.
|
44
|
+
{% endhint %}
|
45
|
+
|
46
|
+
#### Adding a Directory
|
47
|
+
|
48
|
+
To add the entire `lib` directory to the `autoload_path`, add this line to `config/autoload.rb`:
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
Stealth.config.autoload_paths << File.join(Stealth.root, 'lib')
|
52
|
+
```
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# Logs
|
2
|
+
|
3
|
+
Logs are the primary visibility mechanism into your bot. Stealth logs are designed to help you debug during development and also in production.
|
4
|
+
|
5
|
+
Stealth logs all events to `stdout` instead of a log file. This avoids having to routinely clean your log files and also mimics typical production deployments.
|
6
|
+
|
7
|
+
### Interpreting Logs
|
8
|
+
|
9
|
+
Here are some sample log entries from the [Facebook Messenger](../platforms/facebook-messenger.md) platform:
|
10
|
+
|
11
|
+
```
|
12
|
+
Dec 28 18:31:33 sidekiq.1 info pid=4 tid=590 class=Stealth::Services::HandleMessageJob jid=b08b6721a327c72aa5baa09f INFO: start
|
13
|
+
Dec 28 18:31:33 sidekiq.1 info TID-58w [user] User 3772279279459415 -> Received Message: What is Stealth?
|
14
|
+
Dec 28 18:31:33 sidekiq.1 info TID-58w [facebook] Requested user profile for 3772279279459415. Response: 200: {"id":"3772279279459415","name":"Leroy Jenkins","first_name":"Leroy","last_name":"Jenkins","profile_pic":"https:\/\/picsum.photos\/400"}
|
15
|
+
Dec 28 18:31:33 sidekiq.1 info TID-58w [primary_session] User 3772279279459415: setting session to hellos->say_hello
|
16
|
+
Dec 28 18:31:33 sidekiq.1 info TID-58w [previous_session] User 3772279279459415: setting to
|
17
|
+
Dec 28 18:31:33 web.1 info TID-5hs [facebook] Received webhook.
|
18
|
+
Dec 28 18:31:34 sidekiq.1 info TID-58w [facebook] Transmitted. Response: 200: {"recipient_id":"3772279279459415"}
|
19
|
+
Dec 28 18:31:34 sidekiq.1 info TID-58w [facebook] User 3772279279459415 -> Sending: <typing indicator>
|
20
|
+
Dec 28 18:31:37 sidekiq.1 info TID-58w [facebook] Transmitted. Response: 200: {"recipient_id":"3772279279459415","message_id":"m_aukUQrqKbnxqm9FT6nBdZuuz3dIn4BVeTi4JX4XD8lJ2fSuuNXpN7SZkouaoC7SRrdrSDsqF--2Gi0KFZHkFNg"}
|
21
|
+
Dec 28 18:31:37 sidekiq.1 info TID-58w [facebook] User 3772279279459415 -> Sending: Hey Leroy, 👋 welcome to Stealth!
|
22
|
+
Dec 28 18:31:37 sidekiq.1 info TID-58w [facebook] Transmitted. Response: 200: {"recipient_id":"3772279279459415"}
|
23
|
+
Dec 28 18:31:37 sidekiq.1 info TID-58w [facebook] User 3772279279459415 -> Sending: <typing indicator>
|
24
|
+
Dec 28 18:31:42 sidekiq.1 info TID-58w [facebook] Transmitted. Response: 200: {"recipient_id":"3772279279459415","message_id":"m_5Dm01zkLp0Fj44QhvWYpq-uz3dIn4BVeTi4JX4XD8lLRkdqJ3g0yw0RA30Cpb06rkJPfDNGYs6BCV769e-nb7Q"}
|
25
|
+
Dec 28 18:31:42 sidekiq.1 info TID-58w [facebook] User 3772279279459415 -> Sending: Stealth is one of the fastest ways to create a bot.
|
26
|
+
Dec 28 18:31:42 sidekiq.1 info TID-58w [facebook] Transmitted. Response: 200: {"recipient_id":"3772279279459415"}
|
27
|
+
Dec 28 18:31:42 sidekiq.1 info TID-58w [facebook] User 3772279279459415 -> Sending: <typing indicator>
|
28
|
+
Dec 28 18:31:49 sidekiq.1 info TID-58w [facebook] Transmitted. Response: 200: {"recipient_id":"3772279279459415","message_id":"m_s9FICmzHaIi2hEVm2NDDvOuz3dIn4BVeTi4JX4XD8lJlfF98sBAAIos0GgjxrV4tNvIxq9_MW9qqPTO45e6gIA"}
|
29
|
+
Dec 28 18:31:49 sidekiq.1 info TID-58w [facebook] User 3772279279459415 -> Sending: Ready to get started?
|
30
|
+
Dec 28 18:31:49 sidekiq.1 info TID-58w [primary_session] User 3772279279459415: setting session to hellos->get_hello_response
|
31
|
+
Dec 28 18:31:49 sidekiq.1 info TID-58w [previous_session] User 3772279279459415: setting to hellos->say_hello
|
32
|
+
```
|
33
|
+
|
34
|
+
{% hint style="info" %}
|
35
|
+
Transcript logging is enabled for the sample entries above.
|
36
|
+
{% endhint %}
|
37
|
+
|
38
|
+
#### Session Updates
|
39
|
+
|
40
|
+
Each time Stealth changes the session for a user, the `previous_session` is stored as well. On Line 5 above, you can see the user does not have a previous session (`nil`) and so on Line 4 they are being sent to `hellos->say_hello`.
|
41
|
+
|
42
|
+
After the replies are sent, you can see the session is updated again on Line 19 to `hellos->get_hello_response`. This time on Line 20 you can see there is a previous session to set and so we do.
|
43
|
+
|
44
|
+
For more info about sessions and why Stealth stores the previous session, check out the [Session docs](../controllers/sessions/intro.md).
|
45
|
+
|
46
|
+
#### Thread IDs
|
47
|
+
|
48
|
+
Often times in production, you'll see a lot of entries all interlaced together depending on your bot's load. In the example above, there isn't a lot going on, but nonetheless you can see a couple different threads logging their events.
|
49
|
+
|
50
|
+
Thread IDs begin with the prefix `TID-` followed by an alphanumeric string. Threads that have the same ID are the same thread. So in the example above, `TID-58w` are all the same background job started on Line 2. Following this thread ID will allow us to follow all the steps taken by this background job without getting confused by unrelated events.
|
51
|
+
|
52
|
+
#### Event Topics
|
53
|
+
|
54
|
+
After the thread ID, you can see the event topic wrapped in brackets (`[facebook]`). These will be color coded in your console. Session change events, component events, user events, etc are all labeled accordingly.
|
55
|
+
|
56
|
+
#### Finding Events For a User
|
57
|
+
|
58
|
+
Each Stealth component is designed to output the user's `session_id` where applicable. In the example above, you can see each Facebook entry is prefixed with `User 3772279279459415`. 
|
59
|
+
|
60
|
+
In production this would allow you to search for the user's ID (`3772279279459415`) and you would be able to see all events for the user. This is really helpful for debugging.
|
61
|
+
|
62
|
+
### Transcript Logging
|
63
|
+
|
64
|
+
In order to see what your users type, in addition to what your bot sends out, you'll need to enable the transcript logging config setting. Check out the [docs for config settings](../config/settings.md).
|
65
|
+
|
66
|
+
### Logging Custom Events
|
67
|
+
|
68
|
+
If you want to add your own custom events to the log stream, you can use the Stealth`::Logger` class to log those events. This ensures your events will appear formatted like the stock Stealth events. The API for event logging is:
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
Stealth::Logger.l(topic: 'your_topic', message: 'Your message.')
|
72
|
+
```
|
73
|
+
|
74
|
+
The `topic` can be any topic of your choosing and `message` is the string you want to log. If available, you'll want to include the user's `session_id` in the `message`. This will help you tail your logs for events related to particular user.
|
@@ -0,0 +1,22 @@
|
|
1
|
+
---
|
2
|
+
description: Examining the contents of the Procfile
|
3
|
+
---
|
4
|
+
|
5
|
+
# Procfile
|
6
|
+
|
7
|
+
If you used the generator to instantiate your bot, you'll find a `Procfile.dev` in the root directory of your bot. Here are the contents:
|
8
|
+
|
9
|
+
```
|
10
|
+
web: bundle exec puma -C config/puma.rb
|
11
|
+
sidekiq: bundle exec sidekiq -C config/sidekiq.yml -q stealth_webhooks -q stealth_replies -r ./config/boot.rb
|
12
|
+
```
|
13
|
+
|
14
|
+
If you're not familiar with Procfiles, each line specifies a process name and command for the process. So in this default Procfile, we've specified 2 processes, `web` and `sidekiq`.
|
15
|
+
|
16
|
+
### Sidekiq
|
17
|
+
|
18
|
+
Currently, Stealth uses Sidekiq for processing background jobs. It is configured to monitor 2 queues by default: `stealth_webhooks` and `stealth_replies`. You can specify additional queues as needed by your bot by adding `-q <queue_name>` to the `sidekiq` Procfile entry.
|
19
|
+
|
20
|
+
{% hint style="success" %}
|
21
|
+
You can even use Sidekiq Pro and Sidekiq Enterprise if you have a license
|
22
|
+
{% endhint %}
|