xip 0.0.1 → 2.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
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,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xip
4
+ class Controller
5
+ module Nlp
6
+
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ # Memoized in order to prevent multiple requests to the NLP provider
11
+ def perform_nlp!
12
+ Xip::Logger.l(
13
+ topic: :nlp,
14
+ message: "User #{current_session_id} -> Performing NLP."
15
+ )
16
+
17
+ unless Xip.config.nlp_integration.present?
18
+ raise Xip::Errors::ConfigurationError, "An NLP integration has not yet been configured (Xip.config.nlp_integration)"
19
+ end
20
+
21
+ @nlp_result ||= begin
22
+ nlp_client = nlp_client_klass.new
23
+ @nlp_result = @current_message.nlp_result = nlp_client.understand(
24
+ query: current_message.message
25
+ )
26
+
27
+ if Xip.config.log_all_nlp_results
28
+ Xip::Logger.l(
29
+ topic: :nlp,
30
+ message: "User #{current_session_id} -> NLP Result: #{@nlp_result.parsed_result.inspect}"
31
+ )
32
+ end
33
+
34
+ @nlp_result
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def nlp_client_klass
41
+ integration = Xip.config.nlp_integration.to_s.titlecase
42
+ klass = "Xip::Nlp::#{integration}::Client"
43
+ klass.classify.constantize
44
+ end
45
+ end
46
+
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,281 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xip
4
+ class Controller
5
+ module Replies
6
+
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+
11
+ class_attribute :_preprocessors, default: [:erb]
12
+ class_attribute :_replies_path, default: [Xip.root, 'bot', 'replies']
13
+
14
+ def send_replies(custom_reply: nil, inline: nil)
15
+ service_reply = load_service_reply(
16
+ custom_reply: custom_reply,
17
+ inline: inline
18
+ )
19
+
20
+ # Determine if we start at the beginning or somewhere else
21
+ reply_range = calculate_reply_range
22
+ offset = reply_range.first
23
+
24
+ @previous_reply = nil
25
+ service_reply.replies.slice(reply_range).each_with_index do |reply, i|
26
+ # Updates the lock with the current position of the reply
27
+ lock_session!(
28
+ session_slug: current_session.get_session,
29
+ position: i + offset # Otherwise this won't account for explicit starting points
30
+ )
31
+
32
+ begin
33
+ send_reply(reply: reply)
34
+ rescue Xip::Errors::UserOptOut => e
35
+ user_opt_out_handler(msg: e.message)
36
+ return
37
+ rescue Xip::Errors::InvalidSessionID => e
38
+ invalid_session_id_handler(msg: e.message)
39
+ return
40
+ end
41
+
42
+ @previous_reply = reply
43
+ end
44
+
45
+ @progressed = :sent_replies
46
+ ensure
47
+ release_lock!
48
+ end
49
+
50
+ private
51
+
52
+ def send_reply(reply:)
53
+ if !reply.delay? && Xip.config.auto_insert_delays
54
+ # if it's the first reply in the service_reply or the previous reply
55
+ # wasn't a custom delay, then insert a delay
56
+ if @previous_reply.blank? || !@previous_reply.delay?
57
+ send_reply(reply: Reply.dynamic_delay)
58
+ end
59
+ end
60
+
61
+ # Support randomized replies for text and speech replies.
62
+ # We select one before handing the reply off to the driver.
63
+ if reply['text'].is_a?(Array)
64
+ reply['text'] = reply['text'].sample
65
+ end
66
+
67
+ handler = reply_handler.new(
68
+ recipient_id: current_message.sender_id,
69
+ reply: reply
70
+ )
71
+
72
+ translated_reply = handler.send(reply.reply_type)
73
+ client = service_client.new(reply: translated_reply)
74
+ client.transmit
75
+
76
+ log_reply(reply) if Xip.config.transcript_logging
77
+
78
+ # If this was a 'delay' type of reply, we insert the delay
79
+ if reply.delay?
80
+ insert_delay(duration: reply['duration'])
81
+ end
82
+ end
83
+
84
+ def insert_delay(duration:)
85
+ begin
86
+ sleep_duration = if duration == 'dynamic'
87
+ dyn_duration = dynamic_delay(previous_reply: @previous_reply)
88
+
89
+ Xip.config.dynamic_delay_muliplier * dyn_duration
90
+ else
91
+ Float(duration)
92
+ end
93
+
94
+ sleep(sleep_duration)
95
+ rescue ArgumentError, TypeError
96
+ raise(ArgumentError, 'Invalid duration specified. Duration must be a Numeric')
97
+ end
98
+ end
99
+
100
+ def load_service_reply(custom_reply:, inline:)
101
+ if inline.present?
102
+ Xip::ServiceReply.new(
103
+ recipient_id: current_session_id,
104
+ yaml_reply: inline,
105
+ preprocessor: :none,
106
+ context: nil
107
+ )
108
+ else
109
+ yaml_reply, preprocessor = action_replies(custom_reply)
110
+
111
+ Xip::ServiceReply.new(
112
+ recipient_id: current_session_id,
113
+ yaml_reply: yaml_reply,
114
+ preprocessor: preprocessor,
115
+ context: binding
116
+ )
117
+ end
118
+ end
119
+
120
+ def service_client
121
+ begin
122
+ Kernel.const_get("Xip::Services::#{current_service.classify}::Client")
123
+ rescue NameError
124
+ raise(Xip::Errors::ServiceNotRecognized, "The service '#{current_service}' was not recognized")
125
+ end
126
+ end
127
+
128
+ def reply_handler
129
+ begin
130
+ Kernel.const_get("Xip::Services::#{current_service.classify}::ReplyHandler")
131
+ rescue NameError
132
+ raise(Xip::Errors::ServiceNotRecognized, "The service '#{current_service}' was not recognized")
133
+ end
134
+ end
135
+
136
+ def replies_folder
137
+ current_session.flow_string.underscore.pluralize
138
+ end
139
+
140
+ def reply_dir
141
+ [*self._replies_path, replies_folder]
142
+ end
143
+
144
+ def base_reply_filename
145
+ "#{current_session.state_string}.yml"
146
+ end
147
+
148
+ def reply_filenames(custom_reply_filename=nil)
149
+ reply_filename = if custom_reply_filename.present?
150
+ custom_reply_filename
151
+ else
152
+ base_reply_filename
153
+ end
154
+
155
+ service_filename = [reply_filename, current_service].join('+')
156
+
157
+ # Service-specific filenames take precedance (returned first)
158
+ [service_filename, reply_filename]
159
+ end
160
+
161
+ def find_reply_and_preprocessor(custom_reply)
162
+ selected_preprocessor = :none
163
+
164
+ if custom_reply.present?
165
+ dir_and_file = custom_reply.rpartition(File::SEPARATOR)
166
+ _dir = dir_and_file.first
167
+ _file = "#{dir_and_file.last}.yml"
168
+ _replies_dir = [*self._replies_path, _dir]
169
+ possible_filenames = reply_filenames(_file)
170
+ reply_file_path = File.join(_replies_dir, _file)
171
+ service_reply_path = File.join(_replies_dir, reply_filenames(_file).first)
172
+ else
173
+ _replies_dir = *reply_dir
174
+ possible_filenames = reply_filenames
175
+ reply_file_path = File.join(_replies_dir, base_reply_filename)
176
+ service_reply_path = File.join(_replies_dir, reply_filenames.first)
177
+ end
178
+
179
+ # Check if the service_filename exists
180
+ # If so, we can skip checking for a preprocessor
181
+ if File.exist?(service_reply_path)
182
+ return service_reply_path, selected_preprocessor
183
+ end
184
+
185
+ # Cycles through possible preprocessor and variant combinations
186
+ # Early returns for performance
187
+ for preprocessor in self.class._preprocessors do
188
+ for reply_filename in possible_filenames do
189
+ selected_filepath = File.join(_replies_dir, [reply_filename, preprocessor.to_s].join('.'))
190
+ if File.exist?(selected_filepath)
191
+ reply_file_path = selected_filepath
192
+ selected_preprocessor = preprocessor
193
+ return reply_file_path, selected_preprocessor
194
+ end
195
+ end
196
+ end
197
+
198
+ return reply_file_path, selected_preprocessor
199
+ end
200
+
201
+ def action_replies(custom_reply=nil)
202
+ reply_path, selected_preprocessor = find_reply_and_preprocessor(custom_reply)
203
+
204
+ begin
205
+ file_contents = File.read(reply_path)
206
+ rescue Errno::ENOENT
207
+ raise(Xip::Errors::ReplyNotFound, "Could not find reply: '#{reply_path}'")
208
+ end
209
+
210
+ return file_contents, selected_preprocessor
211
+ end
212
+
213
+ def user_opt_out_handler(msg:)
214
+ if self.respond_to?(:handle_opt_out, true)
215
+ self.send(:handle_opt_out)
216
+ Xip::Logger.l(
217
+ topic: current_service,
218
+ message: "User #{current_session_id} opted out. [#{msg}]"
219
+ )
220
+ else
221
+ Xip::Logger.l(
222
+ topic: :err,
223
+ message: "User #{current_session_id} unhandled exception due to opt-out."
224
+ )
225
+ end
226
+
227
+ do_nothing
228
+ end
229
+
230
+ def invalid_session_id_handler(msg:)
231
+ if self.respond_to?(:handle_invalid_session_id, true)
232
+ self.send(:handle_invalid_session_id)
233
+ Xip::Logger.l(
234
+ topic: current_service,
235
+ message: "User #{current_session_id} has an invalid session_id. [#{msg}]"
236
+ )
237
+ else
238
+ Xip::Logger.l(
239
+ topic: :err,
240
+ message: "User #{current_session_id} unhandled exception due to invalid session_id."
241
+ )
242
+ end
243
+
244
+ do_nothing
245
+ end
246
+
247
+ def calculate_reply_range
248
+ # if an explicit starting point is specified, use that until the
249
+ # end of the range, otherwise start at the beginning
250
+ if @pos.present?
251
+ (@pos..-1)
252
+ else
253
+ (0..-1)
254
+ end
255
+ end
256
+
257
+ def log_reply(reply)
258
+ message = case reply.reply_type
259
+ when 'text'
260
+ reply['text']
261
+ when 'speech'
262
+ reply['speech']
263
+ when 'ssml'
264
+ reply['ssml']
265
+ when 'delay'
266
+ '<typing indicator>'
267
+ else
268
+ "<#{reply.reply_type}>"
269
+ end
270
+
271
+ Xip::Logger.l(
272
+ topic: current_service,
273
+ message: "User #{current_session_id} -> Sending: #{message}"
274
+ )
275
+ end
276
+
277
+ end # instance methods
278
+
279
+ end
280
+ end
281
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xip
4
+ class Controller
5
+ module UnrecognizedMessage
6
+
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+
11
+ def run_unrecognized_message(err:)
12
+ err_message = "The message \"#{current_message.message}\" was not recognized in the original context."
13
+
14
+ Xip::Logger.l(
15
+ topic: 'unrecognized_message',
16
+ message: err_message
17
+ )
18
+
19
+ unless defined?(UnrecognizedMessagesController)
20
+ Xip::Logger.l(
21
+ topic: 'unrecognized_message',
22
+ message: 'Running catch_all; UnrecognizedMessagesController not defined.'
23
+ )
24
+
25
+ run_catch_all(err: err)
26
+ return false
27
+ end
28
+
29
+ unrecognized_msg_controller = UnrecognizedMessagesController.new(
30
+ service_message: current_message
31
+ )
32
+
33
+ begin
34
+ # Run handle_unrecognized_message action
35
+ unrecognized_msg_controller.handle_unrecognized_message
36
+
37
+ if unrecognized_msg_controller.progressed?
38
+ Xip::Logger.l(
39
+ topic: 'unrecognized_message',
40
+ message: 'A match was detected. Skipping catch-all.'
41
+ )
42
+ else
43
+ # Log, but we don't want to run the catch_all for a poorly
44
+ # coded UnrecognizedMessagesController
45
+ Xip::Logger.l(
46
+ topic: 'unrecognized_message',
47
+ message: 'Did not send replies, update session, or step'
48
+ )
49
+ end
50
+ rescue StandardError => e
51
+ # Run the catch_all directly since we're already in an unrecognized
52
+ # message state
53
+ run_catch_all(err: e)
54
+ end
55
+ end
56
+
57
+ end
58
+
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ Dir.glob(File.expand_path('core_ext/*.rb', __dir__)).each do |path|
4
+ require path
5
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Numeric
4
+
5
+ def states
6
+ self
7
+ end
8
+ alias :state :states
9
+
10
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ class String
4
+
5
+ EXCLUDED_CHARS = %w[" ' . , ! ? ( ) - _ ` ‘ ’ “ ”].freeze
6
+ EXCLUDED_CHARS_ESC = EXCLUDED_CHARS.map { |c| "\\#{c}" }
7
+ EXCLUDED_CHARS_RE = /#{EXCLUDED_CHARS_ESC.join('|')}/
8
+
9
+ # Removes blank padding and double+single quotes
10
+ def normalize
11
+ self.upcase.strip
12
+ end
13
+
14
+ def without_punctuation
15
+ self.gsub(EXCLUDED_CHARS_RE, '')
16
+ end
17
+
18
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xip
4
+
5
+ # Responsible for coordinating incoming messages
6
+ # 1. Receives incoming request params
7
+ # 2. Initializes respective service request handler
8
+ # 3. Processes params through service request handler (might be async)
9
+ # 4. Inits base XipController with state params returned from the service
10
+ # request handler
11
+ # 5. Returns an HTTP response to be returned to the requestor
12
+ class Dispatcher
13
+
14
+ attr_reader :service, :params, :headers, :message_handler
15
+
16
+ def initialize(service:, params:, headers:)
17
+ @service = service
18
+ @params = params
19
+ @headers = headers
20
+ @message_handler = message_handler_klass.new(
21
+ params: params,
22
+ headers: headers
23
+ )
24
+ end
25
+
26
+ def coordinate
27
+ message_handler.coordinate
28
+ end
29
+
30
+ def process
31
+ service_message = message_handler.process
32
+
33
+ if Xip.config.transcript_logging
34
+ log_incoming_message(service_message)
35
+ end
36
+
37
+ bot_controller = BotController.new(service_message: service_message)
38
+ bot_controller.route
39
+ end
40
+
41
+ private
42
+
43
+ def message_handler_klass
44
+ begin
45
+ Kernel.const_get("Xip::Services::#{service.classify}::MessageHandler")
46
+ rescue NameError
47
+ raise(Xip::Errors::ServiceNotRecognized, "The service '#{service}' was not recognized")
48
+ end
49
+ end
50
+
51
+ def log_incoming_message(service_message)
52
+ message = if service_message.location.present?
53
+ "Received: <user shared location>"
54
+ elsif service_message.attachments.present?
55
+ "Received: <user sent attachment>"
56
+ elsif service_message.payload.present?
57
+ "Received Payload: #{service_message.payload}"
58
+ else
59
+ "Received Message: #{service_message.message}"
60
+ end
61
+
62
+ Xip::Logger.l(
63
+ topic: 'user',
64
+ message: "User #{service_message.sender_id} -> #{message}"
65
+ )
66
+ end
67
+ end
68
+ end