kogno 1.0.1

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 (67) hide show
  1. checksums.yaml +7 -0
  2. data/bin/kogno +92 -0
  3. data/lib/boot.rb +9 -0
  4. data/lib/core/bin_helpers/messenger_ctl.rb +48 -0
  5. data/lib/core/bin_helpers/scaffolding.rb +203 -0
  6. data/lib/core/bin_helpers/scheduled_messages_ctl.rb +95 -0
  7. data/lib/core/bin_helpers/sequences_ctl.rb +95 -0
  8. data/lib/core/bin_helpers/server_ctl.rb +127 -0
  9. data/lib/core/bin_helpers/telegram_ctl.rb +48 -0
  10. data/lib/core/bin_helpers/webhook_ctl.rb +96 -0
  11. data/lib/core/db.rb +8 -0
  12. data/lib/core/extensions/array.rb +28 -0
  13. data/lib/core/extensions/hash.rb +12 -0
  14. data/lib/core/extensions/logger.rb +70 -0
  15. data/lib/core/extensions/string.rb +6 -0
  16. data/lib/core/extensions/wit.rb +60 -0
  17. data/lib/core/global_methods.rb +67 -0
  18. data/lib/core/helpers/string.rb +105 -0
  19. data/lib/core/lib/base_config.rb +17 -0
  20. data/lib/core/lib/block_params.rb +4 -0
  21. data/lib/core/lib/context.rb +1573 -0
  22. data/lib/core/lib/error_handler.rb +36 -0
  23. data/lib/core/lib/message.rb +182 -0
  24. data/lib/core/lib/messenger/api.rb +281 -0
  25. data/lib/core/lib/messenger/facebook_graph.rb +32 -0
  26. data/lib/core/lib/messenger/message.rb +202 -0
  27. data/lib/core/lib/messenger/notification.rb +351 -0
  28. data/lib/core/lib/messenger/post_comment.rb +104 -0
  29. data/lib/core/lib/messenger/recurring_notification.rb +81 -0
  30. data/lib/core/lib/nlp.rb +191 -0
  31. data/lib/core/lib/notification.rb +371 -0
  32. data/lib/core/lib/spelling.rb +13 -0
  33. data/lib/core/lib/telegram/api.rb +197 -0
  34. data/lib/core/lib/telegram/chat_activity.rb +111 -0
  35. data/lib/core/lib/telegram/inline_query.rb +112 -0
  36. data/lib/core/lib/telegram/message.rb +327 -0
  37. data/lib/core/lib/telegram/notification.rb +507 -0
  38. data/lib/core/lib/whatsapp/api.rb +153 -0
  39. data/lib/core/lib/whatsapp/message.rb +132 -0
  40. data/lib/core/lib/whatsapp/notification.rb +206 -0
  41. data/lib/core/lib/whatsapp/status_message.rb +58 -0
  42. data/lib/core/loaders/config_files.rb +15 -0
  43. data/lib/core/models/chat_log.rb +4 -0
  44. data/lib/core/models/long_payload.rb +25 -0
  45. data/lib/core/models/matched_message.rb +5 -0
  46. data/lib/core/models/messenger_recurring_notification.rb +16 -0
  47. data/lib/core/models/scheduled_message.rb +40 -0
  48. data/lib/core/models/sequence.rb +29 -0
  49. data/lib/core/models/telegram_chat_group.rb +26 -0
  50. data/lib/core/models/user.rb +285 -0
  51. data/lib/core/web/webhook.rb +198 -0
  52. data/lib/kogno.rb +130 -0
  53. data/scaffolding/new_project/Gemfile +3 -0
  54. data/scaffolding/new_project/application.rb +5 -0
  55. data/scaffolding/new_project/bot/contexts/main_context.rb +10 -0
  56. data/scaffolding/new_project/bot/conversation.rb +14 -0
  57. data/scaffolding/new_project/bot/models/user.rb +3 -0
  58. data/scaffolding/new_project/config/application.rb +28 -0
  59. data/scaffolding/new_project/config/database.yml +8 -0
  60. data/scaffolding/new_project/config/locales/en.yml +4 -0
  61. data/scaffolding/new_project/config/locales/es.yml +3 -0
  62. data/scaffolding/new_project/config/nlp.rb +23 -0
  63. data/scaffolding/new_project/config/platforms/messenger.rb +74 -0
  64. data/scaffolding/new_project/config/platforms/telegram.rb +45 -0
  65. data/scaffolding/new_project/config/platforms/whatsapp.rb +13 -0
  66. data/scaffolding/new_project/web/routes.rb +10 -0
  67. metadata +220 -0
