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.
Files changed (146) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +116 -0
  3. data/.gitignore +12 -0
  4. data/CHANGELOG.md +135 -0
  5. data/Gemfile +4 -1
  6. data/Gemfile.lock +65 -15
  7. data/LICENSE +6 -4
  8. data/README.md +51 -1
  9. data/VERSION +1 -0
  10. data/bin/xip +3 -11
  11. data/lib/xip.rb +1 -3
  12. data/lib/xip/base.rb +189 -0
  13. data/lib/xip/cli.rb +273 -0
  14. data/lib/xip/cli_base.rb +24 -0
  15. data/lib/xip/commands/command.rb +13 -0
  16. data/lib/xip/commands/console.rb +74 -0
  17. data/lib/xip/commands/server.rb +63 -0
  18. data/lib/xip/configuration.rb +56 -0
  19. data/lib/xip/controller/callbacks.rb +63 -0
  20. data/lib/xip/controller/catch_all.rb +84 -0
  21. data/lib/xip/controller/controller.rb +274 -0
  22. data/lib/xip/controller/dev_jumps.rb +40 -0
  23. data/lib/xip/controller/dynamic_delay.rb +61 -0
  24. data/lib/xip/controller/helpers.rb +128 -0
  25. data/lib/xip/controller/interrupt_detect.rb +99 -0
  26. data/lib/xip/controller/messages.rb +283 -0
  27. data/lib/xip/controller/nlp.rb +49 -0
  28. data/lib/xip/controller/replies.rb +281 -0
  29. data/lib/xip/controller/unrecognized_message.rb +61 -0
  30. data/lib/xip/core_ext.rb +5 -0
  31. data/lib/xip/core_ext/numeric.rb +10 -0
  32. data/lib/xip/core_ext/string.rb +18 -0
  33. data/lib/xip/dispatcher.rb +68 -0
  34. data/lib/xip/errors.rb +55 -0
  35. data/lib/xip/flow/base.rb +69 -0
  36. data/lib/xip/flow/specification.rb +56 -0
  37. data/lib/xip/flow/state.rb +82 -0
  38. data/lib/xip/generators/builder.rb +41 -0
  39. data/lib/xip/generators/builder/.gitignore +30 -0
  40. data/lib/xip/generators/builder/Gemfile +19 -0
  41. data/lib/xip/generators/builder/Procfile.dev +2 -0
  42. data/lib/xip/generators/builder/README.md +9 -0
  43. data/lib/xip/generators/builder/Rakefile +2 -0
  44. data/lib/xip/generators/builder/bot/controllers/bot_controller.rb +55 -0
  45. data/lib/xip/generators/builder/bot/controllers/catch_alls_controller.rb +21 -0
  46. data/lib/xip/generators/builder/bot/controllers/concerns/.keep +0 -0
  47. data/lib/xip/generators/builder/bot/controllers/goodbyes_controller.rb +9 -0
  48. data/lib/xip/generators/builder/bot/controllers/hellos_controller.rb +9 -0
  49. data/lib/xip/generators/builder/bot/controllers/interrupts_controller.rb +9 -0
  50. data/lib/xip/generators/builder/bot/controllers/unrecognized_messages_controller.rb +9 -0
  51. data/lib/xip/generators/builder/bot/helpers/bot_helper.rb +2 -0
  52. data/lib/xip/generators/builder/bot/models/bot_record.rb +3 -0
  53. data/lib/xip/generators/builder/bot/models/concerns/.keep +0 -0
  54. data/lib/xip/generators/builder/bot/replies/catch_alls/level1.yml +2 -0
  55. data/lib/xip/generators/builder/bot/replies/goodbyes/say_goodbye.yml +2 -0
  56. data/lib/xip/generators/builder/bot/replies/hellos/say_hello.yml +2 -0
  57. data/lib/xip/generators/builder/config.ru +4 -0
  58. data/lib/xip/generators/builder/config/boot.rb +6 -0
  59. data/lib/xip/generators/builder/config/database.yml +25 -0
  60. data/lib/xip/generators/builder/config/environment.rb +2 -0
  61. data/lib/xip/generators/builder/config/flow_map.rb +25 -0
  62. data/lib/xip/generators/builder/config/initializers/autoload.rb +8 -0
  63. data/lib/xip/generators/builder/config/initializers/inflections.rb +16 -0
  64. data/lib/xip/generators/builder/config/puma.rb +25 -0
  65. data/lib/xip/generators/builder/config/services.yml +35 -0
  66. data/lib/xip/generators/builder/config/sidekiq.yml +3 -0
  67. data/lib/xip/generators/builder/db/seeds.rb +7 -0
  68. data/lib/xip/generators/generate.rb +39 -0
  69. data/lib/xip/generators/generate/flow/controllers/controller.tt +7 -0
  70. data/lib/xip/generators/generate/flow/helpers/helper.tt +3 -0
  71. data/lib/xip/generators/generate/flow/replies/ask_example.tt +9 -0
  72. data/lib/xip/helpers/redis.rb +40 -0
  73. data/lib/xip/jobs.rb +9 -0
  74. data/lib/xip/lock.rb +82 -0
  75. data/lib/xip/logger.rb +9 -3
  76. data/lib/xip/migrations/configurator.rb +73 -0
  77. data/lib/xip/migrations/generators.rb +16 -0
  78. data/lib/xip/migrations/railtie_config.rb +14 -0
  79. data/lib/xip/migrations/tasks.rb +43 -0
  80. data/lib/xip/nlp/client.rb +21 -0
  81. data/lib/xip/nlp/result.rb +56 -0
  82. data/lib/xip/reloader.rb +89 -0
  83. data/lib/xip/reply.rb +36 -0
  84. data/lib/xip/scheduled_reply.rb +18 -0
  85. data/lib/xip/server.rb +63 -0
  86. data/lib/xip/service_message.rb +17 -0
  87. data/lib/xip/service_reply.rb +44 -0
  88. data/lib/xip/services/base_client.rb +24 -0
  89. data/lib/xip/services/base_message_handler.rb +27 -0
  90. data/lib/xip/services/base_reply_handler.rb +72 -0
  91. data/lib/xip/services/jobs/handle_message_job.rb +21 -0
  92. data/lib/xip/session.rb +203 -0
  93. data/lib/xip/version.rb +7 -1
  94. data/logo.svg +17 -0
  95. data/spec/configuration_spec.rb +93 -0
  96. data/spec/controller/callbacks_spec.rb +217 -0
  97. data/spec/controller/catch_all_spec.rb +154 -0
  98. data/spec/controller/controller_spec.rb +889 -0
  99. data/spec/controller/dynamic_delay_spec.rb +70 -0
  100. data/spec/controller/helpers_spec.rb +119 -0
  101. data/spec/controller/interrupt_detect_spec.rb +171 -0
  102. data/spec/controller/messages_spec.rb +744 -0
  103. data/spec/controller/nlp_spec.rb +93 -0
  104. data/spec/controller/replies_spec.rb +694 -0
  105. data/spec/controller/unrecognized_message_spec.rb +168 -0
  106. data/spec/dispatcher_spec.rb +79 -0
  107. data/spec/flow/flow_spec.rb +82 -0
  108. data/spec/flow/state_spec.rb +109 -0
  109. data/spec/helpers/redis_spec.rb +77 -0
  110. data/spec/lock_spec.rb +100 -0
  111. data/spec/nlp/client_spec.rb +23 -0
  112. data/spec/nlp/result_spec.rb +57 -0
  113. data/spec/replies/hello.yml.erb +15 -0
  114. data/spec/replies/messages/say_hola.yml+facebook.erb +6 -0
  115. data/spec/replies/messages/say_hola.yml+twilio.erb +6 -0
  116. data/spec/replies/messages/say_hola.yml.erb +6 -0
  117. data/spec/replies/messages/say_howdy_with_dynamic.yml +79 -0
  118. data/spec/replies/messages/say_msgs_without_breaks.yml +4 -0
  119. data/spec/replies/messages/say_offer.yml +6 -0
  120. data/spec/replies/messages/say_offer_with_dynamic.yml +6 -0
  121. data/spec/replies/messages/say_oi.yml.erb +15 -0
  122. data/spec/replies/messages/say_randomize_speech.yml +10 -0
  123. data/spec/replies/messages/say_randomize_text.yml +10 -0
  124. data/spec/replies/messages/say_yo.yml +6 -0
  125. data/spec/replies/messages/say_yo.yml+twitter +6 -0
  126. data/spec/replies/messages/sub1/sub2/say_nested.yml +10 -0
  127. data/spec/reply_spec.rb +61 -0
  128. data/spec/scheduled_reply_spec.rb +23 -0
  129. data/spec/service_reply_spec.rb +92 -0
  130. data/spec/session_spec.rb +366 -0
  131. data/spec/spec_helper.rb +22 -66
  132. data/spec/support/alternate_helpers/foo_helper.rb +5 -0
  133. data/spec/support/controllers/vaders_controller.rb +24 -0
  134. data/spec/support/helpers/fun/games_helper.rb +7 -0
  135. data/spec/support/helpers/fun/pdf_helper.rb +7 -0
  136. data/spec/support/helpers/standalone_helper.rb +5 -0
  137. data/spec/support/helpers_typo/users_helper.rb +2 -0
  138. data/spec/support/nlp_clients/dialogflow.rb +9 -0
  139. data/spec/support/nlp_clients/luis.rb +9 -0
  140. data/spec/support/nlp_results/luis_result.rb +163 -0
  141. data/spec/support/sample_messages.rb +66 -0
  142. data/spec/support/services.yml +31 -0
  143. data/spec/support/services_with_erb.yml +31 -0
  144. data/spec/version_spec.rb +16 -0
  145. data/xip.gemspec +25 -14
  146. metadata +320 -18
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xip
4
+ module Migrations
5
+ class InternalConfigurationsProxy
6
+
7
+ attr_reader :configurations
8
+
9
+ def initialize(configurations)
10
+ @configurations = configurations
11
+ end
12
+
13
+ def on(config_key)
14
+ if @configurations[config_key] && block_given?
15
+ @configurations[config_key] = yield(@configurations[config_key]) || @configurations[config_key]
16
+ end
17
+ @configurations[config_key]
18
+ end
19
+ end
20
+
21
+ class Configurator
22
+ def self.load_configurations
23
+ self.new
24
+ @env_config ||= Rails.application.config.database_configuration
25
+ ActiveRecord::Base.configurations = @env_config
26
+ @env_config
27
+ end
28
+
29
+ def self.environments_config
30
+ proxy = InternalConfigurationsProxy.new(load_configurations)
31
+ yield(proxy) if block_given?
32
+ end
33
+
34
+ def initialize(options = {})
35
+ default_schema = ENV['SCHEMA'] || ActiveRecord::Tasks::DatabaseTasks.schema_file(ActiveRecord::Base.schema_format)
36
+ defaults = {
37
+ config: "config/database.yml",
38
+ migrate_dir: "db/migrate",
39
+ seeds: "db/seeds.rb",
40
+ schema: default_schema
41
+ }
42
+ @options = defaults.merge(options)
43
+
44
+ Rails.application.config.root = Pathname.pwd
45
+ Rails.application.config.paths["config/database"] = config
46
+ end
47
+
48
+ def config
49
+ @options[:config]
50
+ end
51
+
52
+ def migrate_dir
53
+ @options[:migrate_dir]
54
+ end
55
+
56
+ def seeds
57
+ @options[:seeds]
58
+ end
59
+
60
+ def schema
61
+ @options[:schema]
62
+ end
63
+
64
+ def config_for_all
65
+ Configurator.load_configurations.dup
66
+ end
67
+
68
+ def config_for(environment)
69
+ config_for_all[environment.to_s]
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+
5
+ module Xip
6
+ module Migrations
7
+ class Generator
8
+ def self.migration(name, options="")
9
+ generator_params = [name] + options.split(" ")
10
+ Rails::Generators.invoke("active_record:migration", generator_params,
11
+ destination_root: Xip.root
12
+ )
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xip
4
+ module Migrations
5
+ class RailtieConfig < Rails::Application
6
+ config.generators.options[:rails] = { orm: :active_record }
7
+
8
+ config.generators.options[:active_record] = {
9
+ :migration => true,
10
+ :timestamps => true
11
+ }
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xip
4
+ module Migrations
5
+ class Tasks
6
+
7
+ class << self
8
+ def configure
9
+ configurator = Configurator.new
10
+
11
+ paths = Rails.application.config.paths
12
+
13
+ paths.add "config/database", with: configurator.config
14
+ paths.add "db/migrate", with: configurator.migrate_dir
15
+ paths.add "db/seeds.rb", with: configurator.seeds
16
+ end
17
+
18
+ def load_tasks
19
+ return unless defined?(ActiveRecord)
20
+
21
+ configure
22
+
23
+ Configurator.environments_config do |proxy|
24
+ ActiveRecord::Tasks::DatabaseTasks.database_configuration = proxy.configurations
25
+ end
26
+
27
+ RailtieConfig.load_tasks
28
+
29
+ # %w(
30
+ # connection
31
+ # environment
32
+ # db/new_migration
33
+ # ).each do
34
+ # |task| load "xip/migrations/tasks/#{task}.rake"
35
+ # end
36
+
37
+ load "active_record/railties/databases.rake"
38
+ end
39
+ end
40
+
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xip
4
+ module Nlp
5
+ class Client
6
+
7
+ def client
8
+ nil
9
+ end
10
+
11
+ def understand(query:)
12
+ nil
13
+ end
14
+
15
+ def understand_speech(audio_file:, audio_config: nil)
16
+ nil
17
+ end
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xip
4
+ module Nlp
5
+ class Result
6
+
7
+ ENTITY_TYPES = %i(number currency email percentage phone age
8
+ url ordinal geo dimension temp datetime duration
9
+ key_phrase name)
10
+
11
+ attr_reader :result
12
+
13
+ def initialize(result:)
14
+ @result = result
15
+ end
16
+
17
+ def parsed_result
18
+ nil
19
+ end
20
+
21
+ def intent_id
22
+ nil
23
+ end
24
+
25
+ def intent
26
+ nil
27
+ end
28
+
29
+ def intent_score
30
+ nil
31
+ end
32
+
33
+ def raw_entities
34
+ {}
35
+ end
36
+
37
+ def entities
38
+ {}
39
+ end
40
+
41
+ # :postive, :negative, :neutral
42
+ def sentiment
43
+ nil
44
+ end
45
+
46
+ def sentiment_score
47
+ nil
48
+ end
49
+
50
+ def present?
51
+ parsed_result.present?
52
+ end
53
+
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'zeitwerk'
4
+
5
+ module Xip
6
+ class Reloader
7
+
8
+ def initialize
9
+ @reloader = Class.new(ActiveSupport::Reloader)
10
+ @loader = Zeitwerk::Loader.new
11
+ # @loader.logger = method(:puts)
12
+ @loader
13
+ end
14
+
15
+ def load_bot!
16
+ load_autoload_paths!
17
+ enable_reloading!
18
+ enable_eager_load!
19
+ @loader.setup
20
+ end
21
+
22
+ def load_autoload_paths!
23
+ if Xip.config.autoload_paths.present?
24
+ Xip.config.autoload_paths.each do |autoload_path|
25
+ @loader.push_dir(autoload_path)
26
+ end
27
+
28
+ # Bot-specific ignores
29
+ Xip.config.autoload_ignore_paths.each do |autoload_ignore_path|
30
+ @loader.ignore(autoload_ignore_path)
31
+ end
32
+
33
+ # Ignore setup files
34
+ @loader.ignore(File.join(Xip.root, 'config', 'initializers'))
35
+ @loader.ignore(File.join(Xip.root, 'config', 'boot.rb'))
36
+ @loader.ignore(File.join(Xip.root, 'config', 'environment.rb'))
37
+ @loader.ignore(File.join(Xip.root, 'config', 'puma.rb'))
38
+ end
39
+ end
40
+
41
+ def enable_eager_load!
42
+ if Xip.config.eager_load
43
+ @loader.eager_load
44
+ Xip::Logger.l(topic: 'xip', message: 'Eager loading enabled.')
45
+ end
46
+ end
47
+
48
+ def enable_reloading!
49
+ if Xip.config.hot_reload
50
+ @checker = ActiveSupport::EventedFileUpdateChecker.new([], files_to_watch) do
51
+ reload!
52
+ end
53
+
54
+ @loader.enable_reloading
55
+ Xip::Logger.l(topic: 'xip', message: 'Hot reloading enabled.')
56
+ end
57
+ end
58
+
59
+ # Only reloads if a change has been detected in one of the autoload files`
60
+ def reload
61
+ if Xip.config.hot_reload
62
+ @checker.execute_if_updated
63
+ end
64
+ end
65
+
66
+ # Force reloads reglardless of filesystem changes
67
+ def reload!
68
+ if Xip.config.hot_reload
69
+ @loader.reload
70
+ end
71
+ end
72
+
73
+ def call
74
+ @reloader.wrap do
75
+ reload
76
+ yield
77
+ end
78
+ end
79
+
80
+ private
81
+
82
+ def files_to_watch
83
+ Xip.config.autoload_paths.map do |path|
84
+ [path, 'rb']
85
+ end.to_h
86
+ end
87
+
88
+ end
89
+ end
@@ -0,0 +1,36 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Xip
5
+ class Reply
6
+
7
+ attr_accessor :reply_type, :reply
8
+
9
+ def initialize(unstructured_reply:)
10
+ @reply_type = unstructured_reply["reply_type"]
11
+ @reply = unstructured_reply
12
+ end
13
+
14
+ def [](key)
15
+ @reply[key]
16
+ end
17
+
18
+ def []=(key, value)
19
+ @reply[key] = value
20
+ end
21
+
22
+ def delay?
23
+ reply_type == 'delay'
24
+ end
25
+
26
+ def self.dynamic_delay
27
+ self.new(
28
+ unstructured_reply: {
29
+ 'reply_type' => 'delay',
30
+ 'duration' => 'dynamic'
31
+ }
32
+ )
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,18 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Xip
5
+
6
+ class ScheduledReplyJob < Xip::Jobs
7
+ sidekiq_options queue: :xip_replies, retry: false
8
+
9
+ def perform(service, user_id, flow, state, target_id=nil)
10
+ service_message = ServiceMessage.new(service: service)
11
+ service_message.sender_id = user_id
12
+ service_message.target_id = target_id
13
+ controller = BotController.new(service_message: service_message)
14
+ controller.step_to(flow: flow, state: state)
15
+ end
16
+ end
17
+
18
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sinatra/base'
4
+ require 'multi_json'
5
+
6
+ module Xip
7
+ class Server < Sinatra::Base
8
+
9
+ def self.get_or_post(url, &block)
10
+ get(url, &block)
11
+ post(url, &block)
12
+ end
13
+
14
+ get '/' do
15
+ <<~WELCOME
16
+ <html>
17
+ <head>
18
+ <title>Xip</title>
19
+ </head>
20
+ <body>
21
+ <center>
22
+ <a href='https://xipkit.com'>
23
+ <img src='http://cdn.xipkit.com/logo-light.svg' height='120' alt='Xip Logo' aria-label='xipkit.com' />
24
+ </a>
25
+ </center>
26
+ </body>
27
+ </html>
28
+ WELCOME
29
+ end
30
+
31
+ get_or_post '/incoming/:service' do
32
+ Xip::Logger.l(topic: params[:service], message: 'Received webhook.')
33
+
34
+ # JSON params need to be parsed and added to the params
35
+ if request.env['CONTENT_TYPE']&.match(/application\/json/i)
36
+ json_params = MultiJson.load(request.body.read)
37
+ params.merge!(json_params)
38
+ end
39
+
40
+ dispatcher = Xip::Dispatcher.new(
41
+ service: params[:service],
42
+ params: params,
43
+ headers: get_helpers_from_request(request)
44
+ )
45
+
46
+ headers 'Access-Control-Allow-Origin' => '*',
47
+ 'Access-Control-Allow-Methods' => ['OPTIONS', 'GET', 'POST']
48
+ # content_type 'audio/mp3'
49
+ content_type 'application/octet-stream'
50
+
51
+ dispatcher.coordinate
52
+ end
53
+
54
+ private
55
+
56
+ def get_helpers_from_request(request)
57
+ request.env.select do |header, value|
58
+ %w[HTTP_HOST].include?(header)
59
+ end
60
+ end
61
+
62
+ end
63
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xip
4
+ class ServiceMessage
5
+
6
+ attr_accessor :sender_id, :target_id, :timestamp, :service, :message,
7
+ :location, :attachments, :payload, :referral, :nlp_result,
8
+ :catch_all_reason
9
+
10
+ def initialize(service:)
11
+ @service = service
12
+ @attachments = []
13
+ @location = {}
14
+ end
15
+
16
+ end
17
+ end