stealth 1.0.0.pre1 → 1.0.0.pre2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a447e930b543fe9125bb09baaaf12f160f5fa715e346fd6b304285f5a31f56f2
4
- data.tar.gz: f5ff084dcb475b87ff972458751b16cc05779bed6eebf67f3feb15ba97909d68
3
+ metadata.gz: 1c9ae069b8bf53745259b83be47c652d625fb4c6f0c897c72dda88aa30696032
4
+ data.tar.gz: bed60f67d25cf795dff6e469f249c5c527dd008f56df5820502492450d4b8e8d
5
5
  SHA512:
6
- metadata.gz: ad24b962addcbe10da90dc44a8b0220c7ced906c42a525f45d37f4ee9baad43600b968cae7fdabc2f5a5cad4279718203ec49680a396c6fa8b24429a6f8dc891
7
- data.tar.gz: 207b7e854c327ef050a013017da9e284d820f3cac5819af8558ea9b7a86c8f252bef1fb7fe9bd8741fb5119d56be8523ae19930f0403d33f3e7b99e11ba132ab
6
+ metadata.gz: 012b9b948b15255b2fe2d8ae068e33d41cce1a398429b32df46f5e2fd656ed73374a488e22cbbb997921cc0913c4b9df84ad707e339427777ee3652b94435f8a
7
+ data.tar.gz: ea5cf2ce7fbe4858d3fba6bc5aa3535150ab32c9955aaa95783fd3e51908655b949093223d2b2a715e47177c321a2e91c18246cf249534e4c817a7714e690343
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- stealth (0.10.6)
4
+ stealth (1.0.0.pre1)
5
5
  activesupport (~> 5.2.0)
6
6
  multi_json (~> 1.12)
7
7
  puma (~> 3.10)
@@ -20,14 +20,14 @@ GEM
20
20
  concurrent-ruby (1.0.5)
21
21
  connection_pool (2.2.1)
22
22
  diff-lcs (1.3)
23
- i18n (1.0.0)
23
+ i18n (1.0.1)
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
29
  oj (3.5.0)
30
- puma (3.11.3)
30
+ puma (3.11.4)
31
31
  rack (2.0.4)
32
32
  rack-protection (2.0.1)
33
33
  rack
