stealth 0.10.6 → 1.0.0.pre1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +22 -22
- data/README.md +46 -1
- data/VERSION +1 -1
- data/lib/stealth/base.rb +36 -16
- data/lib/stealth/controller/controller.rb +1 -61
- data/lib/stealth/controller/replies.rb +99 -0
- data/lib/stealth/generators/builder/Gemfile +1 -1
- data/lib/stealth/generators/builder/Procfile.dev +2 -0
- data/lib/stealth/generators/builder/bot/flow_map.rb.tt +1 -1
- data/lib/stealth/generators/builder/bot/replies/catch_alls/level1.yml +2 -0
- data/lib/stealth/generators/builder/bot/replies/goodbyes/say_goodbye.yml +2 -0
- data/lib/stealth/generators/builder/bot/replies/hellos/say_hello.yml +1 -1
- data/lib/stealth/generators/builder/readme.md +9 -1
- data/lib/stealth/generators/builder.rb +2 -1
- data/lib/stealth/generators/generate/flow/controllers/controller.tt +1 -1
- data/lib/stealth/generators/generate/flow/helpers/helper.tt +1 -1
- data/lib/stealth/generators/generate.rb +5 -5
- data/lib/stealth/server.rb +19 -1
- data/lib/stealth/service_reply.rb +19 -8
- data/spec/controller/{state_transitions_spec.rb → controller_spec.rb} +1 -1
- data/spec/controller/replies_spec.rb +153 -0
- data/spec/replies/{nested_reply_with_erb.yml → hello.yml.erb} +2 -3
- data/spec/replies/messages/say_offer.yml +6 -0
- data/spec/replies/messages/say_oi.yml.erb +15 -0
- data/spec/service_reply_spec.rb +63 -5
- data/stealth.gemspec +3 -3
- metadata +22 -14
- data/lib/stealth/generators/builder/bot/replies/catch_alls/catch_all.yml +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a447e930b543fe9125bb09baaaf12f160f5fa715e346fd6b304285f5a31f56f2
|
|
4
|
+
data.tar.gz: f5ff084dcb475b87ff972458751b16cc05779bed6eebf67f3feb15ba97909d68
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ad24b962addcbe10da90dc44a8b0220c7ced906c42a525f45d37f4ee9baad43600b968cae7fdabc2f5a5cad4279718203ec49680a396c6fa8b24429a6f8dc891
|
|
7
|
+
data.tar.gz: 207b7e854c327ef050a013017da9e284d820f3cac5819af8558ea9b7a86c8f252bef1fb7fe9bd8741fb5119d56be8523ae19930f0403d33f3e7b99e11ba132ab
|
data/Gemfile.lock
CHANGED
|
@@ -1,55 +1,55 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
stealth (0.10.
|
|
5
|
-
activesupport (~> 5.2.0
|
|
4
|
+
stealth (0.10.6)
|
|
5
|
+
activesupport (~> 5.2.0)
|
|
6
6
|
multi_json (~> 1.12)
|
|
7
7
|
puma (~> 3.10)
|
|
8
8
|
sidekiq (~> 5.0)
|
|
9
|
-
sinatra (~> 2.0)
|
|
9
|
+
sinatra (~> 2.0.1)
|
|
10
10
|
thor (~> 0.20)
|
|
11
11
|
|
|
12
12
|
GEM
|
|
13
13
|
remote: https://rubygems.org/
|
|
14
14
|
specs:
|
|
15
|
-
activesupport (5.2.0
|
|
15
|
+
activesupport (5.2.0)
|
|
16
16
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
17
|
-
i18n (
|
|
17
|
+
i18n (>= 0.7, < 2)
|
|
18
18
|
minitest (~> 5.1)
|
|
19
19
|
tzinfo (~> 1.1)
|
|
20
20
|
concurrent-ruby (1.0.5)
|
|
21
21
|
connection_pool (2.2.1)
|
|
22
22
|
diff-lcs (1.3)
|
|
23
|
-
i18n (0.
|
|
23
|
+
i18n (1.0.0)
|
|
24
24
|
concurrent-ruby (~> 1.0)
|
|
25
25
|
minitest (5.11.3)
|
|
26
26
|
mock_redis (0.17.3)
|
|
27
27
|
multi_json (1.13.1)
|
|
28
28
|
mustermann (1.0.2)
|
|
29
|
-
oj (3.
|
|
30
|
-
puma (3.11.
|
|
31
|
-
rack (2.0.
|
|
29
|
+
oj (3.5.0)
|
|
30
|
+
puma (3.11.3)
|
|
31
|
+
rack (2.0.4)
|
|
32
32
|
rack-protection (2.0.1)
|
|
33
33
|
rack
|
|
34
|
-
rack-test (0.
|
|
34
|
+
rack-test (0.8.3)
|
|
35
35
|
rack (>= 1.0, < 3)
|
|
36
36
|
redis (4.0.1)
|
|
37
|
-
rspec (3.
|
|
38
|
-
rspec-core (~> 3.
|
|
39
|
-
rspec-expectations (~> 3.
|
|
40
|
-
rspec-mocks (~> 3.
|
|
41
|
-
rspec-core (3.
|
|
42
|
-
rspec-support (~> 3.
|
|
43
|
-
rspec-expectations (3.
|
|
37
|
+
rspec (3.7.0)
|
|
38
|
+
rspec-core (~> 3.7.0)
|
|
39
|
+
rspec-expectations (~> 3.7.0)
|
|
40
|
+
rspec-mocks (~> 3.7.0)
|
|
41
|
+
rspec-core (3.7.1)
|
|
42
|
+
rspec-support (~> 3.7.0)
|
|
43
|
+
rspec-expectations (3.7.0)
|
|
44
44
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
45
|
-
rspec-support (~> 3.
|
|
46
|
-
rspec-mocks (3.
|
|
45
|
+
rspec-support (~> 3.7.0)
|
|
46
|
+
rspec-mocks (3.7.0)
|
|
47
47
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
48
|
-
rspec-support (~> 3.
|
|
49
|
-
rspec-support (3.
|
|
48
|
+
rspec-support (~> 3.7.0)
|
|
49
|
+
rspec-support (3.7.1)
|
|
50
50
|
rspec_junit_formatter (0.3.0)
|
|
51
51
|
rspec-core (>= 2, < 4, != 2.12.0)
|
|
52
|
-
sidekiq (5.1.
|
|
52
|
+
sidekiq (5.1.3)
|
|
53
53
|
concurrent-ruby (~> 1.0)
|
|
54
54
|
connection_pool (~> 2.2, >= 2.2.0)
|
|
55
55
|
rack-protection (>= 1.5.0)
|
data/README.md
CHANGED
|
@@ -1 +1,46 @@
|
|
|
1
|
-
# Stealth
|
|
1
|
+
# <a href='https://hellostealth.org'><img src='logo.svg' height='120' alt='Stealth Logo' aria-label='hellostealth.org' /></a>
|
|
2
|
+
|
|
3
|
+
Stealth is a Ruby based framework for creating conversational (voice & chat) bots. It's design is inspired by Ruby on Rails's philosophy of convention over configuration. It has an MVC architecture with the slight caveat that `views` are aptly named `replies`.
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
|
|
7
|
+
Getting started with Stealth is simple:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
> gem install stealth
|
|
11
|
+
> stealth new <bot>
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Service Integrations
|
|
15
|
+
|
|
16
|
+
Stealth is extensible. All service integrations are split out into separate Ruby Gems. Things like analytics and natural language processing ([NLP](https://en.wikipedia.org/wiki/Natural-language_processing)) can be added in as gems as well.
|
|
17
|
+
|
|
18
|
+
Currently, there are gems for:
|
|
19
|
+
|
|
20
|
+
### Messaging
|
|
21
|
+
* [Facebook Messenger](https://github.com/hellostealth/stealth-facebook)
|
|
22
|
+
* [Twilio SMS](https://github.com/hellostealth/stealth-twilio)
|
|
23
|
+
|
|
24
|
+
### Analytics
|
|
25
|
+
* [Mixpanel](https://github.com/hellostealth/stealth-mixpanel)
|
|
26
|
+
|
|
27
|
+
## Docs
|
|
28
|
+
|
|
29
|
+
You can find our full docs [here](https://docs.hellostealth.org).
|
|
30
|
+
|
|
31
|
+
## Thanks
|
|
32
|
+
|
|
33
|
+
Stealth wouldn't exist without the great work of many other open source projects and people including:
|
|
34
|
+
|
|
35
|
+
* [Ruby](https://www.ruby-lang.org/) for creating our favorite programming language;
|
|
36
|
+
* [Ruby on Rails](http://rubyonrails.org) for projects like `ActiveRecord` and serving as an inspiration;
|
|
37
|
+
* [Thor](http://whatisthor.com) for providing us with CLI tools and generators;
|
|
38
|
+
* [Sinatra](http://sinatrarb.com) for providing a fantastic, modular way for handling HTTP requests;
|
|
39
|
+
* [Sidekiq](https://sidekiq.org) for the super quick background jobs;
|
|
40
|
+
* [Dr. Robert Ford](http://westworld.wikia.com/wiki/Robert_Ford) a.k.a. Anthony Hopkins.
|
|
41
|
+
|
|
42
|
+
## License
|
|
43
|
+
|
|
44
|
+
"Stealth" and the Stealth logo are copyright (c) 2018 The Black Ops Bureau Inc.
|
|
45
|
+
|
|
46
|
+
Stealth source code is released under the MIT License.
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
1.0.0.pre1
|
data/lib/stealth/base.rb
CHANGED
|
@@ -11,20 +11,6 @@ require 'stealth/version'
|
|
|
11
11
|
require 'stealth/errors'
|
|
12
12
|
require 'stealth/logger'
|
|
13
13
|
require 'stealth/configuration'
|
|
14
|
-
require 'stealth/jobs'
|
|
15
|
-
require 'stealth/dispatcher'
|
|
16
|
-
require 'stealth/server'
|
|
17
|
-
require 'stealth/reply'
|
|
18
|
-
require 'stealth/scheduled_reply'
|
|
19
|
-
require 'stealth/service_reply'
|
|
20
|
-
require 'stealth/service_message'
|
|
21
|
-
require 'stealth/session'
|
|
22
|
-
require 'stealth/controller/callbacks'
|
|
23
|
-
require 'stealth/controller/catch_all'
|
|
24
|
-
require 'stealth/controller/helpers'
|
|
25
|
-
require 'stealth/controller/controller'
|
|
26
|
-
require 'stealth/flow/base'
|
|
27
|
-
require 'stealth/services/base_client'
|
|
28
14
|
|
|
29
15
|
module Stealth
|
|
30
16
|
|
|
@@ -36,7 +22,25 @@ module Stealth
|
|
|
36
22
|
@root ||= File.expand_path(Pathname.new(Dir.pwd))
|
|
37
23
|
end
|
|
38
24
|
|
|
25
|
+
def self.reloader
|
|
26
|
+
@reloader ||= begin
|
|
27
|
+
if Stealth.env == 'development'
|
|
28
|
+
ActiveSupport::Dependencies.mechanism = :load
|
|
29
|
+
ActiveSupport::Dependencies.autoload_paths = Dir["bot/**/*.rb"]
|
|
30
|
+
# @reloader = ActiveSupport::FileUpdateChecker.new(Dir["bot/**/*.rb"]) do
|
|
31
|
+
# puts "YAAASSS"
|
|
32
|
+
# end
|
|
33
|
+
# ActiveSupport::Dependencies.autoload_paths = %w[bot/controllers bot/helpers bot/models]
|
|
34
|
+
# ActiveSupport::Dependencies.autoload_paths = [File.join(Stealth.root, "bot")]
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
39
|
def self.boot
|
|
40
|
+
if Stealth.env == 'development'
|
|
41
|
+
ActiveSupport::Dependencies.mechanism = :load
|
|
42
|
+
ActiveSupport::Dependencies.autoload_paths = Dir["bot/**/*.rb"]
|
|
43
|
+
end
|
|
40
44
|
load_environment
|
|
41
45
|
end
|
|
42
46
|
|
|
@@ -72,14 +76,14 @@ module Stealth
|
|
|
72
76
|
require File.join(Stealth.root, 'config', 'boot')
|
|
73
77
|
require_directory("config/initializers")
|
|
74
78
|
# Require explicitly to ensure it loads first
|
|
75
|
-
|
|
79
|
+
require_dependency File.join(Stealth.root, 'bot', 'controllers', 'bot_controller')
|
|
76
80
|
require_directory("bot")
|
|
77
81
|
end
|
|
78
82
|
|
|
79
83
|
private
|
|
80
84
|
|
|
81
85
|
def self.require_directory(directory)
|
|
82
|
-
for_each_file_in(directory) { |file|
|
|
86
|
+
for_each_file_in(directory) { |file| require_dependency(file) }
|
|
83
87
|
end
|
|
84
88
|
|
|
85
89
|
def self.for_each_file_in(directory, &blk)
|
|
@@ -91,3 +95,19 @@ module Stealth
|
|
|
91
95
|
end
|
|
92
96
|
|
|
93
97
|
end
|
|
98
|
+
|
|
99
|
+
require 'stealth/jobs'
|
|
100
|
+
require 'stealth/dispatcher'
|
|
101
|
+
require 'stealth/server'
|
|
102
|
+
require 'stealth/reply'
|
|
103
|
+
require 'stealth/scheduled_reply'
|
|
104
|
+
require 'stealth/service_reply'
|
|
105
|
+
require 'stealth/service_message'
|
|
106
|
+
require 'stealth/session'
|
|
107
|
+
require 'stealth/controller/callbacks'
|
|
108
|
+
require 'stealth/controller/replies'
|
|
109
|
+
require 'stealth/controller/catch_all'
|
|
110
|
+
require 'stealth/controller/helpers'
|
|
111
|
+
require 'stealth/controller/controller'
|
|
112
|
+
require 'stealth/flow/base'
|
|
113
|
+
require 'stealth/services/base_client'
|
|
@@ -5,6 +5,7 @@ module Stealth
|
|
|
5
5
|
class Controller
|
|
6
6
|
|
|
7
7
|
include Stealth::Controller::Callbacks
|
|
8
|
+
include Stealth::Controller::Replies
|
|
8
9
|
include Stealth::Controller::CatchAll
|
|
9
10
|
include Stealth::Controller::Helpers
|
|
10
11
|
|
|
@@ -35,37 +36,6 @@ module Stealth
|
|
|
35
36
|
raise(Stealth::Errors::ControllerRoutingNotImplemented, "Please implement `route` method in BotController")
|
|
36
37
|
end
|
|
37
38
|
|
|
38
|
-
def send_replies
|
|
39
|
-
service_reply = Stealth::ServiceReply.new(
|
|
40
|
-
recipient_id: current_user_id,
|
|
41
|
-
yaml_reply: action_replies,
|
|
42
|
-
context: binding
|
|
43
|
-
)
|
|
44
|
-
|
|
45
|
-
for reply in service_reply.replies do
|
|
46
|
-
handler = reply_handler.new(
|
|
47
|
-
recipient_id: current_user_id,
|
|
48
|
-
reply: reply
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
translated_reply = handler.send(reply.reply_type)
|
|
52
|
-
client = service_client.new(reply: translated_reply)
|
|
53
|
-
client.transmit
|
|
54
|
-
|
|
55
|
-
# If this was a 'delay' type of reply, let's respect the delay
|
|
56
|
-
if reply.reply_type == 'delay'
|
|
57
|
-
begin
|
|
58
|
-
sleep_duration = Float(reply["duration"])
|
|
59
|
-
sleep(sleep_duration)
|
|
60
|
-
rescue ArgumentError, TypeError
|
|
61
|
-
raise(ArgumentError, 'Invalid duration specified. Duration must be a float')
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
@progressed = :sent_replies
|
|
67
|
-
end
|
|
68
|
-
|
|
69
39
|
def flow_controller
|
|
70
40
|
@flow_controller ||= begin
|
|
71
41
|
flow_controller = [current_session.flow_string.pluralize, 'controller'].join('_').classify.constantize
|
|
@@ -132,36 +102,6 @@ module Stealth
|
|
|
132
102
|
|
|
133
103
|
private
|
|
134
104
|
|
|
135
|
-
def reply_handler
|
|
136
|
-
begin
|
|
137
|
-
Kernel.const_get("Stealth::Services::#{current_service.classify}::ReplyHandler")
|
|
138
|
-
rescue NameError
|
|
139
|
-
raise(Stealth::Errors::ServiceNotRecognized, "The service '#{current_service}' was not recognized")
|
|
140
|
-
end
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
def service_client
|
|
144
|
-
begin
|
|
145
|
-
Kernel.const_get("Stealth::Services::#{current_service.classify}::Client")
|
|
146
|
-
rescue NameError
|
|
147
|
-
raise(Stealth::Errors::ServiceNotRecognized, "The service '#{current_service}' was not recognized")
|
|
148
|
-
end
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
def replies_folder
|
|
152
|
-
current_session.flow_string.underscore.pluralize
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
def action_replies
|
|
156
|
-
reply_file_path = File.join(Stealth.root, 'bot', 'replies', replies_folder, "#{current_session.state_string}.yml")
|
|
157
|
-
|
|
158
|
-
begin
|
|
159
|
-
File.read(reply_file_path)
|
|
160
|
-
rescue Errno::ENOENT
|
|
161
|
-
raise(Stealth::Errors::ReplyNotFound, "Could not find a reply in #{reply_file_path}")
|
|
162
|
-
end
|
|
163
|
-
end
|
|
164
|
-
|
|
165
105
|
def update_session(flow:, state:)
|
|
166
106
|
Stealth::Logger.l(topic: "session", message: "User #{current_user_id}: updating session to #{flow}->#{state}")
|
|
167
107
|
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Stealth
|
|
5
|
+
class Controller
|
|
6
|
+
module Replies
|
|
7
|
+
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
|
|
10
|
+
included do
|
|
11
|
+
|
|
12
|
+
class_attribute :_preprocessors, default: [:erb]
|
|
13
|
+
class_attribute :_replies_path, default: [Stealth.root, 'bot', 'replies']
|
|
14
|
+
|
|
15
|
+
def send_replies
|
|
16
|
+
yaml_reply, preprocessor = action_replies
|
|
17
|
+
|
|
18
|
+
service_reply = Stealth::ServiceReply.new(
|
|
19
|
+
recipient_id: current_user_id,
|
|
20
|
+
yaml_reply: yaml_reply,
|
|
21
|
+
preprocessor: preprocessor,
|
|
22
|
+
context: binding
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
for reply in service_reply.replies do
|
|
26
|
+
handler = reply_handler.new(
|
|
27
|
+
recipient_id: current_user_id,
|
|
28
|
+
reply: reply
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
translated_reply = handler.send(reply.reply_type)
|
|
32
|
+
client = service_client.new(reply: translated_reply)
|
|
33
|
+
client.transmit
|
|
34
|
+
|
|
35
|
+
# If this was a 'delay' type of reply, we insert the delay
|
|
36
|
+
if reply.reply_type == 'delay'
|
|
37
|
+
begin
|
|
38
|
+
sleep_duration = Float(reply["duration"])
|
|
39
|
+
sleep(sleep_duration)
|
|
40
|
+
rescue ArgumentError, TypeError
|
|
41
|
+
raise(ArgumentError, 'Invalid duration specified. Duration must be a float')
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
@progressed = :sent_replies
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def service_client
|
|
52
|
+
begin
|
|
53
|
+
Kernel.const_get("Stealth::Services::#{current_service.classify}::Client")
|
|
54
|
+
rescue NameError
|
|
55
|
+
raise(Stealth::Errors::ServiceNotRecognized, "The service '#{current_service}' was not recognized")
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def reply_handler
|
|
60
|
+
begin
|
|
61
|
+
Kernel.const_get("Stealth::Services::#{current_service.classify}::ReplyHandler")
|
|
62
|
+
rescue NameError
|
|
63
|
+
raise(Stealth::Errors::ServiceNotRecognized, "The service '#{current_service}' was not recognized")
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def replies_folder
|
|
68
|
+
current_session.flow_string.underscore.pluralize
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def action_replies
|
|
72
|
+
reply_dir = [*self._replies_path, replies_folder]
|
|
73
|
+
reply_filename = "#{current_session.state_string}.yml"
|
|
74
|
+
reply_file_path = File.join(*reply_dir, reply_filename)
|
|
75
|
+
selected_preprocessor = :none
|
|
76
|
+
|
|
77
|
+
for preprocessor in self.class._preprocessors do
|
|
78
|
+
selected_filepath = File.join(*reply_dir, [reply_filename, preprocessor.to_s].join('.'))
|
|
79
|
+
if File.exists?(selected_filepath)
|
|
80
|
+
reply_file_path = selected_filepath
|
|
81
|
+
selected_preprocessor = preprocessor
|
|
82
|
+
break
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
begin
|
|
87
|
+
file_contents = File.read(reply_file_path)
|
|
88
|
+
rescue Errno::ENOENT
|
|
89
|
+
raise(Stealth::Errors::ReplyNotFound, "Could not find a reply in #{reply_file_path}")
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
return file_contents, selected_preprocessor
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
end # instance methods
|
|
96
|
+
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
- reply_type: text
|
|
2
|
-
text:
|
|
2
|
+
text: Hello World!
|
|
@@ -1 +1,9 @@
|
|
|
1
|
-
# Stealth
|
|
1
|
+
# <a href='https://hellostealth.org'><img src='http://assets.blackops.nyc/stealth/logo.svg' height='120' alt='Stealth Logo' aria-label='hellostealth.org' /></a>
|
|
2
|
+
|
|
3
|
+
To boot this bot locally, we recommend the following:
|
|
4
|
+
|
|
5
|
+
1. `gem install foreman`
|
|
6
|
+
2. Start Redis
|
|
7
|
+
3. `foreman start -f Procfile.dev`
|
|
8
|
+
|
|
9
|
+
Using `foreman` will start the web server and Sidekiq processes together.
|
|
@@ -25,7 +25,8 @@ module Stealth
|
|
|
25
25
|
# Miscellaneous Files
|
|
26
26
|
copy_file "config.ru", "#{name}/config.ru"
|
|
27
27
|
copy_file "Gemfile", "#{name}/Gemfile"
|
|
28
|
-
copy_file "
|
|
28
|
+
copy_file "README.md", "#{name}/README.md"
|
|
29
|
+
copy_file "Procfile.dev", "#{name}/Procfile.dev"
|
|
29
30
|
end
|
|
30
31
|
|
|
31
32
|
def change_directory_bundle
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
module <%= name.camelize %>Helper < BotHelper
|
|
1
|
+
module <%= name.underscore.camelize %>Helper < BotHelper
|
|
2
2
|
end
|
|
@@ -13,19 +13,19 @@ module Stealth
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def create_controller
|
|
16
|
-
template('controllers/controller.tt', "controllers/#{name.pluralize}_controller.rb")
|
|
16
|
+
template('controllers/controller.tt', "bot/controllers/#{name.pluralize}_controller.rb")
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def create_replies
|
|
20
20
|
# Sample Ask Reply
|
|
21
|
-
template('replies/ask_reply.tt', "replies/#{name.pluralize}/ask_example.yml")
|
|
21
|
+
template('replies/ask_reply.tt', "bot/replies/#{name.pluralize}/ask_example.yml")
|
|
22
22
|
# Sample Say Replies
|
|
23
|
-
template('replies/say_yes_reply.tt', "replies/#{name.pluralize}/say_yes_example.yml")
|
|
24
|
-
template('replies/say_no_reply.tt', "replies/#{name.pluralize}/say_no_example.yml")
|
|
23
|
+
template('replies/say_yes_reply.tt', "bot/replies/#{name.pluralize}/say_yes_example.yml")
|
|
24
|
+
template('replies/say_no_reply.tt', "bot/replies/#{name.pluralize}/say_no_example.yml")
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def create_helper
|
|
28
|
-
template('helpers/helper.tt', "helpers/#{name}_helper.rb")
|
|
28
|
+
template('helpers/helper.tt', "bot/helpers/#{name}_helper.rb")
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def edit_flow_map
|
data/lib/stealth/server.rb
CHANGED
|
@@ -13,10 +13,28 @@ module Stealth
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
get '/' do
|
|
16
|
-
|
|
16
|
+
<<~WELCOME
|
|
17
|
+
<html>
|
|
18
|
+
<head>
|
|
19
|
+
<title>Stealth</title>
|
|
20
|
+
</head>
|
|
21
|
+
<body>
|
|
22
|
+
<center>
|
|
23
|
+
<a href='https://hellostealth.org'>
|
|
24
|
+
<img src='http://assets.blackops.nyc/stealth/logo.svg' height='120' alt='Stealth Logo' aria-label='hellostealth.org' />
|
|
25
|
+
</a>
|
|
26
|
+
</center>
|
|
27
|
+
</body>
|
|
28
|
+
</html>
|
|
29
|
+
WELCOME
|
|
17
30
|
end
|
|
18
31
|
|
|
19
32
|
get_or_post '/incoming/:service' do
|
|
33
|
+
if Stealth.env == 'development'
|
|
34
|
+
Stealth::Logger.l(topic: "ARGF", message: "RELOADING")
|
|
35
|
+
ActiveSupport::Dependencies.clear
|
|
36
|
+
end
|
|
37
|
+
|
|
20
38
|
Stealth::Logger.l(topic: "incoming", message: "Received webhook from #{params[:service]}")
|
|
21
39
|
|
|
22
40
|
# JSON params need to be parsed and added to the params
|
|
@@ -4,18 +4,21 @@
|
|
|
4
4
|
module Stealth
|
|
5
5
|
class ServiceReply
|
|
6
6
|
|
|
7
|
-
attr_accessor :recipient_id, :replies
|
|
7
|
+
attr_accessor :recipient_id, :replies, :yaml_reply, :context
|
|
8
8
|
|
|
9
|
-
def initialize(recipient_id:, yaml_reply:, context:)
|
|
9
|
+
def initialize(recipient_id:, yaml_reply:, context:, preprocessor: :none)
|
|
10
10
|
@recipient_id = recipient_id
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
@yaml_reply = yaml_reply
|
|
12
|
+
@context = context
|
|
13
|
+
|
|
14
|
+
processed_reply = case preprocessor
|
|
15
|
+
when :erb
|
|
16
|
+
preprocess_erb
|
|
17
|
+
when :none
|
|
18
|
+
@yaml_reply
|
|
16
19
|
end
|
|
17
20
|
|
|
18
|
-
@replies = load_replies(YAML.load(
|
|
21
|
+
@replies = load_replies(YAML.load(processed_reply))
|
|
19
22
|
end
|
|
20
23
|
|
|
21
24
|
private
|
|
@@ -26,5 +29,13 @@ module Stealth
|
|
|
26
29
|
end
|
|
27
30
|
end
|
|
28
31
|
|
|
32
|
+
def preprocess_erb
|
|
33
|
+
begin
|
|
34
|
+
ERB.new(yaml_reply).result(context)
|
|
35
|
+
rescue NameError => e
|
|
36
|
+
raise(Stealth::Errors::UndefinedVariable, e.message)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
29
40
|
end
|
|
30
41
|
end
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', '/spec_helper'))
|
|
5
|
+
|
|
6
|
+
describe "Stealth::Controller replies" do
|
|
7
|
+
|
|
8
|
+
Stealth::Controller._replies_path = File.expand_path("../replies", __dir__)
|
|
9
|
+
|
|
10
|
+
let(:facebook_message) { SampleMessage.new(service: 'facebook') }
|
|
11
|
+
let(:controller) { MessagesController.new(service_message: facebook_message.message_with_text) }
|
|
12
|
+
|
|
13
|
+
# Stub out base Facebook integration
|
|
14
|
+
module Stealth
|
|
15
|
+
module Services
|
|
16
|
+
module Facebook
|
|
17
|
+
class ReplyHandler
|
|
18
|
+
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class Client
|
|
22
|
+
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class MessagesController < Stealth::Controller
|
|
29
|
+
def say_oi
|
|
30
|
+
@first_name = "Presley"
|
|
31
|
+
send_replies
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def say_offer
|
|
35
|
+
send_replies
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
describe "class attributes" do
|
|
40
|
+
it "should have altered the _replies_path class attribute" do
|
|
41
|
+
expect(MessagesController._replies_path).to eq(File.expand_path("../replies", __dir__))
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "should have altered the _preprocessors class attribute" do
|
|
45
|
+
expect(MessagesController._preprocessors).to eq([:erb])
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
describe "action_replies" do
|
|
50
|
+
it "should select the :erb preprocessor when reply extension is .yml" do
|
|
51
|
+
allow(controller.current_session).to receive(:flow_string).and_return("message")
|
|
52
|
+
allow(controller.current_session).to receive(:state_string).and_return("say_oi")
|
|
53
|
+
file_contents, selected_preprocessor = controller.send(:action_replies)
|
|
54
|
+
|
|
55
|
+
expect(selected_preprocessor).to eq(:erb)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it "should select the :none preprocessor when there is no reply extension" do
|
|
59
|
+
allow(controller.current_session).to receive(:flow_string).and_return("message")
|
|
60
|
+
allow(controller.current_session).to receive(:state_string).and_return("say_offer")
|
|
61
|
+
file_contents, selected_preprocessor = controller.send(:action_replies)
|
|
62
|
+
|
|
63
|
+
expect(selected_preprocessor).to eq(:none)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "should read the reply's file contents" do
|
|
67
|
+
allow(controller.current_session).to receive(:flow_string).and_return("message")
|
|
68
|
+
allow(controller.current_session).to receive(:state_string).and_return("say_offer")
|
|
69
|
+
file_contents, selected_preprocessor = controller.send(:action_replies)
|
|
70
|
+
|
|
71
|
+
expect(file_contents).to eq(File.read(File.expand_path("../replies/messages/say_offer.yml", __dir__)))
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
describe "reply with ERB" do
|
|
76
|
+
let(:stubbed_handler) { double("handler") }
|
|
77
|
+
let(:stubbed_client) { double("client") }
|
|
78
|
+
|
|
79
|
+
before(:each) do
|
|
80
|
+
allow(Stealth::Services::Facebook::ReplyHandler).to receive(:new).and_return(stubbed_handler)
|
|
81
|
+
allow(Stealth::Services::Facebook::Client).to receive(:new).and_return(stubbed_client)
|
|
82
|
+
allow(controller.current_session).to receive(:flow_string).and_return("message")
|
|
83
|
+
allow(controller.current_session).to receive(:state_string).and_return("say_oi")
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it "should translate each reply_type in the reply" do
|
|
87
|
+
allow(stubbed_client).to receive(:transmit).and_return(true)
|
|
88
|
+
allow(controller).to receive(:sleep).and_return(true)
|
|
89
|
+
|
|
90
|
+
expect(stubbed_handler).to receive(:text).exactly(3).times
|
|
91
|
+
expect(stubbed_handler).to receive(:delay).exactly(2).times
|
|
92
|
+
controller.say_oi
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "should transmit each reply_type in the reply" do
|
|
96
|
+
allow(stubbed_handler).to receive(:text).exactly(3).times
|
|
97
|
+
allow(stubbed_handler).to receive(:delay).exactly(2).times
|
|
98
|
+
allow(controller).to receive(:sleep).and_return(true)
|
|
99
|
+
|
|
100
|
+
expect(stubbed_client).to receive(:transmit).exactly(5).times
|
|
101
|
+
controller.say_oi
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it "should sleep on delays" do
|
|
105
|
+
allow(stubbed_handler).to receive(:text).exactly(3).times
|
|
106
|
+
allow(stubbed_handler).to receive(:delay).exactly(2).times
|
|
107
|
+
allow(stubbed_client).to receive(:transmit).exactly(5).times
|
|
108
|
+
|
|
109
|
+
expect(controller).to receive(:sleep).exactly(2).times
|
|
110
|
+
controller.say_oi
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
describe "plain reply" do
|
|
115
|
+
let(:stubbed_handler) { double("handler") }
|
|
116
|
+
let(:stubbed_client) { double("client") }
|
|
117
|
+
|
|
118
|
+
before(:each) do
|
|
119
|
+
allow(Stealth::Services::Facebook::ReplyHandler).to receive(:new).and_return(stubbed_handler)
|
|
120
|
+
allow(Stealth::Services::Facebook::Client).to receive(:new).and_return(stubbed_client)
|
|
121
|
+
allow(controller.current_session).to receive(:flow_string).and_return("message")
|
|
122
|
+
allow(controller.current_session).to receive(:state_string).and_return("say_offer")
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
it "should translate each reply_type in the reply" do
|
|
126
|
+
allow(stubbed_client).to receive(:transmit).and_return(true)
|
|
127
|
+
allow(controller).to receive(:sleep).and_return(true)
|
|
128
|
+
|
|
129
|
+
expect(stubbed_handler).to receive(:text).exactly(2).times
|
|
130
|
+
expect(stubbed_handler).to receive(:delay).exactly(1).times
|
|
131
|
+
controller.say_offer
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
it "should transmit each reply_type in the reply" do
|
|
135
|
+
allow(stubbed_handler).to receive(:text).exactly(2).times
|
|
136
|
+
allow(stubbed_handler).to receive(:delay).exactly(1).times
|
|
137
|
+
allow(controller).to receive(:sleep).and_return(true)
|
|
138
|
+
|
|
139
|
+
expect(stubbed_client).to receive(:transmit).exactly(3).times
|
|
140
|
+
controller.say_offer
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
it "should sleep on delays" do
|
|
144
|
+
allow(stubbed_handler).to receive(:text).exactly(2).times
|
|
145
|
+
allow(stubbed_handler).to receive(:delay).exactly(1).times
|
|
146
|
+
allow(stubbed_client).to receive(:transmit).exactly(3).times
|
|
147
|
+
|
|
148
|
+
expect(controller).to receive(:sleep).exactly(1).times
|
|
149
|
+
controller.say_offer
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
end
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
- reply_type: text
|
|
2
2
|
text: "Hi, <%= first_name %>. Welcome to Stealth bot..."
|
|
3
|
-
-
|
|
3
|
+
- reply_type: delay
|
|
4
4
|
duration: 2
|
|
5
5
|
- reply_type: text
|
|
6
6
|
text: "We offer users an awesome Ruby framework for building chat bots."
|
|
7
|
-
-
|
|
7
|
+
- reply_type: delay
|
|
8
8
|
duration: 2
|
|
9
9
|
- reply_type: text
|
|
10
10
|
text: "What do you think of our bot?"
|
|
@@ -13,4 +13,3 @@
|
|
|
13
13
|
payload: cool
|
|
14
14
|
- text: "Show me more"
|
|
15
15
|
payload: more
|
|
16
|
-
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
- reply_type: text
|
|
2
|
+
text: "Hi, <%= @first_name %>. Welcome to Stealth bot..."
|
|
3
|
+
- reply_type: delay
|
|
4
|
+
duration: 2
|
|
5
|
+
- reply_type: text
|
|
6
|
+
text: "We offer users an awesome Ruby framework for building chat bots."
|
|
7
|
+
- reply_type: delay
|
|
8
|
+
duration: 2
|
|
9
|
+
- reply_type: text
|
|
10
|
+
text: "What do you think of our bot?"
|
|
11
|
+
buttons:
|
|
12
|
+
- text: "Cool"
|
|
13
|
+
payload: cool
|
|
14
|
+
- text: "Show me more"
|
|
15
|
+
payload: more
|
data/spec/service_reply_spec.rb
CHANGED
|
@@ -5,31 +5,89 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
|
|
5
5
|
|
|
6
6
|
describe "Stealth::ServiceReply" do
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
let(:yaml_reply) { File.read(File.join(File.dirname(__FILE__), 'replies', 'nested_reply_with_erb.yml')) }
|
|
8
|
+
let(:recipient_id) { "8b3e0a3c-62f1-401e-8b0f-615c9d256b1f" }
|
|
9
|
+
let(:yaml_reply) { File.read(File.join(File.dirname(__FILE__), 'replies', 'hello.yml.erb')) }
|
|
11
10
|
|
|
11
|
+
describe "nested reply with ERB" do
|
|
12
12
|
it "should load all the replies" do
|
|
13
13
|
first_name = "Presley"
|
|
14
14
|
|
|
15
15
|
service_reply = Stealth::ServiceReply.new(
|
|
16
16
|
recipient_id: recipient_id,
|
|
17
17
|
yaml_reply: yaml_reply,
|
|
18
|
-
context: binding
|
|
18
|
+
context: binding,
|
|
19
|
+
preprocessor: :erb
|
|
19
20
|
)
|
|
20
21
|
|
|
21
22
|
expect(service_reply.replies.size).to eq 5
|
|
22
23
|
end
|
|
23
24
|
|
|
25
|
+
it "should load all replies as Stealth::Reply objects" do
|
|
26
|
+
first_name = "Presley"
|
|
27
|
+
|
|
28
|
+
service_reply = Stealth::ServiceReply.new(
|
|
29
|
+
recipient_id: recipient_id,
|
|
30
|
+
yaml_reply: yaml_reply,
|
|
31
|
+
context: binding,
|
|
32
|
+
preprocessor: :erb
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
expect(service_reply.replies).to all(be_an(Stealth::Reply))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "should replace the ERB tag" do
|
|
39
|
+
first_name = "Presley"
|
|
40
|
+
|
|
41
|
+
service_reply = Stealth::ServiceReply.new(
|
|
42
|
+
recipient_id: recipient_id,
|
|
43
|
+
yaml_reply: yaml_reply,
|
|
44
|
+
context: binding,
|
|
45
|
+
preprocessor: :erb
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
phrase_in_reply = service_reply.replies.first['text']
|
|
49
|
+
expect(phrase_in_reply).to eq "Hi, Presley. Welcome to Stealth bot..."
|
|
50
|
+
end
|
|
51
|
+
|
|
24
52
|
it "should raise Stealth::Errors::UndefinedVariable when local variable is not available" do
|
|
25
53
|
expect {
|
|
26
54
|
service_reply = Stealth::ServiceReply.new(
|
|
27
55
|
recipient_id: recipient_id,
|
|
28
56
|
yaml_reply: yaml_reply,
|
|
29
|
-
context: binding
|
|
57
|
+
context: binding,
|
|
58
|
+
preprocessor: :erb
|
|
30
59
|
)
|
|
31
60
|
}.to raise_error(Stealth::Errors::UndefinedVariable)
|
|
32
61
|
end
|
|
33
62
|
end
|
|
34
63
|
|
|
64
|
+
describe "processing a reply without a preprocessor specified" do
|
|
65
|
+
it "should not replace the ERB tag when no preprocessor is specified" do
|
|
66
|
+
first_name = "Gisele"
|
|
67
|
+
|
|
68
|
+
service_reply = Stealth::ServiceReply.new(
|
|
69
|
+
recipient_id: recipient_id,
|
|
70
|
+
yaml_reply: yaml_reply,
|
|
71
|
+
context: binding
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
phrase_in_reply = service_reply.replies.first['text']
|
|
75
|
+
expect(phrase_in_reply).to eq "Hi, <%= first_name %>. Welcome to Stealth bot..."
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it "should not replace the ERB tag when :none is specified as the preprocessor" do
|
|
79
|
+
first_name = "Gisele"
|
|
80
|
+
|
|
81
|
+
service_reply = Stealth::ServiceReply.new(
|
|
82
|
+
recipient_id: recipient_id,
|
|
83
|
+
yaml_reply: yaml_reply,
|
|
84
|
+
context: binding,
|
|
85
|
+
preprocessor: :none
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
phrase_in_reply = service_reply.replies.first['text']
|
|
89
|
+
expect(phrase_in_reply).to eq "Hi, <%= first_name %>. Welcome to Stealth bot..."
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
35
93
|
end
|
data/stealth.gemspec
CHANGED
|
@@ -6,18 +6,18 @@ Gem::Specification.new do |s|
|
|
|
6
6
|
s.name = 'stealth'
|
|
7
7
|
s.summary = 'Ruby framework for conversational bots'
|
|
8
8
|
s.description = 'Ruby framework for building conversational bots.'
|
|
9
|
-
s.homepage = 'https://github.com/
|
|
9
|
+
s.homepage = 'https://github.com/hellostealth/stealth'
|
|
10
10
|
s.licenses = ['MIT']
|
|
11
11
|
s.version = version
|
|
12
12
|
s.author = 'Mauricio Gomes'
|
|
13
13
|
s.email = 'mauricio@edge14.com'
|
|
14
14
|
|
|
15
|
-
s.add_dependency 'sinatra', '~> 2.0'
|
|
15
|
+
s.add_dependency 'sinatra', '~> 2.0.1'
|
|
16
16
|
s.add_dependency 'puma', '~> 3.10'
|
|
17
17
|
s.add_dependency 'thor', '~> 0.20'
|
|
18
18
|
s.add_dependency 'multi_json', '~> 1.12'
|
|
19
19
|
s.add_dependency 'sidekiq', '~> 5.0'
|
|
20
|
-
s.add_dependency 'activesupport', '~> 5.2.0
|
|
20
|
+
s.add_dependency 'activesupport', '~> 5.2.0'
|
|
21
21
|
|
|
22
22
|
s.add_development_dependency 'rspec', '~> 3.6'
|
|
23
23
|
s.add_development_dependency 'rspec_junit_formatter', '~> 0.3'
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: stealth
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.0.pre1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mauricio Gomes
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2018-04-
|
|
11
|
+
date: 2018-04-19 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: sinatra
|
|
@@ -16,14 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version:
|
|
19
|
+
version: 2.0.1
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version:
|
|
26
|
+
version: 2.0.1
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: puma
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -86,14 +86,14 @@ dependencies:
|
|
|
86
86
|
requirements:
|
|
87
87
|
- - "~>"
|
|
88
88
|
- !ruby/object:Gem::Version
|
|
89
|
-
version: 5.2.0
|
|
89
|
+
version: 5.2.0
|
|
90
90
|
type: :runtime
|
|
91
91
|
prerelease: false
|
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
|
93
93
|
requirements:
|
|
94
94
|
- - "~>"
|
|
95
95
|
- !ruby/object:Gem::Version
|
|
96
|
-
version: 5.2.0
|
|
96
|
+
version: 5.2.0
|
|
97
97
|
- !ruby/object:Gem::Dependency
|
|
98
98
|
name: rspec
|
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -177,6 +177,7 @@ files:
|
|
|
177
177
|
- lib/stealth/controller/catch_all.rb
|
|
178
178
|
- lib/stealth/controller/controller.rb
|
|
179
179
|
- lib/stealth/controller/helpers.rb
|
|
180
|
+
- lib/stealth/controller/replies.rb
|
|
180
181
|
- lib/stealth/dispatcher.rb
|
|
181
182
|
- lib/stealth/errors.rb
|
|
182
183
|
- lib/stealth/flow/base.rb
|
|
@@ -185,13 +186,14 @@ files:
|
|
|
185
186
|
- lib/stealth/flow/state.rb
|
|
186
187
|
- lib/stealth/generators/builder.rb
|
|
187
188
|
- lib/stealth/generators/builder/Gemfile
|
|
189
|
+
- lib/stealth/generators/builder/Procfile.dev
|
|
188
190
|
- lib/stealth/generators/builder/bot/controllers/bot_controller.rb
|
|
189
191
|
- lib/stealth/generators/builder/bot/controllers/catch_alls_controller.rb
|
|
190
192
|
- lib/stealth/generators/builder/bot/controllers/goodbyes_controller.rb
|
|
191
193
|
- lib/stealth/generators/builder/bot/controllers/hellos_controller.rb
|
|
192
194
|
- lib/stealth/generators/builder/bot/flow_map.rb.tt
|
|
193
195
|
- lib/stealth/generators/builder/bot/helpers/bot_helper.rb
|
|
194
|
-
- lib/stealth/generators/builder/bot/replies/catch_alls/
|
|
196
|
+
- lib/stealth/generators/builder/bot/replies/catch_alls/level1.yml
|
|
195
197
|
- lib/stealth/generators/builder/bot/replies/goodbyes/say_goodbye.yml
|
|
196
198
|
- lib/stealth/generators/builder/bot/replies/hellos/say_hello.yml
|
|
197
199
|
- lib/stealth/generators/builder/config.ru
|
|
@@ -224,11 +226,14 @@ files:
|
|
|
224
226
|
- logo.svg
|
|
225
227
|
- spec/configuration_spec.rb
|
|
226
228
|
- spec/controller/callbacks_spec.rb
|
|
229
|
+
- spec/controller/controller_spec.rb
|
|
227
230
|
- spec/controller/helpers_spec.rb
|
|
228
|
-
- spec/controller/
|
|
231
|
+
- spec/controller/replies_spec.rb
|
|
229
232
|
- spec/flow/flow_spec.rb
|
|
230
233
|
- spec/flow/state_spec.rb
|
|
231
|
-
- spec/replies/
|
|
234
|
+
- spec/replies/hello.yml.erb
|
|
235
|
+
- spec/replies/messages/say_offer.yml
|
|
236
|
+
- spec/replies/messages/say_oi.yml.erb
|
|
232
237
|
- spec/service_reply_spec.rb
|
|
233
238
|
- spec/session_spec.rb
|
|
234
239
|
- spec/spec_helper.rb
|
|
@@ -242,7 +247,7 @@ files:
|
|
|
242
247
|
- spec/support/services_with_erb.yml
|
|
243
248
|
- spec/version_spec.rb
|
|
244
249
|
- stealth.gemspec
|
|
245
|
-
homepage: https://github.com/
|
|
250
|
+
homepage: https://github.com/hellostealth/stealth
|
|
246
251
|
licenses:
|
|
247
252
|
- MIT
|
|
248
253
|
metadata: {}
|
|
@@ -257,9 +262,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
257
262
|
version: '0'
|
|
258
263
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
259
264
|
requirements:
|
|
260
|
-
- - "
|
|
265
|
+
- - ">"
|
|
261
266
|
- !ruby/object:Gem::Version
|
|
262
|
-
version:
|
|
267
|
+
version: 1.3.1
|
|
263
268
|
requirements: []
|
|
264
269
|
rubyforge_project:
|
|
265
270
|
rubygems_version: 2.7.6
|
|
@@ -269,11 +274,14 @@ summary: Ruby framework for conversational bots
|
|
|
269
274
|
test_files:
|
|
270
275
|
- spec/configuration_spec.rb
|
|
271
276
|
- spec/controller/callbacks_spec.rb
|
|
277
|
+
- spec/controller/controller_spec.rb
|
|
272
278
|
- spec/controller/helpers_spec.rb
|
|
273
|
-
- spec/controller/
|
|
279
|
+
- spec/controller/replies_spec.rb
|
|
274
280
|
- spec/flow/flow_spec.rb
|
|
275
281
|
- spec/flow/state_spec.rb
|
|
276
|
-
- spec/replies/
|
|
282
|
+
- spec/replies/hello.yml.erb
|
|
283
|
+
- spec/replies/messages/say_offer.yml
|
|
284
|
+
- spec/replies/messages/say_oi.yml.erb
|
|
277
285
|
- spec/service_reply_spec.rb
|
|
278
286
|
- spec/session_spec.rb
|
|
279
287
|
- spec/spec_helper.rb
|
|
File without changes
|