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,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