@@ -0,0 +1,81 @@
1
+ module Kogno
2
+ module Messenger
3
+ class RecurringNotification < Kogno::Message
4
+
5
+ attr_accessor :event
6
+ attr_accessor :nlp
7
+
8
+ @overwritten_payload = nil
9
+
10
+ def initialize(data)
11
+ @data = data
12
+ @page_id = page_id
13
+ end
14
+
15
+ def type
16
+ :recurring_notification
17
+ end
18
+
19
+ def sender_id
20
+ return @data[:sender][:id]
21
+ end
22
+
23
+ def page_id
24
+ return @data[:recipient][:id]
25
+ end
26
+
27
+ def raw_payload
28
+ payload = @data[:optin][:payload] rescue nil
29
+ return(payload)
30
+ end
31
+
32
+ def notification_messages_status
33
+ if @data[:optin][:notification_messages_status] == "STOP_NOTIFICATIONS"
34
+ :stopped
35
+ else
36
+ :active
37
+ end
38
+ end
39
+
40
+ def get_context(user,notification)
41
+ # Here you can use different contexts by self.post_id
42
+ context_class = Context::router(Kogno::Application.config.routes.recurring_notification)[:class]
43
+ context = context_class.new(user,self,notification,nil)
44
+ return(context)
45
+ end
46
+
47
+ def handle_event(debug=false)
48
+
49
+ begin
50
+
51
+ user = User.find_or_create_by_psid(self.sender_id, :messenger, self.page_id)
52
+ user.update_messenger_recurring_notification(@data[:optin])
53
+ user.get_session_vars
54
+ self.set_nlp(user.locale)
55
+ I18n.locale = user.locale unless user.locale.nil?
56
+
57
+ notification = Notification.new(user,self)
58
+
59
+ context = get_context(user,notification)
60
+ context.run_for_recurring_notification_only
61
+
62
+ notification.send
63
+
64
+ if Kogno::Application.config.store_log_in_database
65
+ user.log_message(self, :recurring_notifications)
66
+ user.log_response(notification)
67
+ end
68
+
69
+ rescue StandardError => e
70
+ error_token = Digest::MD5.hexdigest("#{Time.now}#{rand(1000)}") # This helps to identify the error that arrives to Slack in order to search it in logs/http.log
71
+ logger.write e.message, :red
72
+ logger.write "Error Token: #{error_token}", :red
73
+ logger.write "Backtrace:\n\t#{e.backtrace.join("\n\t")}", :red
74
+ ErrorHandler.notify_by_slack(Kogno::Application.config.app_name,e, error_token) if Kogno::Application.config.error_notifier.slack[:enable] rescue false
75
+ end
76
+
77
+ end
78
+
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,191 @@
1
+ require 'wit'
2
+ module Kogno
3
+ class Nlp
4
+
5
+ attr_accessor :wit, :intents, :entities, :traits, :processed, :phrase, :context_reference
6
+
7
+ def initialize(phrase=nil,locale=:en, context_reference=nil)
8
+ self.wit = Wit.new(access_token: Nlp.get_wit_token(locale), api_version: Kogno::Application.config.nlp.wit[:api_version])
9
+ self.processed = false
10
+ self.phrase = phrase
11
+ self.context_reference = context_reference
12
+ self.entities = {}
13
+ end
14
+
15
+ def self.get_wit_token(locale)
16
+ tokens = Kogno::Application.config.nlp.wit[:apps]
17
+ if !locale.nil? and !tokens[locale.to_sym].nil?
18
+ return tokens[locale.to_sym]
19
+ else # Hash
20
+ return tokens[:default]
21
+ end
22
+ end
23
+
24
+ def set_context_reference(context_reference)
25
+ self.context_reference = context_reference
26
+ end
27
+
28
+ def process_phrase(phrase=nil, context_reference=nil)
29
+ if !Kogno::Application.config.nlp.wit[:enable]
30
+ logger.write "NLP Service is not enabled in config/initializers/nlp.rb", :red
31
+ return false
32
+ end
33
+
34
+ wit_tries = 0
35
+
36
+ phrase = phrase.nil? ? self.phrase : phrase
37
+ context_reference = context_reference.nil? ? self.context_reference : context_reference
38
+
39
+ self.processed = true
40
+ if phrase.nil? or phrase.empty?
41
+ {}
42
+ elsif phrase.length > 256
43
+ {}
44
+ else
45
+
46
+ phrase = Spelling::correction(phrase)
47
+ # logger.write "-----", :red
48
+ # logger.write phrase, :red
49
+
50
+ wit_output = nil
51
+ loop do
52
+ wit_output = self.wit.message_with_context(phrase, context_reference) rescue :error
53
+ break if wit_output != :error || wit_tries == 3
54
+ logger.debug "Wit error!!!", :red
55
+ wit_tries+=1
56
+ sleep(1)
57
+ end
58
+
59
+ self.intents = wit_output["intents"].deep_symbolize_keys! rescue {}
60
+ self.entities = wit_output["entities"].deep_symbolize_keys! rescue {}
61
+ self.traits = wit_output["traits"].deep_symbolize_keys! rescue {}
62
+
63
+ logger.debug " NLP RESOLVED VALUES => #{wit_output}", :light_blue
64
+
65
+ self.add_pre_processed_entities()
66
+ end
67
+
68
+ end
69
+
70
+ def process_phrase_once
71
+ unless self.processed
72
+ self.process_phrase
73
+ end
74
+ end
75
+
76
+ def empty_entities?
77
+ self.processed and self.entities.empty?
78
+ end
79
+
80
+ def intent(name_only=true)
81
+ final_intent = nil
82
+ intents = (self.intents rescue nil)
83
+ unless intents.nil?
84
+ intents.each do |intent|
85
+ if final_intent.nil?
86
+ final_intent = intent
87
+ elsif intent[:confidence] > final_intent[:confidence]
88
+ final_intent = intent
89
+ end
90
+ end
91
+ end
92
+ if final_intent.nil?
93
+ return nil
94
+ else
95
+ return name_only ? final_intent[:name] : final_intent
96
+ end
97
+ end
98
+
99
+ def expression
100
+ self.entities[:expression].first[:value] rescue nil
101
+ end
102
+
103
+ def location
104
+ self.entities[:location] rescue []
105
+ end
106
+
107
+ def search_query
108
+ self.entities[:search_query] rescue nil
109
+ end
110
+
111
+ def local_search_query
112
+ self.entities[:local_search_query] rescue nil
113
+ end
114
+
115
+
116
+ def dates
117
+ self.entities[:datetime].map{|d| d[:values][0][:value].to_date} rescue []
118
+ end
119
+
120
+ def datetime_range
121
+
122
+ params = self.entities[:"wit$datetime:datetime"] rescue []
123
+
124
+ dates = {
125
+ :from => nil,
126
+ :to => nil
127
+ }
128
+
129
+ # case (params.count rescue 0)
130
+ count = params.count rescue 0
131
+
132
+ # when 1
133
+ if count == 1
134
+ if params[0][:value].nil?
135
+ dates[:from] = (params[0][:values][0][:from][:value].to_date rescue nil)
136
+ dates[:to] = (params[0][:values][0][:to][:value].to_date rescue nil)
137
+ dates[:to] = dates[:to] - 1.day unless dates[:to].nil? #esto es por que el git al traer rango trae un dia mas
138
+ else
139
+ dates[:from] = (params[0][:value].to_date rescue nil)
140
+ unless self.entities[:duration].nil?
141
+ dates[:to] = get_return_date_from_duration(dates[:from]) unless dates[:from].nil?
142
+ end
143
+ end
144
+ # when 2
145
+ elsif count > 1
146
+ dates[:from] = (params[0][:value].to_date rescue nil)
147
+ dates[:to] = (params[1][:value].to_date rescue nil)
148
+ end
149
+
150
+ if ((dates[:from] > dates[:to]) rescue false) #solo para asegurar que la fecha de salida sea anterior a la de llegada
151
+ from_tmp = dates[:from]
152
+ dates[:from] = dates[:to]
153
+ dates[:to] = from_tmp
154
+ end
155
+
156
+ return (dates)
157
+ end
158
+
159
+ def datetime_range?
160
+ datetime_range = self.datetime_range
161
+ !datetime_range[:from].nil? && !datetime_range[:to].nil?
162
+ end
163
+
164
+
165
+ def duration_in_days
166
+
167
+ multiplier = {"day"=>1,"week"=>7,"month"=>31}
168
+ duration = (self.entities[:duration][0] rescue nil)
169
+ unless duration.nil?
170
+ return(duration[:value]*multiplier[duration[:unit]] rescue 0)
171
+ else
172
+ return nil
173
+ end
174
+
175
+ end
176
+
177
+ def self.most_confidence(elements)
178
+ if elements.class == Array
179
+ return elements.sort_by{|element| element["confidence"]}.first
180
+ else
181
+ return false
182
+ end
183
+ end
184
+
185
+ def add_pre_processed_entities
186
+ self.entities[:datetime_range] = self.datetime_range if self.datetime_range?
187
+ self.entities[:datetime] = self.entities[:"wit$datetime:datetime"].map{|v| v[:value] || v[:from][:value] rescue nil} unless self.entities[:"wit$datetime:datetime"].nil?
188
+ end
189
+
190
+ end
191
+ end
@@ -0,0 +1,371 @@
1
+ module Kogno
2
+ class Notification
3
+
4
+ # attr_accessor :template
5
+ # attr_accessor :user, :msg, :context
6
+ # attr_accessor :nlp
7
+
8
+ def initialize(recipient=nil,msg=nil)
9
+ @messages = []
10
+ @inline_query_results = []
11
+ @before_messages = []
12
+ @after_messages = []
13
+ @vars = {}
14
+ @recipient = recipient
15
+ @user = @recipient if @recipient.type == :user
16
+ @message = msg
17
+ @response_log = []
18
+ @message_log = []
19
+ @context_obj_for_tilt_template = nil
20
+ end
21
+
22
+ def set_context(context)
23
+ @context = context
24
+ end
25
+
26
+ def send(messaging_type="RESPONSE",recipient_id=nil,page_id=nil,delete=true)
27
+ logger.write "---- send() not implemented yet for this platform ----", :red
28
+ end
29
+
30
+ def send_using_token()
31
+ logger.write "---- send_with_token() not implemented yet for this platform ----", :red
32
+ end
33
+
34
+ def scheduled(send_at, tag=nil)
35
+ return false unless @recipient.type == :user
36
+ unless @recipient.nil?
37
+ unless @messages.empty?
38
+ @recipient.scheduled_messages.create({
39
+ messages: @messages.to_json,
40
+ tag: tag,
41
+ send_at: send_at
42
+ })
43
+ self.delete_messages()
44
+ else
45
+ logger.write "Error: No messages to send", :red
46
+ end
47
+ end
48
+ end
49
+
50
+ def send_private_reply(recipient_id=nil,page_id=nil,delete=true)
51
+ logger.write "---- send_private_reply not implemented yet for this platform ----", :red
52
+ end
53
+
54
+ def response_log
55
+ @response_log
56
+ end
57
+
58
+ def message_log
59
+ @message_log
60
+ end
61
+
62
+ def destroy_message_log
63
+ @message_log = []
64
+ end
65
+
66
+ def get_psid_from_response_log
67
+ return(@response_log.first[:recipient_id] rescue nil)
68
+ end
69
+
70
+ def send_multiple(users,messaging_type)
71
+ logger.write "---- send_multiple not implemented yet for this platform ----", :red
72
+ end
73
+
74
+ def match_next_message(users,messaging_type)
75
+ logger.write "---- match_next_message isn't working for this platform ----", :red
76
+ end
77
+
78
+ def import_messages(messages,position="")
79
+ messages = messages.class == Array ? messages : JSON.parse(messages,{:symbolize_names => true})
80
+ case position.to_sym
81
+ when :before
82
+ @before_messages = messages
83
+ when :after
84
+ @after_messages = messages
85
+ else
86
+ @messages = messages
87
+ end
88
+ end
89
+
90
+ def export_messages
91
+
92
+ messages = @messages.to_json
93
+ self.delete_messages()
94
+ return messages
95
+
96
+ end
97
+
98
+ def delete_messages()
99
+ @before_messages = []
100
+ @messages = []
101
+ @after_messages = []
102
+ end
103
+
104
+ def raw(params, type=:message)
105
+ self.push_message(params, type)
106
+ end
107
+
108
+ def text(text)
109
+ logger.write "---- text not implemented yet for this platform ----", :red
110
+ end
111
+
112
+ def loading_typing_on(recipient_id)
113
+ logger.write "---- loading_typing_on not implemented yet for this platform ----", :red
114
+ end
115
+
116
+ def loading_typing_off(recipient_id)
117
+ logger.write "---- loading_typing_off not implemented yet for this platform ----", :red
118
+ end
119
+
120
+ def typing_on(duration)
121
+ self.push_message({:action => :typing_on, :duration => duration}, :action)
122
+ end
123
+
124
+ def typing(seconds)
125
+ typing_on(seconds)
126
+ end
127
+
128
+ def typing_off
129
+ self.push_message({:action => :typing_off}, :action)
130
+ end
131
+
132
+ def quick_reply(text,replies,typed_postbacks=true)
133
+ logger.write "---- quick_reply not implemented yet for this platform ----", :red
134
+ end
135
+
136
+ def button(text,buttons,typed_postbacks=true)
137
+ logger.write "---- button not implemented yet for this platform ----", :red
138
+ end
139
+
140
+ def list(elements,buttons=[])
141
+ logger.write "---- list not implemented yet for this platform ----", :red
142
+ end
143
+
144
+ def messenger_generic_template(elements,image_aspect_ratio=:horizontal, quick_replies=[])
145
+ logger.write "---- messenger_generic_template not implemented yet for this platform ----", :red
146
+ end
147
+
148
+ def whatsapp_template(name, components=[], lang="en_US")
149
+ logger.write "---- whatsapp_template not implemented yet for this platform ----", :red
150
+ end
151
+
152
+ def carousel(elements,last_element={},image_aspect_ratio=:horizontal , quick_replies=[])
153
+ logger.write "---- carousel not implemented yet for this platform ----", :red
154
+ end
155
+
156
+ def recurring_notification_request(args)
157
+ logger.write "---- recurring_notification_request not implemented yet for this platform ----", :red
158
+ end
159
+
160
+ def set_vars(vars)
161
+ @vars = @vars.merge(vars)
162
+ end
163
+
164
+ def self.rand_text(text)
165
+ if text.class == Array
166
+ return(text[rand(text.count)])
167
+ else
168
+ return(text)
169
+ end
170
+ end
171
+
172
+
173
+ def replace_place_holders(message)
174
+ message_in_json = message.to_json
175
+ @vars.each do |place_holder,replacement_value|
176
+ message_in_json = message_in_json.gsub(":::#{place_holder}:::",replacement_value.to_s)
177
+ end
178
+ JSON.parse(message_in_json)
179
+ end
180
+
181
+
182
+ def push_message(message,type=:message) # :message o :action
183
+ new_message = {:type => type, :value => message}
184
+ logger.debug_json new_message, :blue
185
+ @messages << new_message
186
+ end
187
+
188
+ def payload(payload,params={})
189
+ "#{payload}:#{params.to_json}"
190
+ end
191
+
192
+ def template(route,params={})
193
+ map = digest_template_route(route)
194
+ if map == false
195
+ return false
196
+ else
197
+ action_group = map[:action_group]
198
+ action = map[:action]
199
+ end
200
+
201
+ if @context.nil?
202
+ @reply = self
203
+ Context.base_template(action_group,action,params,false, self)
204
+ else
205
+ @context.template(action_group,action,params)
206
+ end
207
+ end
208
+
209
+ # def partial(action_group,action,params={})
210
+ # template(action_group,"#{action}",params)
211
+ # end
212
+
213
+ # def template_block(action_group,action,params={},&block)
214
+ # @reply = self
215
+ # eval(template(action_group,action,params,true))
216
+ # end
217
+
218
+ # def partial_block(action_group,action,params={},&block)
219
+ # @reply = self
220
+ # eval(template(action_group,"_#{action}",params,true))
221
+ # end
222
+
223
+ def set_typed_postbacks(typed_postbacks)
224
+ return false unless @recipient.type == :user
225
+ @recipient.vars[:typed_postbacks] = {} if @recipient.vars[:typed_postbacks].nil?
226
+ @recipient.vars[:typed_postbacks] = @recipient.vars[:typed_postbacks].merge(typed_postbacks)
227
+ end
228
+
229
+ # Advanced responses.
230
+
231
+ # def opt_out(text, button_label)
232
+ # self.quick_reply(
233
+ # text,
234
+ # [
235
+ # {
236
+ # :content_type => :text,
237
+ # :title => button_label,
238
+ # :payload => PAYLOADS[:opt_out]
239
+ # }
240
+ # ]
241
+ # )
242
+ # end
243
+
244
+ def location_request(text) # Deprecated by Facebook
245
+ logger.write "---- location_request not implemented yet for this platform ----", :red
246
+ end
247
+
248
+ def url(params)
249
+ logger.write "---- url not implemented yet for this platform ----", :red
250
+ end
251
+
252
+ def location(params)
253
+ logger.write "---- location not implemented yet for this platform ----", :red
254
+ end
255
+
256
+ def image(params)
257
+ logger.write "---- image not implemented yet for this platform ----", :red
258
+ end
259
+
260
+ def video(params)
261
+ logger.write "---- video not implemented yet for this platform ----", :red
262
+ end
263
+
264
+ def document(params)
265
+ logger.write "---- document not implemented yet for this platform ----", :red
266
+ end
267
+
268
+ def contact(params)
269
+ logger.write "---- contact not implemented yet for this platform ----", :red
270
+ end
271
+
272
+ def answer_inline_query(inline_query_id, delete=true)
273
+ logger.write "---- answer_inline_query not implemented yet for this platform ----", :red
274
+ end
275
+
276
+ def answer_callback_query(params={},callback_query_id=nil)
277
+ logger.write "---- answer_callback_query not implemented yet for this platform ----", :red
278
+ end
279
+
280
+ def push_inline_query_result(result)
281
+ logger.write "---- push_inline_query_result not implemented yet for this platform ----", :red
282
+ end
283
+
284
+ def html(code, reply_markup = {} ,extra_params={})
285
+ logger.write "---- html not implemented yet for this platform ----", :red
286
+ end
287
+
288
+ def html_template(action_group, action, params, reply_markup = {} ,extra_params={})
289
+ logger.write "---- html_template not implemented yet for this platform ----", :red
290
+ end
291
+
292
+ def render_html_template(action_group, action, params)
293
+ logger.write "---- render_html_template not implemented yet for this platform ----", :red
294
+ end
295
+
296
+ def markdown(params)
297
+ logger.write "---- markdown not implemented yet for this platform ----", :red
298
+ end
299
+
300
+ def confirm(question,params,type=:quick_reply)
301
+ # params example:
302
+ # {
303
+ # yes: {title: "Yes", payload: true}},
304
+ # no: {title: "No", :payload: false}
305
+ # }
306
+ if type == :quick_reply
307
+ self.quick_reply(
308
+ question,
309
+ [
310
+ {
311
+ :content_type => :text,
312
+ :title => params[:yes][:title],
313
+ :payload => params[:yes][:payload]
314
+ },
315
+ {
316
+ :content_type => :text,
317
+ :title => params[:no][:title],
318
+ :payload => params[:no][:payload],
319
+ }
320
+ ],
321
+ {typed_postbacks: true}
322
+ )
323
+ elsif type == :button
324
+ self.button(
325
+ question,
326
+ [
327
+ {
328
+ :type => :postback,
329
+ :title => params[:accept_title],
330
+ :payload => params[:accept_payload]
331
+ },
332
+ {
333
+ :type => :postback,
334
+ :title => params[:reject_title],
335
+ :payload => params[:reject_payload]
336
+ }
337
+ ]
338
+ )
339
+ end
340
+ self.set_typed_postbacks({"YES"=>params[:accept_payload], "NO"=> params[:reject_payload], "CANCEL" => params[:reject_payload]})
341
+ end
342
+
343
+ protected
344
+
345
+ def digest_template_route(route)
346
+
347
+ route_array = route.to_s.split("/")
348
+ if route_array.count > 1
349
+ action_group = route_array[0]
350
+ action = route_array[1]
351
+ else
352
+ if !@context.nil?
353
+ action_group = @context.name
354
+ action = route_array[0]
355
+ else
356
+ raise "Can't determine the context for template #{route}"
357
+ return false
358
+ end
359
+ end
360
+
361
+ return(
362
+ {
363
+ action_group: action_group,
364
+ action: action
365
+ }
366
+ )
367
+
368
+ end
369
+
370
+ end
371
+ end
@@ -0,0 +1,13 @@
1
+ module Kogno
2
+ class Spelling
3
+
4
+ class << self
5
+
6
+ def correction(sentece) # Este metodo deberia ser sobrescrito en app/extensions si hay que agregar corrector ortografico antes del pasarle al wit
7
+ sentece
8
+ end
9
+
10
+ end
11
+
12
+ end
13
+ end