data/README.md CHANGED
@@ -21,6 +21,9 @@ Currently, there are gems for:
21
21
  * [Facebook Messenger](https://github.com/hellostealth/stealth-facebook)
22
22
  * [Twilio SMS](https://github.com/hellostealth/stealth-twilio)
23
23
 
24
+ ### Natural Language Processing
25
+ * [AWS Comprehend](https://github.com/hellostealth/stealth-aws-comprehend)
26
+
24
27
  ### Analytics
25
28
  * [Mixpanel](https://github.com/hellostealth/stealth-mixpanel)
26
29
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.0.pre1
1
+ 1.0.0.pre2
data/lib/stealth/base.rb CHANGED
@@ -22,25 +22,7 @@ module Stealth
22
22
  @root ||= File.expand_path(Pathname.new(Dir.pwd))
23
23
  end
24
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
25
  def self.boot
40
- if Stealth.env == 'development'
41
- ActiveSupport::Dependencies.mechanism = :load
42
- ActiveSupport::Dependencies.autoload_paths = Dir["bot/**/*.rb"]
43
- end
44
26
  load_environment
45
27
  end
46
28
 
@@ -76,14 +58,15 @@ module Stealth
76
58
  require File.join(Stealth.root, 'config', 'boot')
77
59
  require_directory("config/initializers")
78
60
  # Require explicitly to ensure it loads first
79
- require_dependency File.join(Stealth.root, 'bot', 'controllers', 'bot_controller')
61
+ require File.join(Stealth.root, 'bot', 'controllers', 'bot_controller')
62
+ require File.join(Stealth.root, 'config', 'flow_map')
80
63
  require_directory("bot")
81
64
  end
82
65
 
83
66
  private
84
67
 
85
68
  def self.require_directory(directory)
86
- for_each_file_in(directory) { |file| require_dependency(file) }
69
+ for_each_file_in(directory) { |file| require_relative(file) }
87
70
  end
88
71
 
89
72
  def self.for_each_file_in(directory, &blk)
data/lib/stealth/cli.rb CHANGED
@@ -30,6 +30,7 @@ module Stealth
30
30
  def generate(generator, name)
31
31
  Stealth::Generators::Generate.start([generator, name])
32
32
  end
33
+ map 'g' => 'generate'
33
34
 
34
35
  desc 'version', 'Prints stealth version'
35
36
  long_desc <<-EOS
@@ -51,24 +52,16 @@ module Stealth
51
52
  $ > stealth server -p 4500
52
53
  EOS
53
54
  method_option :port, aliases: '-p', desc: 'The port to run the server on'
54
- method_option :server, desc: 'Choose a specific Rack::Handler (webrick, thin, etc)'
55
- method_option :rackup, desc: 'A rackup configuration file path to load (config.ru)'
56
- method_option :host, desc: 'The host address to bind to'
57
- method_option :debug, desc: 'Turn on debug output'
58
- method_option :warn, desc: 'Turn on warnings'
59
- method_option :daemonize, desc: 'If true, the server will daemonize itself (fork, detach, etc)'
60
- method_option :pid, desc: 'Path to write a pid file after daemonize'
61
- method_option :environment, desc: 'Path to environment configuration (config/environment.rb)'
62
- method_option :code_reloading, desc: 'Code reloading', type: :boolean, default: true
63
55
  method_option :help, desc: 'Displays the usage message'
64
56
  def server
65
57
  if options[:help]
66
58
  invoke :help, ['server']
67
59
  else
68
60
  require 'stealth/commands/server'
69
- Stealth::Commands::Server.new(options).start
61
+ Stealth::Commands::Server.new(port: options.fetch(:port) { 5000 }).start
70
62
  end
71
63
  end
64
+ map 's' => 'server'
72
65
 
73
66
 
74
67
  desc 'console', 'Starts a stealth console'
@@ -87,6 +80,7 @@ module Stealth
87
80
  Stealth::Commands::Console.new(options).start
88
81
  end
89
82
  end
83
+ map 'c' => 'console'
90
84
 
91
85
 
92
86
  desc 'setup', 'Runs setup tasks for a specified service'
@@ -101,16 +95,16 @@ module Stealth
101
95
  service_setup_klass.trigger
102
96
  end
103
97
 
104
-
105
- desc 'clear_sessions', 'Clears all sessions in development'
98
+ desc 'sessions:clear', 'Clears all sessions in development'
106
99
  long_desc <<-EOS
107
- `stealth clear_sessions` clears all sessions from Redis in development.
100
+ `stealth sessions:clear` clears all sessions from Redis in development.
108
101
 
109
- $ > stealth clear_sessions
102
+ $ > stealth sessions:clear
110
103
  EOS
111
- def clear_sessions
104
+ define_method 'sessions:clear' do
112
105
  Stealth.load_environment
113
- $redis.flushdb if ENV['STEALTH_ENV'] == 'development'
106
+ $redis.flushdb if Stealth.env == 'development'
114
107
  end
108
+
115
109
  end
116
110
  end
@@ -7,14 +7,43 @@ require 'stealth/commands/command'
7
7
  module Stealth
8
8
  module Commands
9
9
  class Server < Command
10
- def initialize(options)
11
- super(options)
12
- Stealth.load_environment
10
+ def initialize(port:)
11
+ @port = port
12
+ $stdout.sync = true
13
13
  end
14
14
 
15
15
  def start
16
- Rack::Handler::Puma.run(Stealth::Server)
16
+ # Rack::Handler::Puma.run(Stealth::Server)
17
+ puts ascii_art
18
+ exec "foreman start -f Procfile.dev -p #{@port}"
17
19
  end
20
+
21
+ private
22
+
23
+ def ascii_art
24
+ <<~ART
25
+ --
26
+ -yooy-
27
+ -yo` `oy-
28
+ -yo` `oy-
29
+ -hh` `hh-
30
+ -yo`/y: :y/`oy-
31
+ -yo` /y: :y/ `oy-
32
+ -yo` /y::y/ `oy-
33
+ -yd+ /dd/ +dy-
34
+ -yo` :y/ :y/ /y: /y/ `oy-
35
+ -yo` :y/ :y/ /y: /y/ `oy-
36
+ -yo` :yoy/ /yoy: `oy-
37
+ -yo` :yoy/ /yoy: `oy-
38
+ -yo` :y/ :y/ /y: /y: `oy-
39
+ -yo` :y/ :y/ /y: /y: `oy-
40
+ -yh/ :yy: /hy-
41
+
42
+
43
+ Stealth v#{Stealth::VERSION}
44
+
45
+ ART
46
+ end
18
47
  end
19
48
  end
20
49
  end
@@ -80,39 +80,36 @@ module Stealth
80
80
  Stealth::Logger.l(topic: "session", message: "User #{current_user_id}: scheduled session step to #{flow}->#{state} in #{delay} seconds")
81
81
  end
82
82
 
83
- def step_to(session: nil, flow: nil, state: nil)
83
+ def step_to_at(timestamp, session: nil, flow: nil, state: nil)
84
84
  flow, state = get_flow_and_state(session: session, flow: flow, state: state)
85
- step(flow: flow, state: state)
86
- end
87
85
 
88
- def update_session_to(session: nil, flow: nil, state: nil)
89
- flow, state = get_flow_and_state(session: session, flow: flow, state: state)
90
- update_session(flow: flow, state: state)
86
+ unless timestamp.is_a?(DateTime)
87
+ raise ArgumentError, "Please specify your step_to_at `timestamp` parameter as a DateTime"
88
+ end
89
+
90
+ Stealth::ScheduledReplyJob.perform_at(timestamp, current_service, current_user_id, flow, state)
91
+ Stealth::Logger.l(topic: "session", message: "User #{current_user_id}: scheduled session step to #{flow}->#{state} at #{timestamp.iso8601}")
91
92
  end
92
93
 
93
- def step_to_next
94
- flow, state = get_next_state
94
+ def step_to(session: nil, flow: nil, state: nil)
95
+ flow, state = get_flow_and_state(session: session, flow: flow, state: state)
95
96
  step(flow: flow, state: state)
96
97
  end
97
98
 
98
- def update_session_to_next
99
- flow, state = get_next_state
99
+ def update_session_to(session: nil, flow: nil, state: nil)
100
+ flow, state = get_flow_and_state(session: session, flow: flow, state: state)
100
101
  update_session(flow: flow, state: state)
101
102
  end
102
103
 
103
104
  private
104
105
 
105
106
  def update_session(flow:, state:)
106
- Stealth::Logger.l(topic: "session", message: "User #{current_user_id}: updating session to #{flow}->#{state}")
107
-
108
107
  @current_session = Stealth::Session.new(user_id: current_user_id)
109
108
  @progressed = :updated_session
110
109
  @current_session.set(flow: flow, state: state)
111
110
  end
112
111
 
113
112
  def step(flow:, state:)
114
- Stealth::Logger.l(topic: "session", message: "User #{current_user_id}: stepping to #{flow}->#{state}")
115
-
116
113
  update_session(flow: flow, state: state)
117
114
  @progressed = :stepped
118
115
  @flow_controller = nil
@@ -132,30 +129,16 @@ module Stealth
132
129
 
133
130
  if flow.present?
134
131
  if state.blank?
135
- flow_klass = [flow, 'flow'].join('_').classify.constantize
136
- state = flow_klass.flow_spec.states.keys.first
132
+ state = FlowMap.flow_spec[flow.to_sym].states.keys.first.to_s
137
133
  end
138
134
 
139
- return flow, state
135
+ return flow.to_s, state.to_s
140
136
  end
141
137
 
142
138
  if state.present?
143
- return current_session.flow_string, state
139
+ return current_session.flow_string, state.to_s
144
140
  end
145
141
  end
146
142
 
147
- def get_next_state
148
- current_state_index = current_session.flow.states.index(current_session.state_string.to_sym)
149
- next_state = current_session.flow.states[current_state_index + 1]
150
- if next_state.nil?
151
- raise(
152
- Stealth::Errors::InvalidStateTransitions,
153
- "The next state after #{current_session.state_string} has not yet been defined"
154
- )
155
- end
156
-
157
- return current_session.flow_string, next_state
158
- end
159
-
160
143
  end
161
144
  end
@@ -27,7 +27,7 @@ module Stealth
27
27
 
28
28
  included do
29
29
  class_attribute :_helpers, default: Module.new
30
- class_attribute :helpers_path, default: []
30
+ class_attribute :helpers_path, default: ["bot/helpers"]
31
31
  class_attribute :include_all_helpers, default: true
32
32
  end
33
33
 
@@ -54,6 +54,9 @@ module Stealth
54
54
  # Allow all helpers to be included
55
55
  args += all_bot_helpers if args.delete(:all)
56
56
 
57
+ # Add each helper_path to the LOAD_PATH
58
+ Array(helpers_path).each {|path| $:.unshift(path) }
59
+
57
60
  args.flatten.map! do |arg|
58
61
  case arg
59
62
  when String, Symbol
@@ -11,41 +11,42 @@ module Stealth
11
11
  extend ActiveSupport::Concern
12
12
 
13
13
  class_methods do
14
- attr_reader :flow_spec
15
-
16
- def flow(&specification)
17
- @flow_spec = Specification.new(&specification)
14
+ def flow(flow_name, &specification)
15
+ flow_spec[flow_name.to_sym] = Specification.new(&specification)
18
16
  end
19
17
  end
20
18
 
21
19
  included do
22
- attr_accessor :flow_state, :user_id
20
+ class_attribute :flow_spec, default: {}
21
+
22
+ attr_accessor :flow, :flow_state, :user_id
23
23
 
24
24
  def current_state
25
- res = spec.states[@flow_state.to_sym] if @flow_state
26
- res || spec.initial_state
25
+ res = self.spec.states[@flow_state.to_sym] if @flow_state
26
+ res || self.spec.initial_state
27
27
  end
28
28
 
29
- def spec
30
- # check the singleton class first
31
- class << self
32
- return flow_spec if flow_spec
33
- end
29
+ def current_flow
30
+ @flow || self.class.flow_spec.keys.first
31
+ end
34
32
 
35
- self.class.flow_spec
33
+ def spec
34
+ self.class.flow_spec[current_flow]
36
35
  end
37
36
 
38
37
  def states
39
38
  self.spec.states.keys
40
39
  end
41
40
 
42
- def init_state(state)
43
- raise(ArgumentError, 'No state was specified.') if state.blank?
44
-
41
+ def init(flow:, state:)
42
+ new_flow = flow.to_sym
45
43
  new_state = state.to_sym
46
- unless states.include?(new_state)
47
- raise(Stealth::Errors::InvalidStateTransition, "Unknown state '#{state}' for #{self.class.to_s}")
44
+
45
+ unless state_exists?(potential_flow: new_flow, potential_state: new_state)
46
+ raise(Stealth::Errors::InvalidStateTransition, "Unknown state '#{new_state}' for '#{new_flow}' flow")
48
47
  end
48
+
49
+ @flow = new_flow
49
50
  @flow_state = new_state
50
51
 
51
52
  self
@@ -54,7 +55,15 @@ module Stealth
54
55
  private
55
56
 
56
57
  def flow_and_state
57
- [self.class.to_s, current_state].join("->")
58
+ [current_flow, current_state].join("->")
59
+ end
60
+
61
+ def state_exists?(potential_flow:, potential_state:)
62
+ if self.class.flow_spec[potential_flow].present?
63
+ self.class.flow_spec[potential_flow].states.include?(potential_state)
64
+ else
65
+ raise(Stealth::Errors::InvalidStateTransition, "Unknown flow '#{potential_flow}'")
66
+ end
58
67
  end
59
68
  end
60
69
 
@@ -9,3 +9,7 @@ gem 'stealth', '~> 0.10.0'
9
9
 
10
10
  # Uncomment to enable the Stealth Twilio SMS Driver
11
11
  # gem 'stealth-twilio'
12
+
13
+ group :development do
14
+ gem 'foreman'
15
+ end
@@ -1,10 +1,12 @@
1
1
  class BotController < Stealth::Controller
2
2
 
3
+ helper :all
4
+
3
5
  def route
4
6
  if current_session.present?
5
7
  step_to session: current_session
6
8
  else
7
- step_to flow: 'Hello', state: 'say_hello'
9
+ step_to flow: 'hello', state: 'say_hello'
8
10
  end
9
11
  end
10
12
 
@@ -1,4 +1,4 @@
1
- class <%= name.capitalize.underscore.classify %>Flow
1
+ class FlowMap
2
2
 
3
3
  include Stealth::Flow
4
4
 
@@ -10,7 +10,7 @@ class <%= name.capitalize.underscore.classify %>Flow
10
10
  state :say_goodbye
11
11
  end
12
12
 
13
- flow :catchall do
13
+ flow :catch_all do
14
14
  state :level1
15
15
  end
16
16
 
@@ -2,8 +2,8 @@
2
2
 
3
3
  To boot this bot locally, we recommend the following:
4
4
 
5
- 1. `gem install foreman`
6
- 2. Start Redis
7
- 3. `foreman start -f Procfile.dev`
5
+ 1. `bundle`
6
+ 2. Start your local Redis server
7
+ 3. `stealth s`
8
8
 
9
- Using `foreman` will start the web server and Sidekiq processes together.
9
+ For more information, please check out the [Stealth documentation](https://hellostealth.org/docs).
@@ -1,3 +1,6 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
1
4
  require 'thor/group'
2
5
 
3
6
  module Stealth
@@ -1,3 +1,6 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
1
4
  require 'thor/group'
2
5
 
3
6
  module Stealth
@@ -4,9 +4,45 @@
4
4
  module Stealth
5
5
  class Logger
6
6
 
7
+ COLORS = ::Hash[
8
+ black: 30,
9
+ red: 31,
10
+ green: 32,
11
+ yellow: 33,
12
+ blue: 34,
13
+ magenta: 35,
14
+ cyan: 36,
15
+ gray: 37,
16
+ ].freeze
17
+
18
+ def self.color_code(code)
19
+ COLORS.fetch(code) { raise(ArgumentError, "Color #{code} not supported.") }
20
+ end
21
+
22
+ def self.colorize(input, color:)
23
+ "\e[#{color_code(color)}m#{input}\e[0m"
24
+ end
25
+
7
26
  def self.log(topic:, message:)
8
27
  unless ENV['STEALTH_ENV'] == 'test'
9
- puts "[#{topic.upcase}] #{message}"
28
+ puts "#{print_topic(topic)} #{message}"
29
+ end
30
+ end
31
+
32
+ def self.print_topic(topic)
33
+ topic_string = "[#{topic}]"
34
+
35
+ case topic.to_sym
36
+ when :session
37
+ colorize(topic_string, color: :green)
38
+ when :previous_session
39
+ colorize(topic_string, color: :yellow)
40
+ when :facebook, :twilio
41
+ colorize(topic_string, color: :blue)
42
+ when :catch_all
43
+ colorize(topic_string, color: :red)
44
+ else
45
+ colorize(topic_string, color: :gray)
10
46
  end
11
47
  end
12
48
 
@@ -30,11 +30,6 @@ module Stealth
30
30
  end
31
31
 
32
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
-
38
33
  Stealth::Logger.l(topic: "incoming", message: "Received webhook from #{params[:service]}")
39
34
 
40
35
  # JSON params need to be parsed and added to the params
@@ -31,11 +31,7 @@ module Stealth
31
31
  def flow
32
32
  return nil if flow_string.blank?
33
33
 
34
- @flow ||= begin
35
- flow_klass = [flow_string, 'flow'].join('_').classify.constantize
36
- flow = flow_klass.new.init_state(state_string)
37
- flow
38
- end
34
+ @flow ||= FlowMap.new.init(flow: flow_string, state: state_string)
39
35
  end
40
36
 
41
37
  def state
@@ -63,6 +59,8 @@ module Stealth
63
59
 
64
60
  @flow = nil
65
61
  @session = canonical_session_slug(flow: flow, state: state)
62
+
63
+ Stealth::Logger.l(topic: "session", message: "User #{user_id}: setting session to #{flow}->#{state}")
66
64
  $redis.set(user_id, session)
67
65
  end
68
66
 
@@ -143,20 +143,16 @@ class OtherFlowTestersController < BotController
143
143
  end
144
144
  end
145
145
 
146
- class FlowTesterFlow
146
+ class FlowMap
147
147
  include Stealth::Flow
148
148
 
149
- flow do
149
+ flow :flow_tester do
150
150
  state :my_action
151
151
  state :my_action2
152
152
  state :my_action3
153
153
  end
154
- end
155
-
156
- class OtherFlowTesterFlow
157
- include Stealth::Flow
158
154
 
159
- flow do
155
+ flow :other_flow_tester do
160
156
  state :other_action
161
157
  state :other_action2
162
158
  state :other_action3