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.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/.gitbook.yaml +1 -0
  3. data/.github/dependabot.yml +11 -0
  4. data/.github/workflows/ci.yml +50 -0
  5. data/CHANGELOG.md +2 -0
  6. data/Gemfile.lock +38 -40
  7. data/MAINTENANCE.md +28 -0
  8. data/README.md +1 -0
  9. data/SECURITY.md +13 -0
  10. data/VERSION +1 -1
  11. data/docs/.gitbook/assets/2880px-Turnstile_state_machine_colored.svg.png +0 -0
  12. data/docs/.gitbook/assets/Torniqueterevolution.jpg +0 -0
  13. data/docs/.gitbook/assets/logo.svg +28 -0
  14. data/docs/.gitbook/assets/ruby.png +0 -0
  15. data/docs/README.md +35 -0
  16. data/docs/SUMMARY.md +99 -0
  17. data/docs/basics.md +77 -0
  18. data/docs/building-components/message-services.md +7 -0
  19. data/docs/building-components/nlp.md +7 -0
  20. data/docs/config/services.yml.md +2 -0
  21. data/docs/config/settings.md +2 -0
  22. data/docs/controllers/available-data.md +77 -0
  23. data/docs/controllers/catch-alls.md +135 -0
  24. data/docs/controllers/controller-overview.md +130 -0
  25. data/docs/controllers/dev-jumps.md +47 -0
  26. data/docs/controllers/get_match/README.md +2 -0
  27. data/docs/controllers/get_match/alpha-ordinals.md +2 -0
  28. data/docs/controllers/get_match/entity-match.md +2 -0
  29. data/docs/controllers/get_match/exact-match.md +2 -0
  30. data/docs/controllers/handle_message/README.md +54 -0
  31. data/docs/controllers/handle_message/alpha-ordinal-matcher.md +40 -0
  32. data/docs/controllers/handle_message/homophone-detection.md +36 -0
  33. data/docs/controllers/handle_message/nil-matcher.md +44 -0
  34. data/docs/controllers/handle_message/nlp-matcher.md +51 -0
  35. data/docs/controllers/handle_message/regex-matcher.md +41 -0
  36. data/docs/controllers/handle_message/string-mather.md +23 -0
  37. data/docs/controllers/interrupt-detection.md +2 -0
  38. data/docs/controllers/platform-errors.md +2 -0
  39. data/docs/controllers/route.md +70 -0
  40. data/docs/controllers/sessions/README.md +2 -0
  41. data/docs/controllers/sessions/do_nothing.md +17 -0
  42. data/docs/controllers/sessions/intro.md +37 -0
  43. data/docs/controllers/sessions/step_back.md +88 -0
  44. data/docs/controllers/sessions/step_to.md +47 -0
  45. data/docs/controllers/sessions/step_to_at.md +43 -0
  46. data/docs/controllers/sessions/step_to_in.md +43 -0
  47. data/docs/controllers/sessions/update_session_to.md +47 -0
  48. data/docs/controllers/unrecognized-messages.md +2 -0
  49. data/docs/deployment/heroku.md +2 -0
  50. data/docs/deployment/overview.md +2 -0
  51. data/docs/dev-environment/README.md +2 -0
  52. data/docs/dev-environment/booting-up.md +33 -0
  53. data/docs/dev-environment/environment-variables.md +54 -0
  54. data/docs/dev-environment/hot-code-reloading.md +52 -0
  55. data/docs/dev-environment/logs.md +74 -0
  56. data/docs/dev-environment/procfile.md +22 -0
  57. data/docs/dev-environment/tunnels.md +18 -0
  58. data/docs/flows/flowmap.md +40 -0
  59. data/docs/flows/overview.md +43 -0
  60. data/docs/flows/state-naming.md +53 -0
  61. data/docs/flows/state-options.md +70 -0
  62. data/docs/getting-started.md +19 -0
  63. data/docs/glossary.md +21 -0
  64. data/docs/models/activerecord.md +2 -0
  65. data/docs/models/mongoid.md +2 -0
  66. data/docs/models/overview.md +2 -0
  67. data/docs/nlp-nlu/microsoft-luis.md +2 -0
  68. data/docs/nlp-nlu/openai.md +2 -0
  69. data/docs/nlp-nlu/overview.md +2 -0
  70. data/docs/platforms/alexa-skills.md +2 -0
  71. data/docs/platforms/facebook-messenger.md +2 -0
  72. data/docs/platforms/overview.md +2 -0
  73. data/docs/platforms/sms-whatsapp.md +2 -0
  74. data/docs/platforms/voice.md +2 -0
  75. data/docs/replies/delays.md +2 -0
  76. data/docs/replies/erb.md +2 -0
  77. data/docs/replies/inline-replies.md +2 -0
  78. data/docs/replies/reply-overview.md +2 -0
  79. data/docs/replies/variants.md +2 -0
  80. data/docs/replies/yaml-replies.md +2 -0
  81. data/docs/testing/integration-testing.md +2 -0
  82. data/docs/testing/untitled.md +2 -0
  83. data/lib/stealth/base.rb +1 -1
  84. data/lib/stealth/controller/replies.rb +11 -5
  85. data/lib/stealth/server.rb +10 -1
  86. data/spec/controller/replies_spec.rb +48 -0
  87. data/spec/replies/messages/say_simple_hello.yml +2 -0
  88. data/stealth.gemspec +2 -3
  89. metadata +87 -23
  90. 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,2 @@
1
+ # Sessions
2
+
@@ -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,2 @@
1
+ # Unrecognized Messages
2
+
@@ -0,0 +1,2 @@
1
+ # Heroku
2
+
@@ -0,0 +1,2 @@
1
+ # Deployment Overview
2
+
@@ -0,0 +1,2 @@
1
+ # Dev Environment
2
+
@@ -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.&#x20;
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`.&#x20;
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`.&#x20;
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 %}