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,18 @@
1
+ # Tunnels
2
+
3
+ When developing locally, message platforms require access to the Stealth server running on your machine in order to transmit user messages. 
4
+
5
+ Here are some options you can use:
6
+
7
+ ### ngrok
8
+
9
+ 1. Download [ngrok](https://ngrok.com/download)
10
+ 2. Start your Stealth server as detailed in [Booting Up](booting-up.md#boot-your-bot).
11
+ 3. Open up an ngrok tunnel to your Stealth server and port (default 5000) like this: `ngrok http 5000`. ngrok will output a unique ngrok local tunnel URL to your machine.
12
+
13
+ When you provide your local ngrok URL to a messaging service, you will have to add `/incoming/<service>`. For example:
14
+
15
+ * `https://abc1234.ngrok.io/incoming/facebook`
16
+ * `https://abc1234.ngrok.io/incoming/twilio`
17
+
18
+ More details on service specific settings can be found on the GitHub page for each service gem.
@@ -0,0 +1,40 @@
1
+ # FlowMap
2
+
3
+ The `FlowMap` is the file that contains your flow and state declarations. It's stored in `config/flow_map.rb` and will be generated by Stealth when you instantiate your bot.
4
+
5
+ Here is a `FlowMap` similar to the one that is generated for a new bot:
6
+
7
+ ```ruby
8
+ class FlowMap
9
+
10
+ include Stealth::Flow
11
+
12
+ flow :hello do
13
+ state :say_hello
14
+ state :get_hello_response, fails_to: :say_hello
15
+ end
16
+
17
+ flow :goodbye do
18
+ state :say_goodbye
19
+ end
20
+
21
+ flow :interrupt do
22
+ state :say_interrupted
23
+ end
24
+
25
+ flow :unrecognized_message do
26
+ state :handle_unrecognized_message
27
+ end
28
+
29
+ flow :catch_all do
30
+ state :level1
31
+ end
32
+
33
+ end
34
+ ```
35
+
36
+ In this example, we've declared five flows: `hello`, `goodbye`, `interrupt`, `unrecognized_message`, and `catch_all`. These are the default flows that are generated for you when you create a new bot.
37
+
38
+ Each flow consists of an arbitrary number of states. All of the above flows only have a single state, but the `hello` flow has two. As you build out your bot and add functionality, you'll need to keep the `FlowMap` updated. If you attempt to transition to a flow or state that hasn't yet been declared in the `FlowMap`, you'll encounter a Stealth`::Errors::InvalidStateTransition` exception.
39
+
40
+ States also support additional options like the `fails_to` option for the `get_hello_response` state. We'll cover these state options in the [State Option docs](state-options.md) section.
@@ -0,0 +1,43 @@
1
+ # Flows & States
2
+
3
+ ## Overview
4
+
5
+ Flows and states are the primary building blocks for Stealth bots. Your bot's users can only be in a single flow and state at any given moment. The relationship between flows and states is one of parent and child, respectively. So a flow can _have many_ states and a state always _belongs to_ a single flow.
6
+
7
+ The concept is modeled after [finite-state machines](https://en.m.wikipedia.org/wiki/Finite-state\_machine), though you don't need to familiarize yourself with all of that knowledge. The outline we provide in these docs will be sufficient.
8
+
9
+ Finite-state machines, or more simply state machines, are used throughout engineering to model states within a given machine. Imagine a coin-operated, turnstile you might find in a subway or airport. You insert a coin and the mechanism unlocks to allow you to rotate the arms and pass through.
10
+
11
+ ![Figure 1: A simple, coin-operated turnstile](../../.gitbook/assets/torniqueterevolution.jpg)
12
+
13
+ The operation of this turnstile can (and probably is) modeled as a state machine. Here is an example of what that model looks like:
14
+
15
+ ![Figure 2: Finite-state machine model for the simple, coin-operated turnstile.](../../.gitbook/assets/2880px-turnstile\_state\_machine\_colored.svg.png)
16
+
17
+ In Figure 2, the "starting" state is _Locked_ and if someone attempts to _Push_ the turnstile arms while it is in the _Locked_ state it will indefinitely remain in the _Locked_ state. That's what the self-referencing _Push_ action in Figure 2 is showing. Similarly, in Stealth, states can transition a user to a new state or it can keep a user in the same state either indefinitely or until some specific action is taken.
18
+
19
+ When a user inserts a _Coin_, the state machine in Figure 2 transitions the machine to the _Unlocked_ state. If a user inserts more coins while in this state, the machine just remains in the _Unlocked_ state. When the turnstile arms are _Pushed_, then the machine transitions back to the _Locked_ state.
20
+
21
+ This turnstile example highlights the mental model of flows and states in Stealth quite well. Specifically, states can transition your users to other states or they can keep your user in the same state. In the section about [Sessions](../controllers/sessions/), we'll cover all the ways these transitions can happen.
22
+
23
+ ## Flows
24
+
25
+ A **flow** is the term used to describe a complete interaction between a user and the bot. Flows are comprised of `states`, like a finite state machine. In Figure 2 above, the entire finite-state machine is the flow.
26
+
27
+ For example, if a user is using your bot to receive an insurance quote, the flow might be named `quote`.&#x20;
28
+
29
+ {% hint style="warning" %}
30
+ Stealth requires that flows be named in the singular form, like Ruby on Rails.
31
+ {% endhint %}
32
+
33
+ A flow consists of the following components:
34
+
35
+ 1. A controller file, named in the plural form. For example, a `quote` flow would have a corresponding `QuotesController`. One controller maps to one flow.
36
+ 2. Replies. Each flow will have a directory in the `replies` directory in plural form. Again using the `quote` flow example, the directory would named `quotes`.
37
+ 3. An entry in the `FlowMap`. The `FlowMap` file is where each flow and it's respective states are defined. We'll cover the FlowMap file in [FlowMap docs](flowmap.md) section.
38
+
39
+ ## States
40
+
41
+ A **state** is the logical division of flows. Just like in finite-state machines, users can transition between states. In Stealth, users can even transition between states from different flows. There are no naming conventions enforced by Stealth for states, but in [State Naming section](state-naming.md) we'll cover some best practices.
42
+
43
+ As mentioned in the above, a user can at most be in a single flow and state at any given moment.
@@ -0,0 +1,53 @@
1
+ # State Naming
2
+
3
+ While Stealth doesn't enforce any naming requirements for your states, we do recommend following the naming conventions outlined below. It provides continuity across your team and across bots.
4
+
5
+ Most of your states will fall into the `say`, `get`, and `ask` buckets. On the rare occasion that it does not, feel free to select a name that best describes the state.
6
+
7
+ ## Say, Ask, Get
8
+
9
+ #### Say
10
+
11
+ _Say_ actions are for _saying_ something to the user.
12
+
13
+ For example:
14
+
15
+ ```ruby
16
+ def say_hello
17
+ send_replies
18
+ end
19
+ ```
20
+
21
+ Typically we'd send the user to a new state, but sometimes it's as simple as just saying something like in the case of the end of a flow or conversation.
22
+
23
+ #### Ask
24
+
25
+ _Ask_ actions are for _asking_ something from the user.
26
+
27
+ For example:
28
+
29
+ ```ruby
30
+ def ask_weather
31
+ send_replies
32
+ update_session_to state: 'get_weather_response'
33
+ end
34
+ ```
35
+
36
+ In the above example, we've asked a question via `send_replies` and we've updated the session to a new state. This is the state that will be receiving the response. We'll cover state transitions in detail in the [Sessions Overview](../controllers/sessions/intro.md) section.
37
+
38
+ #### Get
39
+
40
+ _Get_ actions are for _getting_ and parsing a message from the user.
41
+
42
+ For example:
43
+
44
+ ```ruby
45
+ def get_weather_response
46
+ handle_message(
47
+ 'Sunny' => proc { step_to state: 'say_wear_sunglasses' },
48
+ 'Raining' => proc { step_to state: 'say_dont_forget_umbrella' }
49
+ )
50
+ end
51
+ ```
52
+
53
+ In the example above, we're handling two responses by the user. When they say "Sunny" or "Raining". Don't worry too much about the format of `handle_message`. We cover its usage in the [handle\_message docs](../controllers/handle\_message/) section.
@@ -0,0 +1,70 @@
1
+ # State Options
2
+
3
+ In your `FlowMap`, each state may also specify certain options. Some options expose built-in Stealth functionality, while others are completely custom and can be referenced by your code.
4
+
5
+ ```ruby
6
+ class FlowMap
7
+
8
+ include Stealth::Flow
9
+
10
+ flow :hello do
11
+ state :say_hello
12
+ state :get_hello_response, fails_to: :say_hello
13
+ state :say_hola, redirects_to: :say_hello
14
+ end
15
+
16
+ flow :goodbye do
17
+ state :say_goodbye, re_engage: false
18
+ end
19
+
20
+ flow :interrupt do
21
+ state :say_interrupted
22
+ end
23
+
24
+ flow :unrecognized_message do
25
+ state :handle_unrecognized_message
26
+ end
27
+
28
+ flow :catch_all do
29
+ state :level1
30
+ end
31
+
32
+ end
33
+ ```
34
+
35
+ We see three states have options defined: `get_hello_response`, `say_hola`, and `say_goodbye`.
36
+
37
+ #### fails\_to
38
+
39
+ The `fails_to` option is one of the built-in Stealth state options. By default, it's used in the `CatchAllsController` to specify where a user should be sent in the event of an error. We cover this more in the [CatchAll docs](../controllers/catch-alls.md), but in the `get_hello_response` state above, if Stealth encounters an error the `fails_to` option declares the user to be sent to the `say_hello` state of the same flow.
40
+
41
+ The `fails_to` value can also be a string if you wish to specify a different flow. So for example:
42
+
43
+ ```ruby
44
+ state :get_hello_response, fails_to: 'goodbye->say_goodbye'
45
+ ```
46
+
47
+ If Stealth encounters an error in this state, it will be sent to the `say_goodbye` state of the `goodbye` flow.
48
+
49
+ #### redirects\_to
50
+
51
+ The `redirects_to` option is useful when you're performing a rename of a state and the bot has already been deployed to production. Your production users may have existing sessions attached to the state you are renaming. If you were to perform a state rename without attaching a `redirects_to` to the old state name, the user will receive an error the next time they message your bot.
52
+
53
+ {% hint style="info" %}
54
+ For the `redirects_to`values, you can use state names as well as the "flow->state\_name" convention like in `fails_to`.
55
+ {% endhint %}
56
+
57
+ #### Custom Options
58
+
59
+ In addition to the built-in Stealth state options, you are able to define your own. This is helpful for cases where you want to define metadata for a set of states but don't want to define that logic within the controllers.
60
+
61
+ In the example `FlowMap` above, we've defined a `re_engage` option on the `say_goodbye` state. If we pretend our bot re-engages leads after a period of time, this option would be useful for allowing us to declare states for which we do not want re-engagements to be sent. In this case, the user has reached the end of the bot and so we don't want to send them any re-engagements.
62
+
63
+ You can access these custom state options via the `opts` attribute for the state specification.
64
+
65
+ ```ruby
66
+ state_spec = FlowMap.flow_spec[:goodbye].states[:say_goodbye]
67
+ state_spec.opts.present? && state_spec.opts[:re_engage]
68
+ ```
69
+
70
+ Here `state_spec.opts[:re_engage]` contains the value `true`. The hash key will correspond to what you named your option in the `FlowMap`.
@@ -0,0 +1,19 @@
1
+ # Getting Started
2
+
3
+ Stealth is designed to run on Ruby (MRI) 2.5+
4
+
5
+ While we don't require any C-based Ruby gems, we haven't yet certified Stealth on other VMs (such as JRuby). However, we do intend to provide official support for JRuby and TruffleRuby soon.
6
+
7
+ ### Installation
8
+
9
+ You can install Stealth via RubyGems:
10
+
11
+ ```ruby
12
+ gem install stealth
13
+ ```
14
+
15
+ Next, create your new bot:
16
+
17
+ ```
18
+ stealth new <bot_name>
19
+ ```
data/docs/glossary.md ADDED
@@ -0,0 +1,21 @@
1
+ ---
2
+ description: >-
3
+ Some common terms you may read throughout this doc or as you interact with the
4
+ Stealth community.
5
+ ---
6
+
7
+ # Glossary
8
+
9
+ * **message** - An _incoming_ message from a user. A _message_ and a _reply_ are counterparts.
10
+ * **reply** - An _outgoing_ message from your bot. A _message_ and a _reply_ are counterparts.
11
+ * **service message** - This is the long version of _message_. You will likely only see this referenced when developing your own Stealth components.
12
+ * **`current_message`** - This is object that contains the service message. It's available within all controller actions. More info can be found in the [controller docs](controllers/controller-overview.md).
13
+ * **component** - Components are the individual building blocks of Stealth. Stealth itself is the core framework that handles webhooks, replies, etc. Components allow Stealth to connect to messaging platforms, NLP providers, and more. Each component is offered as a separate Ruby gem.
14
+ * **message platform** - Message platforms are the platforms where your bot interacts with its users. E.g., Facebook Messenger, SMS, Whatsapp, Slack, etc.
15
+ * **NLP** - natural language processing. This is the AI subfield that encompasses taking unstructured text (like messages from users) and extracting structured concepts. NLP in Stealth is achieved through components.
16
+ * **NLU** - A subclass of NLP. More aptly describes the type of NLP you'll want to perform with Stealth, but NLP is the more commonly used term.
17
+ * **session** - Sessions allow your Stealth bot to recognize subsequent messages from users. It keeps track of where in the conversation each of your users currently reside.
18
+ * **MVC** - A software design pattern. It's not critical to understand this to get going, but if you're interested you can learn more [here](https://www.google.com/url?sa=t\&rct=j\&q=\&esrc=s\&source=web\&cd=\&cad=rja\&uact=8\&ved=2ahUKEwiGt\_XpzPHtAhXNVc0KHWjiDG8QFjAAegQIBRAC\&url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FModel%25E2%2580%2593view%25E2%2580%2593controller\&usg=AOvVaw1wpuCUJRz1WxG51eRibnYX).
19
+ * **intent** - **** Intents are one of the main things NLP components extract from a message. They are a type of classification. We talk about them in more detail in the `handle_message` [docs](controllers/handle\_message/nlp-matcher.md).
20
+ * **entity** - Entities are individual tokens within a message that an NLP component will extract. So for example, a number or some other trained entity specific to your bot (like car models and makes). More info about these can be found in the `get_match` [docs](controllers/get\_match/entity-match.md).
21
+ * **regex** - A regular expression. These are a programming concept used in string matching. In Stealth, these are most often used in `handle_message` and there are [docs](controllers/handle\_message/regex-matcher.md) for their usage.
@@ -0,0 +1,2 @@
1
+ # ActiveRecord
2
+
@@ -0,0 +1,2 @@
1
+ # Mongoid
2
+
@@ -0,0 +1,2 @@
1
+ # Model Overview
2
+
@@ -0,0 +1,2 @@
1
+ # Microsoft LUIS
2
+
@@ -0,0 +1,2 @@
1
+ # OpenAI
2
+
@@ -0,0 +1,2 @@
1
+ # NLP Overview
2
+
@@ -0,0 +1,2 @@
1
+ # Alexa Skills
2
+
@@ -0,0 +1,2 @@
1
+ # Facebook Messenger
2
+
@@ -0,0 +1,2 @@
1
+ # Platform Overview
2
+
@@ -0,0 +1,2 @@
1
+ # SMS/Whatsapp
2
+
@@ -0,0 +1,2 @@
1
+ # Voice
2
+
@@ -0,0 +1,2 @@
1
+ # Delays
2
+
@@ -0,0 +1,2 @@
1
+ # ERB
2
+
@@ -0,0 +1,2 @@
1
+ # Inline Replies
2
+
@@ -0,0 +1,2 @@
1
+ # Reply Overview
2
+
@@ -0,0 +1,2 @@
1
+ # Variants
2
+
@@ -0,0 +1,2 @@
1
+ # YAML Replies
2
+
@@ -0,0 +1,2 @@
1
+ # Integration Testing
2
+
@@ -0,0 +1,2 @@
1
+ # Specs
2
+
data/lib/stealth/base.rb CHANGED
@@ -123,7 +123,7 @@ module Stealth
123
123
 
124
124
  load_bot!
125
125
 
126
- Sidekiq.options[:reloader] = Stealth.bot_reloader
126
+ Sidekiq[:reloader] = Stealth.bot_reloader
127
127
 
128
128
  if defined?(ActiveRecord)
129
129
  if ENV['DATABASE_URL'].present?
@@ -96,11 +96,11 @@ module Stealth
96
96
  reply: reply
97
97
  )
98
98
 
99
- translated_reply = handler.send(reply.reply_type)
100
- client = service_client.new(reply: translated_reply)
99
+ formatted_reply = handler.send(reply.reply_type)
100
+ client = service_client.new(reply: formatted_reply)
101
101
  client.transmit
102
102
 
103
- log_reply(reply) if Stealth.config.transcript_logging
103
+ log_reply(reply, handler) if Stealth.config.transcript_logging
104
104
 
105
105
  # If this was a 'delay' type of reply, we insert the delay
106
106
  if reply.delay?
@@ -264,10 +264,14 @@ module Stealth
264
264
  end
265
265
  end
266
266
 
267
- def log_reply(reply)
267
+ def log_reply(reply, reply_handler)
268
268
  message = case reply.reply_type
269
269
  when 'text'
270
- reply['text']
270
+ if reply_handler.respond_to?(:translated_reply)
271
+ reply_handler.translated_reply
272
+ else
273
+ reply['text']
274
+ end
271
275
  when 'speech'
272
276
  reply['speech']
273
277
  when 'ssml'
@@ -282,6 +286,8 @@ module Stealth
282
286
  topic: current_service,
283
287
  message: "User #{current_session_id} -> Sending: #{message}"
284
288
  )
289
+
290
+ message
285
291
  end
286
292
 
287
293
  end # instance methods
@@ -35,7 +35,12 @@ module Stealth
35
35
  # JSON params need to be parsed and added to the params
36
36
  if request.env['CONTENT_TYPE']&.match(/application\/json/i)
37
37
  json_params = MultiJson.load(request.body.read)
38
- params.merge!(json_params)
38
+
39
+ if bandwidth?
40
+ params.merge!(json_params.first)
41
+ else
42
+ params.merge!(json_params)
43
+ end
39
44
  end
40
45
 
41
46
  dispatcher = Stealth::Dispatcher.new(
@@ -60,5 +65,9 @@ module Stealth
60
65
  end
61
66
  end
62
67
 
68
+ def bandwidth?
69
+ params[:service] == "bandwidth"
70
+ end
71
+
63
72
  end
64
73
  end
@@ -66,6 +66,10 @@ describe "Stealth::Controller replies" do
66
66
  send_replies custom_reply: 'messages/sub1/sub2/say_nested'
67
67
  end
68
68
 
69
+ def say_simple_hello
70
+ send_replies
71
+ end
72
+
69
73
  def say_inline_reply
70
74
  reply = [
71
75
  { 'reply_type' => 'text', 'text' => 'Hi, Morty. Welcome to Stealth bot...' },
@@ -609,6 +613,50 @@ describe "Stealth::Controller replies" do
609
613
  end
610
614
  end
611
615
 
616
+ describe "Logging replies" do
617
+ let(:stubbed_handler) { double("handler") }
618
+ let(:stubbed_client) { double("client") }
619
+
620
+ before(:each) do
621
+ allow(Stealth::Services::Facebook::ReplyHandler).to receive(:new).and_return(stubbed_handler)
622
+ allow(Stealth::Services::Facebook::Client).to receive(:new).and_return(stubbed_client)
623
+ allow(controller.current_session).to receive(:flow_string).and_return("message")
624
+ allow(controller.current_session).to receive(:state_string).and_return("say_simple_hello")
625
+ Stealth.config.auto_insert_delays = false
626
+ Stealth.config.transcript_logging = true
627
+ end
628
+
629
+ after(:each) do
630
+ Stealth.config.auto_insert_delays = true
631
+ Stealth.config.transcript_logging = false
632
+ end
633
+
634
+ it "should log replies if transcript_logging is enabled" do
635
+ allow(stubbed_client).to receive(:transmit).and_return(true)
636
+ allow(controller).to receive(:sleep).and_return(true).with(2.0)
637
+
638
+ allow(stubbed_handler).to receive(:text).exactly(1).times
639
+ expect(Stealth::Logger).to receive(:l).with(
640
+ topic: 'facebook',
641
+ message: "User #{controller.current_session_id} -> Sending: Hello"
642
+ )
643
+ controller.say_simple_hello
644
+ end
645
+
646
+ it "should log translated replies if transcript_logging is enabled and the driver supports it" do
647
+ allow(stubbed_client).to receive(:transmit).and_return(true)
648
+ allow(controller).to receive(:sleep).and_return(true).with(2.0)
649
+
650
+ allow(stubbed_handler).to receive(:text).exactly(1).times
651
+ allow(stubbed_handler).to receive(:translated_reply).and_return("Bonjour")
652
+ expect(Stealth::Logger).to receive(:l).with(
653
+ topic: 'facebook',
654
+ message: "User #{controller.current_session_id} -> Sending: Bonjour"
655
+ )
656
+ controller.say_simple_hello
657
+ end
658
+ end
659
+
612
660
  describe "client errors" do
613
661
  let(:stubbed_handler) { double("handler") }
614
662
  let(:stubbed_client) { double("client") }
@@ -0,0 +1,2 @@
1
+ - reply_type: text
2
+ text: "Hello"
data/stealth.gemspec CHANGED
@@ -16,12 +16,11 @@ Gem::Specification.new do |s|
16
16
  s.add_dependency 'puma', '>= 4.2', '< 6.0'
17
17
  s.add_dependency 'thor', '~> 1.0'
18
18
  s.add_dependency 'multi_json', '~> 1.12'
19
- s.add_dependency 'sidekiq', '~> 6.0'
19
+ s.add_dependency 'sidekiq', '< 7'
20
20
  s.add_dependency 'activesupport', '~> 6.0'
21
21
 
22
22
  s.add_development_dependency 'rspec', '~> 3.9'
23
- s.add_development_dependency 'rspec_junit_formatter', '~> 0.3'
24
- s.add_development_dependency 'rack-test', '~> 1.1'
23
+ s.add_development_dependency 'rack-test', '~> 2.0'
25
24
  s.add_development_dependency 'mock_redis', '~> 0.22'
26
25
 
27
26
  s.files = `git ls-files`.split("\n")