stealth 1.0.4 → 1.1.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -4
- data/Gemfile +1 -1
- data/Gemfile.lock +32 -77
- data/README.md +1 -0
- data/VERSION +1 -1
- data/docs/03-basics.md +18 -2
- data/docs/04-sessions.md +12 -0
- data/docs/05-controllers.md +3 -3
- data/docs/07-replies.md +42 -1
- data/lib/stealth/base.rb +23 -9
- data/lib/stealth/controller/catch_all.rb +1 -1
- data/lib/stealth/controller/controller.rb +21 -9
- data/lib/stealth/controller/dynamic_delay.rb +64 -0
- data/lib/stealth/controller/replies.rb +55 -14
- data/lib/stealth/flow/base.rb +1 -1
- data/lib/stealth/flow/specification.rb +30 -8
- data/lib/stealth/flow/state.rb +10 -5
- data/lib/stealth/generators/builder/Gemfile +3 -0
- data/lib/stealth/generators/builder/Procfile.dev +1 -1
- data/lib/stealth/generators/builder/bot/controllers/catch_alls_controller.rb +3 -6
- data/lib/stealth/generators/builder/config/services.yml +9 -10
- data/lib/stealth/generators/generate/flow/controllers/controller.tt +0 -19
- data/lib/stealth/generators/generate/flow/helpers/helper.tt +2 -1
- data/lib/stealth/generators/generate/flow/replies/{ask_reply.tt → ask_example.tt} +0 -0
- data/lib/stealth/generators/generate.rb +2 -9
- data/lib/stealth/migrations/tasks.rb +2 -0
- data/lib/stealth/scheduled_reply.rb +1 -1
- data/lib/stealth/server.rb +2 -0
- data/lib/stealth/services/jobs/handle_message_job.rb +1 -1
- data/lib/stealth/session.rb +44 -16
- data/spec/controller/catch_all_spec.rb +2 -1
- data/spec/controller/controller_spec.rb +102 -13
- data/spec/controller/dynamic_delay_spec.rb +72 -0
- data/spec/controller/replies_spec.rb +94 -3
- data/spec/flow/state_spec.rb +33 -4
- data/spec/replies/messages/say_hola.yml+facebook.erb +6 -0
- data/spec/replies/messages/say_hola.yml+twilio.erb +6 -0
- data/spec/replies/messages/say_hola.yml.erb +6 -0
- data/spec/replies/messages/say_howdy_with_dynamic.yml +79 -0
- data/spec/replies/messages/say_offer_with_dynamic.yml +6 -0
- data/spec/replies/messages/say_yo.yml +6 -0
- data/spec/replies/messages/say_yo.yml+twitter +6 -0
- data/spec/session_spec.rb +17 -0
- data/spec/support/sample_messages.rb +24 -26
- data/stealth.gemspec +1 -3
- metadata +25 -41
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -35
- data/.github/ISSUE_TEMPLATE/feature_request.md +0 -17
- data/lib/stealth/generators/generate/flow/models/model.tt +0 -2
- data/lib/stealth/generators/generate/flow/replies/say_no_reply.tt +0 -2
- data/lib/stealth/generators/generate/flow/replies/say_yes_reply.tt +0 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6c918a4ed7d98e6c5e8c60dab8e3e96d1f5a1d6407bb21843fa6a4eb8a46b0fc
|
4
|
+
data.tar.gz: e4fe19747e02ba2b17af1d932e2505153db47173a45bee8558d7ab2cd8c5309b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0fbe087d4e4a7d5e60bc442524588ae6c63f1d842b35ebb7f73bf5b2a09716e07a08fc0d3867c7392045c781b8fff6fe6d9aa304c73d0c0229118a2f2dcc1bcf
|
7
|
+
data.tar.gz: 710cc3404711a0d18c765846acada15b94d0f1ebd0fb42196fcc85fc8c6254070bfcfee93fadf8db5bf439aa1acd286cd5c52ff5737017afdec65ddd64fd9c41
|
data/CHANGELOG.md
CHANGED
@@ -1,10 +1,25 @@
|
|
1
|
-
# Changelog for Stealth v1.0
|
1
|
+
# Changelog for Stealth v1.1.0
|
2
|
+
|
3
|
+
## Breaking Changes
|
4
|
+
|
5
|
+
* [Sidekiq] Sidekiq queues have been renamed from `webhooks` and `default` to `stealth_webhooks` and `stealth_replies`. Please ensure your `Procfile` is adjusted accordingly.
|
6
|
+
* [Flows] `flow_map.current_state.fails_to` now returns a `Stealth::Session` instead of a `Stealth::Flow::State`. This might affect your `catch_alls`.
|
2
7
|
|
3
8
|
## Enhancements
|
4
9
|
|
5
|
-
|
10
|
+
* [Controllers] `current_session_id` now references the session ID in controllers
|
11
|
+
* [Replies] Added support for dynamic delays
|
12
|
+
* [Replies] Added support for service-specific variants
|
13
|
+
* [Models] ActiveRecord is now part of the generated bot Gemfile and can be removed from bots
|
14
|
+
* [Flows] Added support for `redirects_to` during state declaration. If specified, will automatically step a user to the specified state or session.
|
15
|
+
* [Flows] `redirects_to` and `fails_to` now support a session string as an argument (`my_flow->some_state`). This allows you to fail and redirect to other flows. A state name specified as a string or symbol is still allowed.
|
16
|
+
* [Errors] Backtraces are now more readable in logs
|
6
17
|
|
7
18
|
## Bug Fixes
|
8
19
|
|
9
|
-
* [
|
10
|
-
* [Generators] Fixed
|
20
|
+
* [Generators] Fixed flow generation
|
21
|
+
* [Generators] Fixed sample Facebook services.yml example
|
22
|
+
|
23
|
+
## Deprecations
|
24
|
+
|
25
|
+
* [Controllers] `current_user_id` has been soft deprecated in favor of `current_session_id`
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,12 +1,10 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
stealth (1.0.
|
5
|
-
activerecord (~> 5.2)
|
4
|
+
stealth (1.1.0.alpha)
|
6
5
|
activesupport (~> 5.2)
|
7
6
|
multi_json (~> 1.12)
|
8
7
|
puma (~> 3.10)
|
9
|
-
railties (~> 5.2)
|
10
8
|
sidekiq (~> 5.0)
|
11
9
|
sinatra (~> 2.0)
|
12
10
|
thor (~> 0.20)
|
@@ -14,96 +12,53 @@ PATH
|
|
14
12
|
GEM
|
15
13
|
remote: https://rubygems.org/
|
16
14
|
specs:
|
17
|
-
|
18
|
-
actionview (= 5.2.0)
|
19
|
-
activesupport (= 5.2.0)
|
20
|
-
rack (~> 2.0)
|
21
|
-
rack-test (>= 0.6.3)
|
22
|
-
rails-dom-testing (~> 2.0)
|
23
|
-
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
24
|
-
actionview (5.2.0)
|
25
|
-
activesupport (= 5.2.0)
|
26
|
-
builder (~> 3.1)
|
27
|
-
erubi (~> 1.4)
|
28
|
-
rails-dom-testing (~> 2.0)
|
29
|
-
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
30
|
-
activemodel (5.2.0)
|
31
|
-
activesupport (= 5.2.0)
|
32
|
-
activerecord (5.2.0)
|
33
|
-
activemodel (= 5.2.0)
|
34
|
-
activesupport (= 5.2.0)
|
35
|
-
arel (>= 9.0)
|
36
|
-
activesupport (5.2.0)
|
15
|
+
activesupport (5.2.1.1)
|
37
16
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
38
17
|
i18n (>= 0.7, < 2)
|
39
18
|
minitest (~> 5.1)
|
40
19
|
tzinfo (~> 1.1)
|
41
|
-
|
42
|
-
builder (3.2.3)
|
43
|
-
concurrent-ruby (1.0.5)
|
20
|
+
concurrent-ruby (1.1.3)
|
44
21
|
connection_pool (2.2.2)
|
45
|
-
crass (1.0.4)
|
46
22
|
diff-lcs (1.3)
|
47
|
-
|
48
|
-
i18n (1.0.1)
|
23
|
+
i18n (1.1.1)
|
49
24
|
concurrent-ruby (~> 1.0)
|
50
|
-
loofah (2.2.2)
|
51
|
-
crass (~> 1.0.2)
|
52
|
-
nokogiri (>= 1.5.9)
|
53
|
-
method_source (0.9.0)
|
54
|
-
mini_portile2 (2.3.0)
|
55
25
|
minitest (5.11.3)
|
56
|
-
mock_redis (0.
|
26
|
+
mock_redis (0.19.0)
|
57
27
|
multi_json (1.13.1)
|
58
|
-
mustermann (1.0.
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
rack (2.0.4)
|
64
|
-
rack-protection (2.0.3)
|
28
|
+
mustermann (1.0.3)
|
29
|
+
oj (3.7.1)
|
30
|
+
puma (3.12.0)
|
31
|
+
rack (2.0.6)
|
32
|
+
rack-protection (2.0.4)
|
65
33
|
rack
|
66
|
-
rack-test (
|
34
|
+
rack-test (1.1.0)
|
67
35
|
rack (>= 1.0, < 3)
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
method_source
|
77
|
-
rake (>= 0.8.7)
|
78
|
-
thor (>= 0.18.1, < 2.0)
|
79
|
-
rake (12.3.1)
|
80
|
-
redis (4.0.1)
|
81
|
-
rspec (3.7.0)
|
82
|
-
rspec-core (~> 3.7.0)
|
83
|
-
rspec-expectations (~> 3.7.0)
|
84
|
-
rspec-mocks (~> 3.7.0)
|
85
|
-
rspec-core (3.7.1)
|
86
|
-
rspec-support (~> 3.7.0)
|
87
|
-
rspec-expectations (3.7.0)
|
36
|
+
redis (4.0.3)
|
37
|
+
rspec (3.8.0)
|
38
|
+
rspec-core (~> 3.8.0)
|
39
|
+
rspec-expectations (~> 3.8.0)
|
40
|
+
rspec-mocks (~> 3.8.0)
|
41
|
+
rspec-core (3.8.0)
|
42
|
+
rspec-support (~> 3.8.0)
|
43
|
+
rspec-expectations (3.8.1)
|
88
44
|
diff-lcs (>= 1.2.0, < 2.0)
|
89
|
-
rspec-support (~> 3.
|
90
|
-
rspec-mocks (3.
|
45
|
+
rspec-support (~> 3.8.0)
|
46
|
+
rspec-mocks (3.8.0)
|
91
47
|
diff-lcs (>= 1.2.0, < 2.0)
|
92
|
-
rspec-support (~> 3.
|
93
|
-
rspec-support (3.
|
94
|
-
rspec_junit_formatter (0.
|
48
|
+
rspec-support (~> 3.8.0)
|
49
|
+
rspec-support (3.8.0)
|
50
|
+
rspec_junit_formatter (0.4.1)
|
95
51
|
rspec-core (>= 2, < 4, != 2.12.0)
|
96
|
-
sidekiq (5.
|
97
|
-
|
98
|
-
connection_pool (~> 2.2, >= 2.2.0)
|
52
|
+
sidekiq (5.2.3)
|
53
|
+
connection_pool (~> 2.2, >= 2.2.2)
|
99
54
|
rack-protection (>= 1.5.0)
|
100
55
|
redis (>= 3.3.5, < 5)
|
101
|
-
sinatra (2.0.
|
56
|
+
sinatra (2.0.4)
|
102
57
|
mustermann (~> 1.0)
|
103
58
|
rack (~> 2.0)
|
104
|
-
rack-protection (= 2.0.
|
59
|
+
rack-protection (= 2.0.4)
|
105
60
|
tilt (~> 2.0)
|
106
|
-
thor (0.20.
|
61
|
+
thor (0.20.3)
|
107
62
|
thread_safe (0.3.6)
|
108
63
|
tilt (2.0.8)
|
109
64
|
tzinfo (1.2.5)
|
@@ -114,11 +69,11 @@ PLATFORMS
|
|
114
69
|
|
115
70
|
DEPENDENCIES
|
116
71
|
mock_redis (~> 0.17)
|
117
|
-
oj (~> 3.
|
118
|
-
rack-test (~>
|
72
|
+
oj (~> 3.7)
|
73
|
+
rack-test (~> 1.1)
|
119
74
|
rspec (~> 3.6)
|
120
75
|
rspec_junit_formatter (~> 0.3)
|
121
76
|
stealth!
|
122
77
|
|
123
78
|
BUNDLED WITH
|
124
|
-
1.16.
|
79
|
+
1.16.3
|
data/README.md
CHANGED
@@ -23,6 +23,7 @@ Currently, there are gems for:
|
|
23
23
|
### Messaging
|
24
24
|
* [Facebook Messenger](https://github.com/hellostealth/stealth-facebook)
|
25
25
|
* [Twilio SMS](https://github.com/hellostealth/stealth-twilio)
|
26
|
+
* [Smooch](https://github.com/hellostealth/stealth-smooch)
|
26
27
|
|
27
28
|
### Natural Language Processing
|
28
29
|
* [AWS Comprehend](https://github.com/hellostealth/stealth-aws-comprehend)
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0.
|
1
|
+
1.1.0.rc1
|
data/docs/03-basics.md
CHANGED
@@ -75,6 +75,8 @@ class FlowMap
|
|
75
75
|
state :say_hello
|
76
76
|
state :ask_name
|
77
77
|
state :get_name, fails_to: :ask_name
|
78
|
+
state :say_wow, redirects_to: :say_hello
|
79
|
+
state :say_bye, redirects_to: 'goodbye->say_goodbye'
|
78
80
|
end
|
79
81
|
|
80
82
|
flow :goodbye do
|
@@ -89,9 +91,9 @@ class FlowMap
|
|
89
91
|
end
|
90
92
|
```
|
91
93
|
|
92
|
-
Here we have defined three flows: `hello`, `goodbye`, and `catch_all`.
|
94
|
+
Here we have defined three flows: `hello`, `goodbye`, and `catch_all`. These are the default flows that are generated for you when you create a new bot. We have made a few changes above to highlight some functionality.
|
93
95
|
|
94
|
-
|
96
|
+
Each flow consists of an arbitrary number of states. These states should each have a corresponding controller action by the same name. States also support two additional options: `fails_to` and `redirects_to` which we explain below.
|
95
97
|
|
96
98
|
## Default Flows
|
97
99
|
|
@@ -109,6 +111,20 @@ Error handling is one of the most important parts of building great bots. We rec
|
|
109
111
|
|
110
112
|
See the Catch All (#catchalls) section for more information on how Stealth handles `catch_all` flows.
|
111
113
|
|
114
|
+
## fails_to
|
115
|
+
|
116
|
+
The `fails_to` option allows you to specify a state that a user should be redirected to in case of an error. The `CatchAllsController` will still be responsible for determining how to handle the error, but by specifying a `fails_to` state here, the `CatchAllsController` is able to redirect accordingly.
|
117
|
+
|
118
|
+
A freshly generated bot will contain sample `CatchAll` code for redirecting a user to a `fails_to` state.
|
119
|
+
|
120
|
+
The `fails_to` option takes a state name (string or symbol) or a session key. See [Redis Backed Sessions](#sessions.redis_backed_sessions) (or in the FlowMap example above) for more info about session keys. By specifying a session key, you can fail to a completely different flow from the one where the error occurred.
|
121
|
+
|
122
|
+
## redirects_to
|
123
|
+
|
124
|
+
The `redirects_to` option allows you specify a state that a user should be redirected to. This is useful if you have deprecated a state where existing users may still have open sessions pointing to the state. When a user returns to your bot, they will be redirected to the flow and state specified by this option.
|
125
|
+
|
126
|
+
Like `fails_to` above, the `redirects_to` option takes a state name (string or symbol) or a session key. See [Redis Backed Sessions](#sessions.redis_backed_sessions) (or in the FlowMap example above) for more info about session keys. By specifying a session key, you can fail to a completely different flow from the one where the error occurred.
|
127
|
+
|
112
128
|
## Say, Ask, Get
|
113
129
|
|
114
130
|
Stealth recommends you use the `say`, `ask`, and `get` prefix for your flow state names. It's not required, but it is a convention we have found helpful to keep state names under control. It also helps other developers on your team follow along more easily.
|
data/docs/04-sessions.md
CHANGED
@@ -15,3 +15,15 @@ User sessions are stored in Redis. Each session is a lightweight key-value pair.
|
|
15
15
|
So if for example a user with ID `100023838288224423` is currently at the `hello` flow and `ask_name` state, the value for the key would be: `hello->ask_name`.
|
16
16
|
|
17
17
|
You likely won't be interacting with sessions directly since Stealth manages it automatically for you. We just present it here for clarity into how sessions work.
|
18
|
+
|
19
|
+
## Session Expiration
|
20
|
+
|
21
|
+
By default, Stealth will not expire your user sessions. If you want your sessions to expire, you can set the `session_ttl` option:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
Stealth.config.session_ttl = 2_592_000 # seconds (30 * 24 * 60 * 60)
|
25
|
+
```
|
26
|
+
|
27
|
+
The above configuration setting will result in stale keys expiring in 30 days. Sessions that have been accessed reset the TTL (time to live). If you set a value of zero (the default), session expiration will be disabled.
|
28
|
+
|
29
|
+
If a user returns to your bot after their session has expired, they will start again in the `Hello` flow or whatever you have defined to be your first flow.
|
data/docs/05-controllers.md
CHANGED
@@ -110,7 +110,7 @@ class ContactsController < BotController
|
|
110
110
|
end
|
111
111
|
```
|
112
112
|
|
113
|
-
This would render the reply contained in `replies/contacts/say_contact_us.yml`.
|
113
|
+
This would render the reply contained in `replies/contacts/say_contact_us.yml`. See [Reply Variants](#variants) for additional naming options.
|
114
114
|
|
115
115
|
## `step_to_in`
|
116
116
|
|
@@ -174,6 +174,6 @@ Returns `true` or `false` depending on whether or not the `current_message` cont
|
|
174
174
|
|
175
175
|
Returns `true` or `false` depending on whether or not the `current_message` contains attachments.
|
176
176
|
|
177
|
-
### current_user_id
|
177
|
+
### current_session_id (previously current_user_id)
|
178
178
|
|
179
|
-
A convenience method for accessing the
|
179
|
+
A convenience method for accessing the session's ID.
|
data/docs/07-replies.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
title: Replies
|
3
3
|
---
|
4
4
|
|
5
|
-
Stealth replies can send one or more replies to a user.
|
5
|
+
Stealth replies can send one or more replies to a user. The supported reply types will depend on the specific messaging service you're using. Each service integration will detail it's supported reply types in it's respective docs.
|
6
6
|
|
7
7
|
However, here is a generic reply using text, delays, and suggestions.
|
8
8
|
|
@@ -22,6 +22,22 @@ However, here is a generic reply using text, delays, and suggestions.
|
|
22
22
|
- text: "No"
|
23
23
|
```
|
24
24
|
|
25
|
+
## Reply Variants
|
26
|
+
|
27
|
+
By default, Stealth will look for your replies in the folder corresponding to your controller name. So, for example, if you have a `MessagesController`, Stealth will look for replies in `bot/replies/messages`.
|
28
|
+
|
29
|
+
If you have an action named `say_hello`, it will look for a reply file named `bot/replies/messages/say_hello.yml.erb` first, and then if that is not found, it will look for `bot/replies/messages/say_hello.yml`. If neither of these files are found, Stealth will raise a `Stealth::Errors::ReplyNotFound`.
|
30
|
+
|
31
|
+
In addition to these two naming conventions, Stealth 1.1+ supports Reply Variants. By adding the name of the service to your reply filename, Stealth will reply to users from that service using the designated reply file. That's a mouthful. Let's try an example.
|
32
|
+
|
33
|
+
For example, if the bot is replying to a message via an action called `hello`:
|
34
|
+
|
35
|
+
Facebook users would receive the reply in `hello.yml+facebook.erb`.
|
36
|
+
Twilio SMS users would receive the reply in `hello.yml+twilio.erb`.
|
37
|
+
Every other service would receive the reply in `hello.yml.erb`.
|
38
|
+
|
39
|
+
This allows you to take advantage of things like Facebook Messenger Cards while still maintaining compatibility for users using SMS.
|
40
|
+
|
25
41
|
## Format
|
26
42
|
|
27
43
|
Stealth reply templates are written in YAML. Stealth doesn't use advanced YAML features, but we do recommend you familiarize yourself with the syntax. In the above reply example, you should be able to see there are 5 replies included in the reply file.
|
@@ -58,6 +74,31 @@ Delays are a common pattern of chatbot design. After a block of text, it's recom
|
|
58
74
|
|
59
75
|
Stealth will pause for the duration specified. For service integrations that support it (like Facebook), Stealth will send a typing indicator while it is paused.
|
60
76
|
|
77
|
+
### Dynamic Delays
|
78
|
+
|
79
|
+
Rather than specifying an explicit delay duration, you can optionally choose to specify a dynamic duration:
|
80
|
+
|
81
|
+
```yaml
|
82
|
+
- reply_type: delay
|
83
|
+
duration: dynamic
|
84
|
+
```
|
85
|
+
|
86
|
+
The dynamic delay uses a heuristic to dynamically determine the length of the delay. The previous message sent to the user is examined and depending on it's type and text length (in the case of text replies), an optimal duration is computed.
|
87
|
+
|
88
|
+
If you find that the dynamic delays are too fast for your taste, you can slow them down by setting the multiplier value to something between 0 and 1:
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
Stealth.config.dynamic_delay_muliplier = 0.5
|
92
|
+
```
|
93
|
+
|
94
|
+
If you find them to be too slow, you can speed them up by setting the multipler to a value greater than 1:
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
Stealth.config.dynamic_delay_muliplier = 2.5
|
98
|
+
```
|
99
|
+
|
100
|
+
You can set this option by setting the above value in an intializer file, i.e., `config/dynamic_delay_config.rb`.
|
101
|
+
|
61
102
|
## Naming Conventions
|
62
103
|
|
63
104
|
Replies are named after a flow's state (which is also the controller's action). So for a given controller:
|
data/lib/stealth/base.rb
CHANGED
@@ -37,6 +37,11 @@ module Stealth
|
|
37
37
|
@configuration
|
38
38
|
end
|
39
39
|
|
40
|
+
def self.set_config_defaults(config)
|
41
|
+
config.dynamic_delay_muliplier = 1.0
|
42
|
+
config.session_ttl = 0
|
43
|
+
end
|
44
|
+
|
40
45
|
# Loads the services.yml configuration unless one has already been loaded
|
41
46
|
def self.load_services_config(services_yaml)
|
42
47
|
@semaphore ||= Mutex.new
|
@@ -49,7 +54,10 @@ module Stealth
|
|
49
54
|
raise Stealth::Errors::ConfigurationError, "Could not find services.yml configuration for #{env} environment"
|
50
55
|
end
|
51
56
|
|
52
|
-
Stealth::Configuration.new(services_config[env])
|
57
|
+
config = Stealth::Configuration.new(services_config[env])
|
58
|
+
set_config_defaults(config)
|
59
|
+
|
60
|
+
config
|
53
61
|
end
|
54
62
|
end
|
55
63
|
end
|
@@ -72,10 +80,12 @@ module Stealth
|
|
72
80
|
require File.join(Stealth.root, 'config', 'flow_map')
|
73
81
|
require_directory("bot")
|
74
82
|
|
75
|
-
if
|
76
|
-
|
77
|
-
|
78
|
-
|
83
|
+
if defined?(ActiveRecord)
|
84
|
+
if ENV['DATABASE_URL'].present?
|
85
|
+
ActiveRecord::Base.establish_connection(ENV['DATABASE_URL'])
|
86
|
+
else
|
87
|
+
ActiveRecord::Base.establish_connection(YAML::load_file("config/database.yml")[Stealth.env])
|
88
|
+
end
|
79
89
|
end
|
80
90
|
end
|
81
91
|
|
@@ -107,10 +117,14 @@ require 'stealth/controller/callbacks'
|
|
107
117
|
require 'stealth/controller/replies'
|
108
118
|
require 'stealth/controller/catch_all'
|
109
119
|
require 'stealth/controller/helpers'
|
120
|
+
require 'stealth/controller/dynamic_delay'
|
110
121
|
require 'stealth/controller/controller'
|
111
122
|
require 'stealth/flow/base'
|
112
123
|
require 'stealth/services/base_client'
|
113
|
-
|
114
|
-
|
115
|
-
require 'stealth/migrations/
|
116
|
-
require 'stealth/migrations/
|
124
|
+
|
125
|
+
if defined?(ActiveRecord)
|
126
|
+
require 'stealth/migrations/configurator'
|
127
|
+
require 'stealth/migrations/generators'
|
128
|
+
require 'stealth/migrations/railtie_config'
|
129
|
+
require 'stealth/migrations/tasks'
|
130
|
+
end
|
@@ -48,7 +48,7 @@ module Stealth
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def error_slug
|
51
|
-
['error',
|
51
|
+
['error', current_session_id, current_session.flow_string, current_session.state_string].join('-')
|
52
52
|
end
|
53
53
|
|
54
54
|
def calculate_catch_all_state(error_level)
|
@@ -5,17 +5,19 @@ module Stealth
|
|
5
5
|
class Controller
|
6
6
|
|
7
7
|
include Stealth::Controller::Callbacks
|
8
|
+
include Stealth::Controller::DynamicDelay
|
8
9
|
include Stealth::Controller::Replies
|
9
10
|
include Stealth::Controller::CatchAll
|
10
11
|
include Stealth::Controller::Helpers
|
11
12
|
|
12
13
|
attr_reader :current_message, :current_user_id, :current_flow,
|
13
|
-
:current_service, :flow_controller, :action_name
|
14
|
+
:current_service, :flow_controller, :action_name,
|
15
|
+
:current_session_id
|
14
16
|
|
15
17
|
def initialize(service_message:, current_flow: nil)
|
16
18
|
@current_message = service_message
|
17
19
|
@current_service = service_message.service
|
18
|
-
@current_user_id = service_message.sender_id
|
20
|
+
@current_user_id = @current_session_id = service_message.sender_id
|
19
21
|
@current_flow = current_flow
|
20
22
|
@progressed = false
|
21
23
|
end
|
@@ -47,17 +49,27 @@ module Stealth
|
|
47
49
|
end
|
48
50
|
|
49
51
|
def current_session
|
50
|
-
@current_session ||= Stealth::Session.new(user_id:
|
52
|
+
@current_session ||= Stealth::Session.new(user_id: current_session_id)
|
51
53
|
end
|
52
54
|
|
53
55
|
def previous_session
|
54
|
-
@previous_session ||= Stealth::Session.new(user_id:
|
56
|
+
@previous_session ||= Stealth::Session.new(user_id: current_session_id, previous: true)
|
55
57
|
end
|
56
58
|
|
57
59
|
def action(action: nil)
|
58
60
|
@action_name = action
|
59
61
|
@action_name ||= current_session.state_string
|
60
62
|
|
63
|
+
# Check if the user needs to be redirected
|
64
|
+
if current_session.flow.current_state.redirects_to.present?
|
65
|
+
Stealth::Logger.l(
|
66
|
+
topic: "redirect",
|
67
|
+
message: "From #{current_session.session} to #{current_session.flow.current_state.redirects_to.session}"
|
68
|
+
)
|
69
|
+
step_to(session: current_session.flow.current_state.redirects_to)
|
70
|
+
return
|
71
|
+
end
|
72
|
+
|
61
73
|
run_callbacks :action do
|
62
74
|
begin
|
63
75
|
flow_controller.send(@action_name)
|
@@ -76,8 +88,8 @@ module Stealth
|
|
76
88
|
raise ArgumentError, "Please specify your step_to_in `delay` parameter using ActiveSupport::Duration, e.g. `1.day` or `5.hours`"
|
77
89
|
end
|
78
90
|
|
79
|
-
Stealth::ScheduledReplyJob.perform_in(delay, current_service,
|
80
|
-
Stealth::Logger.l(topic: "session", message: "User #{
|
91
|
+
Stealth::ScheduledReplyJob.perform_in(delay, current_service, current_session_id, flow, state)
|
92
|
+
Stealth::Logger.l(topic: "session", message: "User #{current_session_id}: scheduled session step to #{flow}->#{state} in #{delay} seconds")
|
81
93
|
end
|
82
94
|
|
83
95
|
def step_to_at(timestamp, session: nil, flow: nil, state: nil)
|
@@ -87,8 +99,8 @@ module Stealth
|
|
87
99
|
raise ArgumentError, "Please specify your step_to_at `timestamp` parameter as a DateTime"
|
88
100
|
end
|
89
101
|
|
90
|
-
Stealth::ScheduledReplyJob.perform_at(timestamp, current_service,
|
91
|
-
Stealth::Logger.l(topic: "session", message: "User #{
|
102
|
+
Stealth::ScheduledReplyJob.perform_at(timestamp, current_service, current_session_id, flow, state)
|
103
|
+
Stealth::Logger.l(topic: "session", message: "User #{current_session_id}: scheduled session step to #{flow}->#{state} at #{timestamp.iso8601}")
|
92
104
|
end
|
93
105
|
|
94
106
|
def step_to(session: nil, flow: nil, state: nil)
|
@@ -104,7 +116,7 @@ module Stealth
|
|
104
116
|
private
|
105
117
|
|
106
118
|
def update_session(flow:, state:)
|
107
|
-
@current_session = Stealth::Session.new(user_id:
|
119
|
+
@current_session = Stealth::Session.new(user_id: current_session_id)
|
108
120
|
@progressed = :updated_session
|
109
121
|
@current_session.set(flow: flow, state: state)
|
110
122
|
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Stealth
|
5
|
+
class Controller
|
6
|
+
|
7
|
+
module DynamicDelay
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
SHORT_DELAY = 3.0
|
11
|
+
STANDARD_DELAY = 4.3
|
12
|
+
LONG_DELAY = 7.0
|
13
|
+
|
14
|
+
included do
|
15
|
+
def dynamic_delay(service_replies:, position:)
|
16
|
+
if position <= 0
|
17
|
+
calculate_delay(previous_reply: {})
|
18
|
+
else
|
19
|
+
calculate_delay(previous_reply: service_replies[position - 1])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def calculate_delay(previous_reply:)
|
26
|
+
case previous_reply['reply_type']
|
27
|
+
when 'text'
|
28
|
+
calculate_delay_from_text(previous_reply['text'])
|
29
|
+
when 'image'
|
30
|
+
STANDARD_DELAY
|
31
|
+
when 'audio'
|
32
|
+
STANDARD_DELAY
|
33
|
+
when 'video'
|
34
|
+
STANDARD_DELAY
|
35
|
+
when 'file'
|
36
|
+
STANDARD_DELAY
|
37
|
+
when 'cards'
|
38
|
+
STANDARD_DELAY
|
39
|
+
when 'list'
|
40
|
+
STANDARD_DELAY
|
41
|
+
when nil
|
42
|
+
SHORT_DELAY
|
43
|
+
else
|
44
|
+
SHORT_DELAY
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def calculate_delay_from_text(text)
|
50
|
+
case text.size
|
51
|
+
when 0..55
|
52
|
+
SHORT_DELAY
|
53
|
+
when 56..140
|
54
|
+
STANDARD_DELAY
|
55
|
+
when 141..256
|
56
|
+
STANDARD_DELAY * 1.5
|
57
|
+
else
|
58
|
+
LONG_DELAY
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|