xip 0.0.1 → 2.0.0.beta2
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/.circleci/config.yml +116 -0
- data/.gitignore +12 -0
- data/CHANGELOG.md +135 -0
- data/Gemfile +4 -1
- data/Gemfile.lock +65 -15
- data/LICENSE +6 -4
- data/README.md +51 -1
- data/VERSION +1 -0
- data/bin/xip +3 -11
- data/lib/xip.rb +1 -3
- data/lib/xip/base.rb +189 -0
- data/lib/xip/cli.rb +273 -0
- data/lib/xip/cli_base.rb +24 -0
- data/lib/xip/commands/command.rb +13 -0
- data/lib/xip/commands/console.rb +74 -0
- data/lib/xip/commands/server.rb +63 -0
- data/lib/xip/configuration.rb +56 -0
- data/lib/xip/controller/callbacks.rb +63 -0
- data/lib/xip/controller/catch_all.rb +84 -0
- data/lib/xip/controller/controller.rb +274 -0
- data/lib/xip/controller/dev_jumps.rb +40 -0
- data/lib/xip/controller/dynamic_delay.rb +61 -0
- data/lib/xip/controller/helpers.rb +128 -0
- data/lib/xip/controller/interrupt_detect.rb +99 -0
- data/lib/xip/controller/messages.rb +283 -0
- data/lib/xip/controller/nlp.rb +49 -0
- data/lib/xip/controller/replies.rb +281 -0
- data/lib/xip/controller/unrecognized_message.rb +61 -0
- data/lib/xip/core_ext.rb +5 -0
- data/lib/xip/core_ext/numeric.rb +10 -0
- data/lib/xip/core_ext/string.rb +18 -0
- data/lib/xip/dispatcher.rb +68 -0
- data/lib/xip/errors.rb +55 -0
- data/lib/xip/flow/base.rb +69 -0
- data/lib/xip/flow/specification.rb +56 -0
- data/lib/xip/flow/state.rb +82 -0
- data/lib/xip/generators/builder.rb +41 -0
- data/lib/xip/generators/builder/.gitignore +30 -0
- data/lib/xip/generators/builder/Gemfile +19 -0
- data/lib/xip/generators/builder/Procfile.dev +2 -0
- data/lib/xip/generators/builder/README.md +9 -0
- data/lib/xip/generators/builder/Rakefile +2 -0
- data/lib/xip/generators/builder/bot/controllers/bot_controller.rb +55 -0
- data/lib/xip/generators/builder/bot/controllers/catch_alls_controller.rb +21 -0
- data/lib/xip/generators/builder/bot/controllers/concerns/.keep +0 -0
- data/lib/xip/generators/builder/bot/controllers/goodbyes_controller.rb +9 -0
- data/lib/xip/generators/builder/bot/controllers/hellos_controller.rb +9 -0
- data/lib/xip/generators/builder/bot/controllers/interrupts_controller.rb +9 -0
- data/lib/xip/generators/builder/bot/controllers/unrecognized_messages_controller.rb +9 -0
- data/lib/xip/generators/builder/bot/helpers/bot_helper.rb +2 -0
- data/lib/xip/generators/builder/bot/models/bot_record.rb +3 -0
- data/lib/xip/generators/builder/bot/models/concerns/.keep +0 -0
- data/lib/xip/generators/builder/bot/replies/catch_alls/level1.yml +2 -0
- data/lib/xip/generators/builder/bot/replies/goodbyes/say_goodbye.yml +2 -0
- data/lib/xip/generators/builder/bot/replies/hellos/say_hello.yml +2 -0
- data/lib/xip/generators/builder/config.ru +4 -0
- data/lib/xip/generators/builder/config/boot.rb +6 -0
- data/lib/xip/generators/builder/config/database.yml +25 -0
- data/lib/xip/generators/builder/config/environment.rb +2 -0
- data/lib/xip/generators/builder/config/flow_map.rb +25 -0
- data/lib/xip/generators/builder/config/initializers/autoload.rb +8 -0
- data/lib/xip/generators/builder/config/initializers/inflections.rb +16 -0
- data/lib/xip/generators/builder/config/puma.rb +25 -0
- data/lib/xip/generators/builder/config/services.yml +35 -0
- data/lib/xip/generators/builder/config/sidekiq.yml +3 -0
- data/lib/xip/generators/builder/db/seeds.rb +7 -0
- data/lib/xip/generators/generate.rb +39 -0
- data/lib/xip/generators/generate/flow/controllers/controller.tt +7 -0
- data/lib/xip/generators/generate/flow/helpers/helper.tt +3 -0
- data/lib/xip/generators/generate/flow/replies/ask_example.tt +9 -0
- data/lib/xip/helpers/redis.rb +40 -0
- data/lib/xip/jobs.rb +9 -0
- data/lib/xip/lock.rb +82 -0
- data/lib/xip/logger.rb +9 -3
- data/lib/xip/migrations/configurator.rb +73 -0
- data/lib/xip/migrations/generators.rb +16 -0
- data/lib/xip/migrations/railtie_config.rb +14 -0
- data/lib/xip/migrations/tasks.rb +43 -0
- data/lib/xip/nlp/client.rb +21 -0
- data/lib/xip/nlp/result.rb +56 -0
- data/lib/xip/reloader.rb +89 -0
- data/lib/xip/reply.rb +36 -0
- data/lib/xip/scheduled_reply.rb +18 -0
- data/lib/xip/server.rb +63 -0
- data/lib/xip/service_message.rb +17 -0
- data/lib/xip/service_reply.rb +44 -0
- data/lib/xip/services/base_client.rb +24 -0
- data/lib/xip/services/base_message_handler.rb +27 -0
- data/lib/xip/services/base_reply_handler.rb +72 -0
- data/lib/xip/services/jobs/handle_message_job.rb +21 -0
- data/lib/xip/session.rb +203 -0
- data/lib/xip/version.rb +7 -1
- data/logo.svg +17 -0
- data/spec/configuration_spec.rb +93 -0
- data/spec/controller/callbacks_spec.rb +217 -0
- data/spec/controller/catch_all_spec.rb +154 -0
- data/spec/controller/controller_spec.rb +889 -0
- data/spec/controller/dynamic_delay_spec.rb +70 -0
- data/spec/controller/helpers_spec.rb +119 -0
- data/spec/controller/interrupt_detect_spec.rb +171 -0
- data/spec/controller/messages_spec.rb +744 -0
- data/spec/controller/nlp_spec.rb +93 -0
- data/spec/controller/replies_spec.rb +694 -0
- data/spec/controller/unrecognized_message_spec.rb +168 -0
- data/spec/dispatcher_spec.rb +79 -0
- data/spec/flow/flow_spec.rb +82 -0
- data/spec/flow/state_spec.rb +109 -0
- data/spec/helpers/redis_spec.rb +77 -0
- data/spec/lock_spec.rb +100 -0
- data/spec/nlp/client_spec.rb +23 -0
- data/spec/nlp/result_spec.rb +57 -0
- data/spec/replies/hello.yml.erb +15 -0
- 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_msgs_without_breaks.yml +4 -0
- data/spec/replies/messages/say_offer.yml +6 -0
- data/spec/replies/messages/say_offer_with_dynamic.yml +6 -0
- data/spec/replies/messages/say_oi.yml.erb +15 -0
- data/spec/replies/messages/say_randomize_speech.yml +10 -0
- data/spec/replies/messages/say_randomize_text.yml +10 -0
- data/spec/replies/messages/say_yo.yml +6 -0
- data/spec/replies/messages/say_yo.yml+twitter +6 -0
- data/spec/replies/messages/sub1/sub2/say_nested.yml +10 -0
- data/spec/reply_spec.rb +61 -0
- data/spec/scheduled_reply_spec.rb +23 -0
- data/spec/service_reply_spec.rb +92 -0
- data/spec/session_spec.rb +366 -0
- data/spec/spec_helper.rb +22 -66
- data/spec/support/alternate_helpers/foo_helper.rb +5 -0
- data/spec/support/controllers/vaders_controller.rb +24 -0
- data/spec/support/helpers/fun/games_helper.rb +7 -0
- data/spec/support/helpers/fun/pdf_helper.rb +7 -0
- data/spec/support/helpers/standalone_helper.rb +5 -0
- data/spec/support/helpers_typo/users_helper.rb +2 -0
- data/spec/support/nlp_clients/dialogflow.rb +9 -0
- data/spec/support/nlp_clients/luis.rb +9 -0
- data/spec/support/nlp_results/luis_result.rb +163 -0
- data/spec/support/sample_messages.rb +66 -0
- data/spec/support/services.yml +31 -0
- data/spec/support/services_with_erb.yml +31 -0
- data/spec/version_spec.rb +16 -0
- data/xip.gemspec +25 -14
- metadata +320 -18
data/lib/xip/errors.rb
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Xip
|
|
4
|
+
class Errors < StandardError
|
|
5
|
+
|
|
6
|
+
class ConfigurationError < Errors
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
class ReplyFormatNotSupported < Errors
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class ServiceImpaired < Errors
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class ServiceError < Errors
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class ServiceNotRecognized < Errors
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class ControllerRoutingNotImplemented < Errors
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class UndefinedVariable < Errors
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
class RedisNotConfigured < Errors
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class InvalidStateTransition < Errors
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class ReplyNotFound < Errors
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class UnrecognizedMessage < Errors
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
class FlowError < Errors
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
class FlowDefinitionError < Errors
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
class InvalidSessionID < Errors
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
class UserOptOut < Errors
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
class ReservedHomophoneUsed < Errors
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'xip/flow/specification'
|
|
4
|
+
require 'xip/flow/state'
|
|
5
|
+
|
|
6
|
+
module Xip
|
|
7
|
+
module Flow
|
|
8
|
+
|
|
9
|
+
extend ActiveSupport::Concern
|
|
10
|
+
|
|
11
|
+
class_methods do
|
|
12
|
+
def flow(flow_name, &specification)
|
|
13
|
+
flow_spec[flow_name.to_sym] = Specification.new(flow_name, &specification)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
included do
|
|
18
|
+
class_attribute :flow_spec, default: {}
|
|
19
|
+
|
|
20
|
+
attr_accessor :flow, :flow_state, :user_id
|
|
21
|
+
|
|
22
|
+
def current_state
|
|
23
|
+
res = self.spec.states[@flow_state.to_sym] if @flow_state
|
|
24
|
+
res || self.spec.initial_state
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def current_flow
|
|
28
|
+
@flow || self.class.flow_spec.keys.first
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def spec
|
|
32
|
+
self.class.flow_spec[current_flow]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def states
|
|
36
|
+
self.spec.states.keys
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def init(flow:, state:)
|
|
40
|
+
new_flow = flow.to_sym
|
|
41
|
+
new_state = state.to_sym
|
|
42
|
+
|
|
43
|
+
unless state_exists?(potential_flow: new_flow, potential_state: new_state)
|
|
44
|
+
raise(Xip::Errors::InvalidStateTransition, "Unknown state '#{new_state}' for '#{new_flow}' flow")
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
@flow = new_flow
|
|
48
|
+
@flow_state = new_state
|
|
49
|
+
|
|
50
|
+
self
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def flow_and_state
|
|
56
|
+
[current_flow, current_state].join(Xip::Session::SLUG_SEPARATOR)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def state_exists?(potential_flow:, potential_state:)
|
|
60
|
+
if self.class.flow_spec[potential_flow].present?
|
|
61
|
+
self.class.flow_spec[potential_flow].states.include?(potential_state)
|
|
62
|
+
else
|
|
63
|
+
raise(Xip::Errors::InvalidStateTransition, "Unknown flow '#{potential_flow}'")
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Xip
|
|
4
|
+
module Flow
|
|
5
|
+
class Specification
|
|
6
|
+
attr_reader :flow_name
|
|
7
|
+
attr_accessor :states, :initial_state
|
|
8
|
+
|
|
9
|
+
def initialize(flow_name, &specification)
|
|
10
|
+
@states = Hash.new
|
|
11
|
+
@flow_name = flow_name
|
|
12
|
+
instance_eval(&specification)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def state_names
|
|
16
|
+
states.keys
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def state(name, fails_to: nil, redirects_to: nil, **opts)
|
|
22
|
+
fail_state = get_fail_or_redirect_state(fails_to)
|
|
23
|
+
redirect_state = get_fail_or_redirect_state(redirects_to)
|
|
24
|
+
|
|
25
|
+
new_state = Xip::Flow::State.new(
|
|
26
|
+
name: name,
|
|
27
|
+
spec: self,
|
|
28
|
+
fails_to: fail_state,
|
|
29
|
+
redirects_to: redirect_state,
|
|
30
|
+
opts: opts
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
@initial_state = new_state if @states.empty?
|
|
34
|
+
@states[name.to_sym] = new_state
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def get_fail_or_redirect_state(specified_state)
|
|
38
|
+
if specified_state.present?
|
|
39
|
+
session = Xip::Session.new
|
|
40
|
+
|
|
41
|
+
if Xip::Session.is_a_session_string?(specified_state)
|
|
42
|
+
session.session = specified_state
|
|
43
|
+
else
|
|
44
|
+
session.session = Xip::Session.canonical_session_slug(
|
|
45
|
+
flow: flow_name,
|
|
46
|
+
state: specified_state
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
return session
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Xip
|
|
4
|
+
module Flow
|
|
5
|
+
class State
|
|
6
|
+
|
|
7
|
+
include Comparable
|
|
8
|
+
|
|
9
|
+
attr_accessor :name
|
|
10
|
+
attr_reader :spec, :fails_to, :redirects_to, :opts
|
|
11
|
+
|
|
12
|
+
def initialize(name:, spec:, fails_to: nil, redirects_to: nil, opts:)
|
|
13
|
+
if fails_to.present? && !fails_to.is_a?(Xip::Session)
|
|
14
|
+
raise(ArgumentError, 'fails_to state should be a Xip::Session')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
if redirects_to.present? && !redirects_to.is_a?(Xip::Session)
|
|
18
|
+
raise(ArgumentError, 'redirects_to state should be a Xip::Session')
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
@name, @spec = name, spec
|
|
22
|
+
@fails_to, @redirects_to, @opts = fails_to, redirects_to, opts
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def <=>(other_state)
|
|
26
|
+
state_position(self) <=> state_position(other_state)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def +(steps)
|
|
30
|
+
if steps < 0
|
|
31
|
+
new_position = state_position(self) + steps
|
|
32
|
+
|
|
33
|
+
# we don't want to allow the array index to wrap here so we return
|
|
34
|
+
# the first state instead
|
|
35
|
+
if new_position < 0
|
|
36
|
+
new_state = spec.states.keys.first
|
|
37
|
+
else
|
|
38
|
+
new_state = spec.states.keys.at(new_position)
|
|
39
|
+
end
|
|
40
|
+
else
|
|
41
|
+
new_state = spec.states.keys[state_position(self) + steps]
|
|
42
|
+
|
|
43
|
+
# we may have been told to access an out-of-bounds state
|
|
44
|
+
# return the last state
|
|
45
|
+
if new_state.blank?
|
|
46
|
+
new_state = spec.states.keys.last
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
new_state
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def -(steps)
|
|
54
|
+
if steps < 0
|
|
55
|
+
return self + steps.abs
|
|
56
|
+
else
|
|
57
|
+
return self + (-steps)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def to_s
|
|
62
|
+
"#{name}"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def to_sym
|
|
66
|
+
name.to_sym
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def state_position(state)
|
|
72
|
+
states = spec.states.keys
|
|
73
|
+
|
|
74
|
+
unless states.include?(state.to_sym)
|
|
75
|
+
raise(ArgumentError, "state `#{state}' does not exist")
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
states.index(state.to_sym)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'thor/group'
|
|
5
|
+
|
|
6
|
+
module Xip
|
|
7
|
+
module Generators
|
|
8
|
+
class Builder < Thor::Group
|
|
9
|
+
include Thor::Actions
|
|
10
|
+
|
|
11
|
+
argument :name
|
|
12
|
+
|
|
13
|
+
def self.source_root
|
|
14
|
+
File.dirname(__FILE__) + "/builder"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def create_bot_directory
|
|
18
|
+
empty_directory(name)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def create_bot_structure
|
|
22
|
+
directory('bot', "#{name}/bot")
|
|
23
|
+
directory('config', "#{name}/config")
|
|
24
|
+
directory('db', "#{name}/db")
|
|
25
|
+
|
|
26
|
+
# Miscellaneous Files
|
|
27
|
+
copy_file "config.ru", "#{name}/config.ru"
|
|
28
|
+
copy_file "Rakefile", "#{name}/Rakefile"
|
|
29
|
+
copy_file "Gemfile", "#{name}/Gemfile"
|
|
30
|
+
copy_file "README.md", "#{name}/README.md"
|
|
31
|
+
copy_file "Procfile.dev", "#{name}/Procfile.dev"
|
|
32
|
+
copy_file ".gitignore", "#{name}/.gitignore"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def change_directory_bundle
|
|
36
|
+
puts run("cd #{name} && bundle install")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
|
|
2
|
+
#
|
|
3
|
+
# If you find yourself ignoring temporary files generated by your text editor
|
|
4
|
+
# or operating system, you probably want to add a global ignore instead:
|
|
5
|
+
# git config --global core.excludesfile '~/.gitignore_global'
|
|
6
|
+
|
|
7
|
+
# Ignore bundler config.
|
|
8
|
+
/.bundle
|
|
9
|
+
|
|
10
|
+
# Ignore the default SQLite database.
|
|
11
|
+
/db/*.sqlite3
|
|
12
|
+
/db/*.sqlite3-journal
|
|
13
|
+
|
|
14
|
+
# Ignore all logfiles and tempfiles.
|
|
15
|
+
/log/*
|
|
16
|
+
/tmp/*
|
|
17
|
+
!/log/.keep
|
|
18
|
+
!/tmp/.keep
|
|
19
|
+
|
|
20
|
+
# Ignore uploaded files in development
|
|
21
|
+
/storage/*
|
|
22
|
+
|
|
23
|
+
/node_modules
|
|
24
|
+
/yarn-error.log
|
|
25
|
+
|
|
26
|
+
/public/assets
|
|
27
|
+
.byebug_history
|
|
28
|
+
|
|
29
|
+
# Ignore master key for decrypting credentials and more.
|
|
30
|
+
/config/master.key
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
source 'https://rubygems.org'
|
|
2
|
+
|
|
3
|
+
gem 'xip', '~> 2.0'
|
|
4
|
+
|
|
5
|
+
gem 'railties', '~> 6.1'
|
|
6
|
+
gem 'activerecord', '~> 6.1'
|
|
7
|
+
# Use sqlite3 as the database for Active Record
|
|
8
|
+
gem 'sqlite3'
|
|
9
|
+
|
|
10
|
+
# Uncomment to enable the Xip Facebook Driver
|
|
11
|
+
# gem 'xip-facebook'
|
|
12
|
+
|
|
13
|
+
# Uncomment to enable the Xip Twilio SMS Driver
|
|
14
|
+
# gem 'xip-twilio'
|
|
15
|
+
|
|
16
|
+
group :development do
|
|
17
|
+
gem 'foreman'
|
|
18
|
+
gem 'listen', '~> 3.3'
|
|
19
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# <a href='https://xipkit.com'><img src='https://cdn.xipkit.com/logo-dark.svg' width='400' alt='Xip Logo' aria-label='xipkit.com' /></a>
|
|
2
|
+
|
|
3
|
+
To boot this bot locally, we recommend the following:
|
|
4
|
+
|
|
5
|
+
1. `bundle`
|
|
6
|
+
2. Start your local Redis server
|
|
7
|
+
3. `xip s`
|
|
8
|
+
|
|
9
|
+
For more information, please check out the [Xip documentation](https://xipkit.com/docs).
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class BotController < Xip::Controller
|
|
4
|
+
|
|
5
|
+
helper :all
|
|
6
|
+
|
|
7
|
+
def route
|
|
8
|
+
if current_message.payload.present?
|
|
9
|
+
handle_payloads
|
|
10
|
+
# Clear out the payload to prevent duplicate handling
|
|
11
|
+
current_message.payload = nil
|
|
12
|
+
return
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Allow devs to jump around flows and states by typing:
|
|
16
|
+
# /flow_name/state_name or
|
|
17
|
+
# /flow_name (jumps to first state) or
|
|
18
|
+
# //state_name (jumps to state in current flow)
|
|
19
|
+
# (only works for bots in development)
|
|
20
|
+
return if dev_jump_detected?
|
|
21
|
+
|
|
22
|
+
if current_session.present?
|
|
23
|
+
step_to session: current_session
|
|
24
|
+
else
|
|
25
|
+
step_to flow: 'hello', state: 'say_hello'
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
# Handle payloads globally since payload buttons remain in the chat
|
|
32
|
+
# and we cannot guess in which states they will be tapped.
|
|
33
|
+
def handle_payloads
|
|
34
|
+
case current_message.payload
|
|
35
|
+
when 'developer_restart', 'new_user'
|
|
36
|
+
step_to flow: 'hello', state: 'say_hello'
|
|
37
|
+
when 'goodbye'
|
|
38
|
+
step_to flow: 'goodbye'
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Automatically called when clients receive an opt-out error from
|
|
43
|
+
# the platform. You can write your own steps for handling.
|
|
44
|
+
def handle_opt_out
|
|
45
|
+
do_nothing
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Automatically called when clients receive an invalid session_id error from
|
|
49
|
+
# the platform. For example, attempting to text a landline.
|
|
50
|
+
# You can write your own steps for handling.
|
|
51
|
+
def handle_invalid_session_id
|
|
52
|
+
do_nothing
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CatchAllsController < BotController
|
|
4
|
+
|
|
5
|
+
def level1
|
|
6
|
+
send_replies
|
|
7
|
+
|
|
8
|
+
if fail_session.present?
|
|
9
|
+
step_to session: fail_session
|
|
10
|
+
else
|
|
11
|
+
step_to session: previous_session - 2.states
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def fail_session
|
|
18
|
+
previous_session.flow.current_state.fails_to
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|