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,1573 @@
1
+ class Kogno::Context
2
+
3
+ @@callbacks = {} # class callbacks: after_initialize,before_blocks, before_exit, after_blocks
4
+ @@expiration_time = {}
5
+
6
+ def initialize(user,msg,notification,temp_context=nil,notification_group=nil,chat=nil)
7
+
8
+ @user = user
9
+ @chat = chat || @user
10
+ @message = msg
11
+ @reply = notification
12
+ @reply_group = notification_group
13
+
14
+ # return true if call_expiration_time()
15
+
16
+ @sub_contexts = []
17
+ @sub_context_route = ""
18
+ # @sub_contexts = {}
19
+ @sub_contexts_tree = []
20
+
21
+ @blocks = {
22
+ deep_link: {},
23
+ anything: {},
24
+ before: {},
25
+ intents: {},
26
+ commands: {},
27
+ expressions: {},
28
+ postbacks: {},
29
+ stickers: {},
30
+ attachments: {},
31
+ keywords: {},
32
+ regular_expressions: {},
33
+ any_number: {},
34
+ any_text: {},
35
+ location: {},
36
+ nlp_entities: {},
37
+ nlp_datetime_range: {},
38
+ nlp_location: {},
39
+ nlp_search_query: {},
40
+ nlp_datetime: {},
41
+ nlp_duration: {},
42
+ any_postback: {},
43
+ any_intent: {},
44
+ everything_else: {},
45
+ after: {},
46
+ membership_new: {},
47
+ membership_drop: {},
48
+ recurring_notification: {}
49
+ }
50
+
51
+ @sequences={}
52
+ @current_sequence_stage = nil
53
+
54
+ @impact = :self
55
+ @deep_blocks = Marshal.load(Marshal.dump(@blocks))
56
+
57
+ @callbacks = {
58
+ before_delegate:{},
59
+ on_delegate:{},
60
+ on_exit:{},
61
+ on_enter:{}
62
+ }
63
+
64
+
65
+ @halt = false
66
+ @continue = false
67
+
68
+ # self.class.after_initialize()
69
+
70
+ @reply.set_context(self)
71
+ @reply_group.set_context(self) unless @reply_group.nil?
72
+ @context = self
73
+
74
+ @current = temp_context.nil? ? @user.context : temp_context
75
+ # @current = Kogno::Application.config.routes.default if @current.nil? || @current.empty?
76
+
77
+ @memorized_message = nil
78
+
79
+ @current_action = {
80
+ action: nil,
81
+ value: nil
82
+ }
83
+
84
+ @called_block = {
85
+ action: nil,
86
+ value: nil,
87
+ params: nil,
88
+ context: nil,
89
+ deep: false
90
+ }
91
+
92
+ call_class_callback(:after_initialize)
93
+
94
+ end
95
+
96
+ def self.expiration_in(expiration_time)
97
+ @@expiration_time[self] = expiration_time
98
+ end
99
+
100
+ def self.after_initialize(method)
101
+ self.set_class_callback(:after_initialize, method)
102
+ end
103
+
104
+ def self.before_blocks(method)
105
+ self.set_class_callback(:before_blocks, method)
106
+ end
107
+
108
+ def self.before_exit(method)
109
+ self.set_class_callback(:before_exit, method)
110
+ end
111
+
112
+ def self.after_blocks(method)
113
+ self.set_class_callback(:after_blocks, method)
114
+ end
115
+
116
+ def self.set_class_callback(action,method)
117
+ @@callbacks[self] = {} if @@callbacks[self].nil?
118
+ @@callbacks[self][action] = [] if @@callbacks[self][action].nil?
119
+ @@callbacks[self][action].push(method)
120
+ end
121
+
122
+ def set_block(action, input, impact, block)
123
+
124
+ if input.nil?
125
+ @blocks[action][@sub_context_route] = block
126
+ if impact == :deep
127
+ # logger.write "---- ADD DEEP IMPACT TO #{action}", :red
128
+ @deep_blocks[action] = block
129
+ end
130
+ else
131
+ @blocks[action][@sub_context_route] = {} if @blocks[action][@sub_context_route].nil?
132
+ @blocks[action][@sub_context_route][input] = block
133
+ if impact == :deep
134
+ @deep_blocks[action][input] = block
135
+ # logger.write "---- ADD DEEP IMPACT TO #{action}(#{imput})", :red
136
+ end
137
+ end
138
+
139
+ # if impact == :deep
140
+ # @deep_blocks[action][input] = block
141
+ # end
142
+
143
+ end
144
+
145
+ def deep
146
+ @impact = :deep
147
+ self
148
+ end
149
+
150
+ def before(section, &block)
151
+
152
+ set_block(:before, section, @impact, block)
153
+ @impact = :self
154
+
155
+ end
156
+
157
+ def after(section, &block)
158
+
159
+ set_block(:after, section, @impact, block)
160
+ @impact = :self
161
+
162
+ end
163
+
164
+ def before_anything(&block)
165
+
166
+ before :blocks do
167
+ block.call
168
+ end
169
+ @impact = :self
170
+
171
+ end
172
+
173
+ def after_all(&block)
174
+
175
+ after :blocks do
176
+ block.call
177
+ end
178
+ @impact = :self
179
+
180
+ end
181
+
182
+ def membership(type, &block)
183
+ if type == :new
184
+ set_block(:membership_new, nil, :self, block)
185
+ elsif type == :drop
186
+ set_block(:membership_drop, nil, :self, block)
187
+ end
188
+ end
189
+
190
+ def recurring_notification(type, &block)
191
+ set_block(:recurring_notification, type, :self, block)
192
+ end
193
+
194
+
195
+ def deep_link(&block)
196
+ set_block(:deep_link, nil, @impact, block)
197
+ @impact = :self
198
+ end
199
+
200
+ def intent(input, &block)
201
+ if input.class == Array
202
+ input.each do |i|
203
+ set_block(:intents, i.to_s.downcase, @impact, block)
204
+ end
205
+ else
206
+ set_block(:intents, input.to_s.downcase, @impact, block)
207
+ end
208
+ @impact = :self
209
+ end
210
+
211
+ def command(input, &block) # Telegram only
212
+ set_block(:commands, input.to_s.downcase, @impact, block)
213
+ @impact = :self
214
+ end
215
+
216
+ def expression(input, &block)
217
+ set_block(:expressions, input, @impact, block)
218
+ @impact = :self
219
+ end
220
+
221
+ def inline_query(&block)
222
+ sub_context :inline_query do
223
+ block.call
224
+ end
225
+ end
226
+
227
+ def postback(input, &block)
228
+ if input.class == Array
229
+ input.each do |i|
230
+ set_block(:postbacks, i.to_s.downcase, @impact, block)
231
+ end
232
+ else
233
+ set_block(:postbacks, input.to_s.downcase, @impact, block)
234
+ end
235
+ @impact = :self
236
+ end
237
+
238
+ def sticker(input, &block)
239
+ set_block(:stickers, sticker_id, @impact, block)
240
+ @impact = :self
241
+ end
242
+
243
+ def any_attachment(&block)
244
+ set_block(:attachments, nil, @impact, block)
245
+ @impact = :self
246
+ end
247
+
248
+ def keyword(input, &block)
249
+ if input.class == Array
250
+ input.each do |i|
251
+ set_block(:keywords, i.to_s.downcase, @impact, block)
252
+ end
253
+ else
254
+ set_block(:keywords, input.to_s.downcase, @impact, block)
255
+ end
256
+ @impact = :self
257
+ end
258
+
259
+ def regular_expression(input, &block)
260
+ set_block(:regular_expressions, input, @impact, block)
261
+ @impact = :self
262
+ end
263
+
264
+ def location(&block)
265
+ set_block(:location, nil, @impact, block)
266
+ @impact = :self
267
+ end
268
+
269
+ def nlp_entity(input, &block)
270
+ set_block(:nlp_entities, input.to_s.downcase.to_sym, @impact, block)
271
+ @impact = :self
272
+ end
273
+
274
+ def entity(input, &block)
275
+ nlp_entity(input, &block)
276
+ @impact = :self
277
+ end
278
+
279
+ def datetime(&block)
280
+ entity "wit$datetime:datetime" do |values|
281
+ block.call values.map{|v| v[:value] || v[:from][:value] rescue nil}
282
+ end
283
+ @impact = :self
284
+ end
285
+
286
+
287
+ def duration(&block)
288
+ entity "wit$duration:duration" do |values|
289
+ block.call values
290
+ end
291
+ @impact = :self
292
+ end
293
+
294
+ def nlp_location(&block)
295
+ nlp_entity("wit$location:location", &block)
296
+ @impact = :self
297
+ end
298
+
299
+ def datetime_range(&block)
300
+ set_block(:nlp_datetime_range, nil, @impact, block)
301
+ end
302
+
303
+ def anything(&block)
304
+ set_block(:anything, nil, @impact, block)
305
+ @impact = :self
306
+ end
307
+
308
+ def any_number(&block)
309
+ set_block(:any_number, nil, @impact, block)
310
+ @impact = :self
311
+ end
312
+
313
+ def any_text(&block)
314
+ set_block(:any_text, nil, @impact, block)
315
+ @impact = :self
316
+ end
317
+
318
+ def any_postback(&block)
319
+ set_block(:any_postback, nil, @impact, block)
320
+ @impact = :self
321
+ end
322
+
323
+ def any_intent(&block)
324
+ set_block(:any_intent, nil, @impact, block)
325
+ @impact = :self
326
+ end
327
+
328
+ def everything_else(&block)
329
+ set_block(:everything_else, nil, @impact, block)
330
+ @impact = :self
331
+ end
332
+
333
+ def callback(callback_name,&block)
334
+ @callbacks[callback_name.to_sym][@sub_context_route] = block
335
+ end
336
+
337
+ # CallBacks shortcuts
338
+
339
+ def before_delegate(&block)
340
+ callback :before_delegate do
341
+ block.call
342
+ end
343
+ end
344
+
345
+ def on_delegate(&block)
346
+ callback :on_delegate do
347
+ block.call
348
+ end
349
+ end
350
+
351
+ def on_exit(&block)
352
+ callback :on_exit do
353
+ block.call
354
+ end
355
+ end
356
+
357
+ def on_enter(&block)
358
+ callback :on_enter do
359
+ block.call
360
+ end
361
+ end
362
+
363
+ #---
364
+
365
+ # def opt_out(route=nil,type=:change,impact=:self,&block)
366
+
367
+ # if route.nil?
368
+ # opt_out_block = block # you define what to do in opt_out
369
+ # elsif type==:delegate
370
+
371
+ # if route == :main # Goes to main context
372
+ # opt_out_block = proc {delegate_to("main")}
373
+ # elsif route == :parent #Goes to parent context, if isn't. Goes go main
374
+ # opt_out_block = proc {delegate_to(self.parent_context_route)}
375
+ # elsif route == :root #Goes to root contexts, not main.
376
+ # opt_out_block = proc {delegate_to(self.root_context_route)}
377
+ # else
378
+ # opt_out_block = proc {delegate_to(route)}
379
+ # end
380
+
381
+ # else # :change
382
+
383
+ # if route == :main or route == :exit # Goes to main context
384
+ # opt_out_block = proc {change_to("main", true)}
385
+ # elsif route == :parent #Goes to parent context, if isn't. Goes go main
386
+ # opt_out_block = proc {change_to(self.parent_context_route, true)}
387
+ # elsif route == :root #Goes to root contexts, not main.
388
+ # opt_out_block = proc {change_to(self.root_context_route, true)}
389
+ # else
390
+ # opt_out_block = proc {change_to(route, true)}
391
+ # end
392
+
393
+ # end
394
+
395
+ # expression "CANCEL", impact do
396
+ # opt_out_block.call
397
+ # end
398
+
399
+ # postback "LEAVE_CONTEXT", impact do
400
+ # opt_out_block.call
401
+ # end
402
+
403
+ # end
404
+
405
+ def sub_context(route, &block)
406
+ @sub_contexts_tree.push(route)
407
+ @sub_context_route = @sub_contexts_tree.join(".")
408
+ @sub_contexts.push(@sub_context_route)
409
+ if in_route? # The sub_context to execute will be only the ones that are in the route of the current context.
410
+ # @halt = {@sub_context_route => false}
411
+ block.call @user.get_context_params
412
+ end
413
+ @sub_contexts_tree.pop
414
+ @sub_context_route = @sub_contexts_tree.join(".")
415
+ end
416
+
417
+ def answer(route, &block)
418
+ sub_context route do |params|
419
+ block.call params
420
+ end
421
+ end
422
+
423
+ def halt(silent=false)
424
+ logger.write("********** HALT **********", :red) unless silent
425
+ @halt = true
426
+ end
427
+
428
+ def halted?
429
+ @halt
430
+ end
431
+
432
+ def continue
433
+ @continue = true
434
+ end
435
+
436
+ def continue?
437
+ if @continue
438
+ logger.write "********** CONTINUE **********", :red
439
+ @continue = false
440
+ return true
441
+ else
442
+ return false
443
+ end
444
+ end
445
+
446
+ def in_route?
447
+ current_sub_context = current_sub_context()
448
+ if current_sub_context.include?("#{@sub_context_route}.") or @sub_context_route == current_sub_context
449
+ true
450
+ else
451
+ false
452
+ end
453
+ end
454
+
455
+ # CALLS
456
+
457
+ def call_block(action, params=nil, input=:empty)
458
+ @current_action = {
459
+ name: action,
460
+ value: input == :empty ? nil : input
461
+ }
462
+ if input.nil?
463
+ return false
464
+ elsif input == :empty
465
+ block = @blocks[action][current_sub_context()] rescue nil
466
+ else
467
+ block = @blocks[action][current_sub_context()][input] rescue nil
468
+ end
469
+ if block.class == Proc
470
+ logger_call(action, input, params, true)
471
+ if params.class == Kogno::BlockParams
472
+ block.call params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7]
473
+ else
474
+ block.call params
475
+ end
476
+ unless continue?
477
+ @called_block = {
478
+ name: action,
479
+ value: input == :empty ? nil : input,
480
+ params: params,
481
+ context: @current,
482
+ deep: false
483
+ }
484
+ return true
485
+ else
486
+ logger_call(action, input, [], false)
487
+ return false
488
+ end
489
+ else
490
+ if input == :empty
491
+ deep_action_block = @deep_blocks[action] rescue nil
492
+ else
493
+ deep_action_block = @deep_blocks[action][input] rescue nil
494
+ end
495
+
496
+ if deep_action_block.class == Proc
497
+ logger_call(action, input, params, true, true)
498
+ if params.class == Kogno::BlockParams
499
+ deep_action_block.call params[0], params[1], params[3], params[4], params[5], params[6], params[7]
500
+ else
501
+ deep_action_block.call params
502
+ end
503
+ unless continue?
504
+ @called_block = {
505
+ name: action,
506
+ value: input == :empty ? nil : input,
507
+ params: params,
508
+ context: @current,
509
+ deep: true
510
+ }
511
+ return true
512
+ else
513
+ logger_call(action, input, [], false)
514
+ return false
515
+ end
516
+ else
517
+ logger_call(action, input, [], false)
518
+ return false
519
+ end
520
+ end
521
+ end
522
+
523
+ def call_before(section=:blocks, values=nil)
524
+ return true if halted?
525
+ call_block(:before, values, section)
526
+ end
527
+
528
+ def call_after(section=:blocks, values=nil)
529
+ return true if halted?
530
+ call_block(:after, values, section)
531
+ end
532
+
533
+
534
+ def call_membership_new
535
+ if @message.type == :chat_activity and @message.chat_membership_status
536
+ return call_block(:membership_new, @message.chat)
537
+ end
538
+ return false
539
+ end
540
+
541
+ def call_membership_drop
542
+ if @message.type == :chat_activity and !@message.chat_membership_status
543
+ return call_block(:membership_drop, @message.chat)
544
+ end
545
+ return false
546
+ end
547
+
548
+ def call_deep_link
549
+ if !@message.referral(:ref).nil?
550
+ return true if halted?
551
+ param = @message.deep_link_param || @message.referral(:ref)
552
+ call_block(:deep_link, param)
553
+ elsif !@message.deep_link_data.nil?
554
+ param = @message.deep_link_param || @message.deep_link_data
555
+ call_block(:deep_link, param)
556
+ else
557
+ return false
558
+ end
559
+ end
560
+
561
+ def call_recurring_notifications
562
+ return false if @message.type != :recurring_notification
563
+ if @message.notification_messages_status == :active
564
+ return call_block(:recurring_notification, @user.messenger_recurring_notification.data, :granted)
565
+ elsif @message.notification_messages_status == :stopped
566
+ return call_block(:recurring_notification, @user.messenger_recurring_notification.data, :removed)
567
+ else
568
+ return false
569
+ end
570
+
571
+ end
572
+
573
+ def call_anything
574
+ call_block(:anything)
575
+ end
576
+
577
+ def call_intents
578
+ intent_data = @message.nlp.intent(false)
579
+ intent = intent_data[:name] rescue nil
580
+ confidence = intent_data[:confidence] rescue nil
581
+ unless intent.nil?
582
+ context_in_intent = self.class.get(intent)
583
+ new_context = nil
584
+ unless context_in_intent.nil?
585
+ new_context = context_in_intent[:context]
586
+ intent = context_in_intent[:param]
587
+ end
588
+ if !new_context.nil? && self.name != new_context
589
+ # new_context = "#{new_context}.inline_query" if @message.type == :inline_query
590
+ logger_call(:intents, intent, [], true)
591
+ if self.delegate_to("/#{new_context}")
592
+ halt
593
+ return true
594
+ else
595
+ return false
596
+ end
597
+ else
598
+ return true if halted?
599
+ block_response = call_block(:intents, Kogno::BlockParams.new([@message.text, @message.nlp.entities, @message.nlp.traits, confidence]), intent)
600
+ block_response = call_any_intent(Kogno::BlockParams.new([intent, @message.text, @message.nlp.entities, @message.nlp.traits, confidence])) unless block_response
601
+ return block_response
602
+ end
603
+ else
604
+ return false
605
+ end
606
+ end
607
+
608
+ def call_commands
609
+ return false unless @message.platform == :telegram
610
+ command = @message.command
611
+ unless command.nil?
612
+ @message.nlp.process_phrase(@message.command_text)
613
+ block_response = call_block(:commands, Kogno::BlockParams.new([@message.command_text, @message.nlp.entities, @message.nlp.traits]), command)
614
+ if block_response
615
+ return block_response
616
+ else
617
+ default_context = get_default_context_for_command(command)
618
+ unless self.name == default_context.to_s
619
+ logger_call(:commands, command, [], true)
620
+ if self.delegate_to(default_context)
621
+ halt
622
+ return true
623
+ else
624
+ return false
625
+ end
626
+ else
627
+ false
628
+ end
629
+ end
630
+ else
631
+ return false
632
+ end
633
+ end
634
+
635
+ # def call_expressions
636
+ # expression = @message.nlp.expression
637
+
638
+ # unless expression.nil?
639
+ # call_before(:expressions)
640
+ # return true if halted?
641
+
642
+ # block_response = call_block(:expressions, nil, expression)
643
+ # call_after(:expressions)
644
+ # return block_response
645
+ # else
646
+ # return false
647
+ # end
648
+ # end
649
+
650
+
651
+ def self.get_from_typed_postback(msg,user)
652
+ typed_postbacks = user.vars[:typed_postbacks]
653
+ keyword = msg.text.to_payload
654
+ postback = (typed_postbacks[keyword] rescue nil)
655
+ return(self.get_from_payload(postback))
656
+ end
657
+
658
+ def call_typed_postbacks
659
+ # expression = @message.nlp.expression
660
+ typed_postbacks = @user.vars[:typed_postbacks]
661
+ @user.vars.delete(:typed_postbacks)
662
+
663
+ if !@message.text.nil?
664
+ keyword = @message.text.to_payload
665
+ logger_call("typed_postbacks", keyword, [], false)
666
+ postback = (typed_postbacks[keyword] rescue nil)
667
+ unless postback.nil?
668
+ logger.write "KEYWORD FOUND:#{postback.to_yaml}", :green
669
+ @message.overwrite_postback(postback)
670
+ return call_postbacks()
671
+ end
672
+
673
+ end
674
+ return false
675
+ end
676
+
677
+ def call_postbacks(triggers=true)
678
+ postback = @message.payload(true)
679
+ unless postback.nil?
680
+ postback = postback.downcase
681
+ block_params = Kogno::BlockParams.new([postback, @message.params])
682
+ # call_before(:postbacks, block_params) if triggers
683
+ return true if halted?
684
+ block_response = call_block(:postbacks, @message.params, postback)
685
+ block_response = call_any_postback(block_params) unless block_response
686
+ # call_after(:postbacks, block_params) if triggers
687
+ return block_response
688
+ else
689
+ return false
690
+ end
691
+ end
692
+
693
+ # def call_stickers
694
+ # block_response = false
695
+ # unless @message.stickers.empty?
696
+ # call_before(:stickers)
697
+ # return true if halted?
698
+ # @message.stickers.each do |sticker|
699
+ # block_response = call_block(:stickers, nil, sticker)
700
+ # end
701
+ # block_response = call_block(:stickers, nil, "*") unless block_response
702
+ # call_after(:stickers)
703
+ # return block_response
704
+ # else
705
+ # return false
706
+ # end
707
+ # end
708
+
709
+ def call_keywords
710
+ keyword = @message.text.downcase.strip
711
+ unless keyword.empty?
712
+
713
+ # call_before(:keywords)
714
+ return true if halted?
715
+
716
+ block_response = call_block(:keywords, nil ,keyword)
717
+ # call_after(:keywords)
718
+ return block_response
719
+ else
720
+ return false
721
+ end
722
+ end
723
+
724
+ def call_regular_expressions
725
+ text = @message.text.upcase
726
+ regular_expressions = @blocks[:regular_expressions][current_sub_context()]
727
+ unless regular_expressions.nil?
728
+ regular_expressions.each do |regular_expression,block|
729
+ @current_action = {
730
+ name: :regular_expressions,
731
+ value: regular_expression,
732
+ deep: false
733
+ }
734
+ matches = text.scan(regular_expression)
735
+ if matches.count > 0
736
+ logger_call("regular_expression", regular_expression, matches, true)
737
+ block.call matches
738
+ @called_block = {
739
+ name: :regular_expressions,
740
+ value: regular_expression,
741
+ params: nil,
742
+ context: @current,
743
+ deep: false
744
+ }
745
+ return true unless continue?
746
+ end
747
+ end
748
+ end
749
+ deep_regular_expressions = @deep_blocks[:regular_expressions][current_sub_context()]
750
+ unless deep_regular_expressions.nil?
751
+ deep_regular_expressions.each do |regular_expression,block|
752
+ @current_action = {
753
+ name: :regular_expressions,
754
+ value: regular_expression,
755
+ deep: true
756
+ }
757
+ matches = text.scan(regular_expression)
758
+ if matches.count > 0
759
+ logger_call("regular_expression", regular_expression, matches, true, true)
760
+ block.call matches
761
+ @called_block = {
762
+ name: :regular_expressions,
763
+ value: regular_expression,
764
+ param: nil,
765
+ context: @current,
766
+ deep: true
767
+ }
768
+ return true unless continue?
769
+ end
770
+ end
771
+ end
772
+ logger_call("regular_expression", nil, [], false)
773
+ return false
774
+ end
775
+
776
+ def call_location
777
+ unless @message.location.nil?
778
+ # call_before(:location)
779
+ # return true if halted?
780
+
781
+ block_response = call_block(:location, @message.location)
782
+ # call_after(:location)
783
+ return block_response
784
+ else
785
+ return false
786
+ end
787
+ end
788
+
789
+ def call_nlp_entity
790
+ block_response = false
791
+ # call_before(:nlp_entity)
792
+ # return true if halted?
793
+
794
+ if @message.nlp.datetime_range?
795
+ range = @message.nlp.datetime_range
796
+ block_response = call_block(:nlp_datetime_range, Kogno::BlockParams.new([range[:from], range[:to]]))
797
+ end
798
+
799
+ unless block_response
800
+ @message.nlp.entities.each do |entity,values|
801
+ return true if block_response
802
+ block_response = call_block(:nlp_entities, values, entity)
803
+ end
804
+ end
805
+ # call_after(:nlp_entity) if block_response
806
+ return block_response
807
+ end
808
+
809
+ def call_any_number
810
+ if @message.numbers_in_text.count > 0
811
+ block_response = call_block(:any_number, @message.numbers_in_text)
812
+ return block_response
813
+
814
+ else
815
+ return false
816
+ end
817
+ end
818
+
819
+ def call_any_text
820
+ unless @message.text.empty?
821
+ return call_block(:any_text, @message.text)
822
+ else
823
+ return false
824
+ end
825
+ end
826
+
827
+ def call_any_attachment
828
+ unless @message.attachments.nil?
829
+ return call_block(:attachments, @message.attachments)
830
+ else
831
+ return false
832
+ end
833
+ end
834
+
835
+ def call_any_postback(block_params)
836
+ unless @message.payload.nil?
837
+ return call_block(:any_postback, block_params)
838
+ else
839
+ return false
840
+ end
841
+ end
842
+
843
+ def call_any_intent(block_params)
844
+ unless @message.nlp.intent.nil?
845
+ return call_block(:any_intent, block_params)
846
+ else
847
+ return false
848
+ end
849
+ end
850
+
851
+ def call_everything_else
852
+ params = @message.type == :post_comment ? { text: @message.text } : { text: @message.text, payload: @message.payload, entities: @message.nlp.entities, traits: @message.nlp.traits }
853
+ return call_block(:everything_else, params)
854
+ end
855
+
856
+ def call_callback(callback_name)
857
+ callback_block = (@callbacks[callback_name.to_sym][current_sub_context()] rescue nil)
858
+ unless callback_block.nil?
859
+ logger_call("callback", callback_name, [], true)
860
+ callback_block.call
861
+ return true
862
+ else
863
+ logger_call("callback", callback_name, [], false)
864
+ return false
865
+ end
866
+ end
867
+
868
+ def call_class_callback(callback_name)
869
+ conversation_methods = (@@callbacks[Conversation][callback_name].uniq rescue [])
870
+ class_methods = (@@callbacks[self.class][callback_name] rescue [])
871
+ methods = []
872
+ methods += conversation_methods unless conversation_methods.nil?
873
+ methods += class_methods unless class_methods.nil?
874
+ if methods.length > 0
875
+ methods.each do |method|
876
+ if !method.nil? and !halted?
877
+ logger.write "\t#{method}() method found", :red
878
+ self.send(method)
879
+ end
880
+ end
881
+ end
882
+ end
883
+
884
+ def self.class_callbacks
885
+ @@callbacks
886
+ end
887
+
888
+ def call_expiration_time
889
+ unless @@expiration_time[self.class].nil?
890
+ if @user.last_usage > @@expiration_time[self.class]
891
+ delegate_to(:main, true)
892
+ return true
893
+ end
894
+ end
895
+ return false
896
+ end
897
+
898
+ def current_sub_context
899
+ context_tree = @current.to_s.split(Regexp.union(["/","."]))
900
+ context_tree.shift
901
+ return context_tree.join(".")
902
+ end
903
+
904
+ def parent_context_route(context_path=nil)
905
+ context_tree = context_path.nil? ? @current.to_s.split(Regexp.union(["/","."])) : context_path.split(Regexp.union(["/","."]))
906
+ context_tree.pop
907
+ return context_tree.join(".")
908
+ end
909
+
910
+ def root_context_route(context_path=nil)
911
+ context_tree = context_path.nil? ? @current.to_s.split(Regexp.union(["/","."])) : context_path.split(Regexp.union(["/","."]))
912
+ return context_tree.first
913
+ end
914
+
915
+ def current_context_route
916
+ if @current.nil? || @current.empty?
917
+ self.name
918
+ else
919
+ @current
920
+ end
921
+ end
922
+
923
+ def valid_context_route?
924
+ if @current.nil? || @current.empty?
925
+ return true
926
+ elsif @current == self.name
927
+ return true
928
+ elsif @sub_contexts.include?(current_sub_context())
929
+ return true
930
+ else
931
+ return false
932
+ end
933
+ end
934
+
935
+ def nlp_entities_present?(entities)
936
+ (entities & @message.nlp.entities.keys).present?
937
+ end
938
+
939
+
940
+ def run(args={run_blocks_method: true, ignore_everything_else: false})
941
+
942
+ @run_type = :full
943
+
944
+ logger.write "\n\n#{self.nice_current_route} => Starting matching process\n", :bright
945
+
946
+ # before_blocks()
947
+
948
+ call_class_callback(:before_blocks)
949
+
950
+ unless halted?
951
+ blocks() if args[:run_blocks_method] #Loading blocks
952
+
953
+ unless self.valid_context_route?
954
+ logger.write "Error: the context in route #{@current} doesn't exist", :red
955
+ self.exit_context()
956
+ return false
957
+ end
958
+
959
+ call_before(:blocks)
960
+
961
+ if !call_location() and !halted? # Deprecated
962
+ if !call_postbacks() and !halted?
963
+ if !call_typed_postbacks() and !halted?
964
+ if !call_deep_link() and !halted?
965
+ if !@message.empty?
966
+ if !call_commands() and !halted? # Telegram only
967
+ # if !call_stickers() and !halted?
968
+ if !call_any_attachment() and !halted?
969
+ if !call_regular_expressions() and !halted?
970
+ if !call_keywords() and !halted?
971
+ call_before(:nlp)
972
+ @message.nlp.process_phrase_once
973
+ call_after(:nlp, @message.nlp.entities)
974
+ # if !call_expressions() and !halted?
975
+ if !call_intents() and !halted?
976
+ if !call_nlp_entity() and !halted?
977
+ if !call_any_number() and !halted?
978
+ if !call_any_text() and !halted?
979
+ unless args[:ignore_everything_else]
980
+ if !call_everything_else() and !halted?
981
+ logger.write "\tNo match found in #{@current}", :yellow
982
+ call_after(:blocks)
983
+ call_class_callback(:after_blocks)
984
+ return false
985
+ end
986
+ else
987
+ return false
988
+ end
989
+ end
990
+ end
991
+ end
992
+ end
993
+ # end
994
+ end
995
+ end
996
+ end
997
+ # end
998
+ end
999
+ else
1000
+ logger.write "---- THIS MESSAGE IS EMPTY ----", :red
1001
+ end
1002
+ end
1003
+ end
1004
+ end
1005
+ end
1006
+ unless halted?
1007
+ call_after(:blocks)
1008
+ call_class_callback(:after_blocks)
1009
+ end
1010
+ end
1011
+ return @called_block
1012
+ end
1013
+
1014
+ def run_class_callbacks_only
1015
+ call_class_callback(:before_blocks)
1016
+ unless halted?
1017
+ call_class_callback(:after_blocks)
1018
+ end
1019
+ end
1020
+
1021
+ def run_for_text_only(args={run_blocks_method: true, ignore_everything_else: false})
1022
+
1023
+ @run_type = :text_only
1024
+
1025
+ logger.write "\n\n#{self.nice_current_route} => Starting matching process (text only)\n", :bright
1026
+
1027
+ # before_blocks()
1028
+
1029
+ call_class_callback(:before_blocks)
1030
+
1031
+ unless halted?
1032
+ blocks() if args[:run_blocks_method] #Loading blocks
1033
+
1034
+ unless self.valid_context_route?
1035
+ logger.write "Error: the context in route #{@current} doesn't exist", :red
1036
+ self.exit_context()
1037
+ return false
1038
+ end
1039
+
1040
+ call_before(:blocks)
1041
+ if !@message.empty?
1042
+ if !call_regular_expressions() and !halted?
1043
+ if !call_keywords() and !halted?
1044
+ call_before(:nlp)
1045
+ @message.nlp.process_phrase_once
1046
+ call_after(:nlp, @message.nlp.entities)
1047
+ # if !call_expressions() and !halted?
1048
+ if !call_intents() and !halted?
1049
+ if !call_nlp_entity() and !halted?
1050
+ if !call_any_number() and !halted?
1051
+ if !call_any_text() and !halted?
1052
+ unless args[:ignore_everything_else]
1053
+ if !call_everything_else() and !halted?
1054
+ logger.write "#{self.class.name.to_s}.run_for_text_only => nothing found | #{@current}", :yellow
1055
+ call_after(:blocks)
1056
+ call_class_callback(:after_blocks)
1057
+ return false
1058
+ end
1059
+ else
1060
+ return false
1061
+ end
1062
+ end
1063
+ end
1064
+ end
1065
+ end
1066
+ # end
1067
+ end
1068
+ end
1069
+ else
1070
+ logger.write "---- THIS MESSAGE IS EMPTY AND WE DON'T DOING NOTHING WITH IT ----", :red
1071
+ end
1072
+ unless halted?
1073
+ call_after(:blocks)
1074
+ call_class_callback(:after_blocks)
1075
+ end
1076
+ end
1077
+ return @called_block
1078
+ end
1079
+
1080
+ def run_for_chat_activity_only(args={run_blocks_method: true, ignore_everything_else: false})
1081
+
1082
+ @run_type = :text_only
1083
+
1084
+ logger.write "\n\n#{self.nice_current_route} => Starting matching process (chat activity only)\n", :bright
1085
+
1086
+
1087
+ call_class_callback(:before_blocks)
1088
+
1089
+ unless halted?
1090
+ blocks() if args[:run_blocks_method] #Loading blocks
1091
+
1092
+ unless self.valid_context_route?
1093
+ logger.write "Error: the context in route #{@current} doesn't exist", :red
1094
+ self.exit_context()
1095
+ return false
1096
+ end
1097
+
1098
+ call_before(:blocks)
1099
+ if !call_membership_new()
1100
+ if !call_membership_drop()
1101
+ logger.write "#{self.nice_current_route}.run_for_chat_activity_only => nothing found", :yellow
1102
+ call_after(:blocks)
1103
+ call_class_callback(:after_blocks)
1104
+ end
1105
+ return false
1106
+ end
1107
+ end
1108
+
1109
+ return @called_block
1110
+ end
1111
+
1112
+ def run_for_recurring_notification_only()
1113
+
1114
+ @run_type = :text_only
1115
+
1116
+ logger.write "\n\n#{self.nice_current_route} => Starting matching process (recurring notification only)\n", :bright
1117
+
1118
+
1119
+
1120
+ call_class_callback(:before_blocks)
1121
+
1122
+ unless halted?
1123
+ blocks()
1124
+
1125
+ unless self.valid_context_route?
1126
+ logger.write "Error: the context in route #{@current} doesn't exist", :red
1127
+ self.exit_context()
1128
+ return false
1129
+ end
1130
+
1131
+ call_before(:blocks)
1132
+ call_recurring_notifications()
1133
+ if !call_postbacks(false) and !halted?
1134
+ call_class_callback(:after_blocks)
1135
+ return false
1136
+ end
1137
+ end
1138
+
1139
+ call_class_callback(:after_blocks)
1140
+ return @called_block
1141
+
1142
+ end
1143
+
1144
+ def name
1145
+ self.class.to_s.underscore.sub("_context","")
1146
+ end
1147
+
1148
+ def type
1149
+ :context
1150
+ end
1151
+
1152
+ def self.router(route,type=:default)
1153
+ type = :default if type.to_sym == :message
1154
+ context_class = nil
1155
+ sub_context_route = nil
1156
+ if route.to_s.empty?
1157
+ context_class = "#{Kogno::Application.routes.to_h[type.to_s]}_context".classify
1158
+ else
1159
+ route_array = route.to_s.split(Regexp.union(["/","."]))
1160
+ context_name = route_array[0]
1161
+ sub_context_route = route_array[1..].join(".")
1162
+ context_class = "#{context_name}_context".classify
1163
+ end
1164
+
1165
+ valid_class = (eval(context_class) rescue nil)
1166
+ if valid_class.nil?
1167
+ valid = false
1168
+ else
1169
+ valid = true
1170
+ context_class = valid_class
1171
+ end
1172
+ return (
1173
+ {
1174
+ class: context_class,
1175
+ sub_context_route: sub_context_route,
1176
+ valid: valid
1177
+ }
1178
+ )
1179
+ end
1180
+
1181
+ def self.get_from_payload(postback_payload)
1182
+ if !postback_payload.nil? && postback_payload.class == String
1183
+ routes = postback_payload.split(Regexp.union(["/","-",'__']))
1184
+ if routes.count > 1
1185
+ routes.pop
1186
+ return routes.join(".")
1187
+ end
1188
+ end
1189
+
1190
+ return nil
1191
+
1192
+ end
1193
+
1194
+ def self.get(value)
1195
+
1196
+ if !value.nil? && value.class == String
1197
+ $contexts.each do |context|
1198
+ if value.start_with?("#{context}_")
1199
+ return(
1200
+ {
1201
+ context: context,
1202
+ param: value.sub("#{context}_","")
1203
+ }
1204
+ )
1205
+ end
1206
+ end
1207
+ end
1208
+
1209
+ return nil
1210
+
1211
+ end
1212
+
1213
+ def delegate_to(route, args={})
1214
+ args = {ignore_everything_else:true}.merge(args)
1215
+ action_found = false
1216
+ route = translate_route(route)
1217
+ call_callback(:before_delegate)
1218
+ context_route = self.class.router(route, @message.type)
1219
+ context_class = context_route[:class]
1220
+ sub_context_route = context_route[:sub_context_route]
1221
+ unless context_route[:valid]
1222
+ logger.write "ERROR: #{context_class.to_s} doesn't exist.", :red
1223
+ return false
1224
+ end
1225
+ logger.write "Delegating to #{route}", :pink
1226
+ delegated_context = context_class.new(@user,@message,@reply,route)
1227
+ delegated_context.blocks()
1228
+ delegated_context.call_callback(:on_delegate)
1229
+ unless delegated_context.halted?
1230
+ if @run_type == :text_only
1231
+ action_found = delegated_context.run_for_text_only({run_blocks_method: false, ignore_everything_else: args[:ignore_everything_else]})
1232
+ else
1233
+ action_found = delegated_context.run({run_blocks_method: false, ignore_everything_else: args[:ignore_everything_else]})
1234
+ end
1235
+ end
1236
+ # if action_found
1237
+ halt(true)
1238
+ return true
1239
+ # else
1240
+ # return false
1241
+ # end
1242
+ end
1243
+
1244
+ def change_to(route, params={})
1245
+ route = translate_route(route)
1246
+ call_callback(:on_exit) unless sub_context?(route) # will_exit? prevents to not execute :on_exit callback when the the contexts changes to a sub_context
1247
+ context_route = self.class.router(route, @message.type)
1248
+ context_class = context_route[:class]
1249
+
1250
+ unless context_route[:valid]
1251
+ logger.write "ERROR: #{context_class.to_s} doesn't exist.", :red
1252
+ return false
1253
+ else
1254
+ call_class_callback(:before_exit) if self.class != context_class # only will be executed if the contexts is changed to another
1255
+ unless self.halted?
1256
+ @user.set_context(route, params)
1257
+ context = context_class.new(@user,@message,@reply)
1258
+ context.blocks()
1259
+ context.call_callback(:on_enter)
1260
+ end
1261
+ end
1262
+ end
1263
+
1264
+ def ask(answer_route=nil, params={}, &block)
1265
+ unless answer_route.nil?
1266
+ route = (self.parent_context_route != "" and answer_route[0] != "/") ? "../#{answer_route}" : answer_route
1267
+ change_to(route, params)
1268
+ end
1269
+
1270
+ unless block.nil?
1271
+ if answer_route.nil?
1272
+ callback :on_enter do
1273
+ block.call
1274
+ end
1275
+ else
1276
+ block.call
1277
+ end
1278
+ end
1279
+
1280
+ end
1281
+
1282
+ def save_current_context
1283
+ @user.save_current_context
1284
+ end
1285
+
1286
+ def change_to_saved_context
1287
+ saved_context = @user.vars[:saved_context]
1288
+ unless saved_context.nil?
1289
+ @user.delete_previous_context
1290
+ change_to(saved_context[:context], saved_context[:params])
1291
+ else
1292
+ self.exit()
1293
+ end
1294
+ end
1295
+
1296
+ def exit_answer
1297
+ self.exit()
1298
+ end
1299
+
1300
+ def keep
1301
+ @user.set_context(@current)
1302
+ end
1303
+
1304
+ def translate_route(route)
1305
+ route = route.to_s
1306
+ case route
1307
+ when "/", "", nil
1308
+ return ""
1309
+ when :parent, "../"
1310
+ return self.parent_context_route
1311
+ else
1312
+ if route[0] == "/"
1313
+ tmp_route = route.split("/")
1314
+ tmp_route.slice!(0)
1315
+ return tmp_route.join(".")
1316
+ elsif route[0..2] == "../"
1317
+ return "#{self.parent_context_route}.#{route[3..]}"
1318
+ elsif route [0..1] == "./"
1319
+ return "#{self.current_context_route}.#{route[2..]}"
1320
+ elsif !route.index("/").nil?
1321
+ return route.gsub("/",".")
1322
+ else
1323
+ # return "#{@current}.#{route}"
1324
+ return "#{self.current_context_route}.#{route}"
1325
+ end
1326
+ end
1327
+ end
1328
+
1329
+ def exit
1330
+ call_callback(:on_exit)
1331
+ call_class_callback(:before_exit) unless self.class == MainContext
1332
+ unless self.halted?
1333
+ @user.exit_context
1334
+ end
1335
+ end
1336
+
1337
+ def exit_context
1338
+ self.exit
1339
+ end
1340
+
1341
+ def sub_context?(sub_context)
1342
+ (sub_context =~ /#{@current.to_s}/) == 0
1343
+ end
1344
+
1345
+ def set_nlp_reference(**values) # This is used to send context references to Wit https://wit.ai/docs/http/20170307#context_link
1346
+ @user.vars[:nlp_context_ref] = values
1347
+ end
1348
+
1349
+ def destroy_nlp_reference
1350
+ @user.vars.delete(:nlp_context_ref)
1351
+ end
1352
+
1353
+ # Methods to overwrite
1354
+
1355
+ def blocks
1356
+ #overwrite this with your routes
1357
+ end
1358
+
1359
+ def params
1360
+ @user.get_context_params
1361
+ end
1362
+
1363
+ def self.base_template(action_group,action, params,returnable, instance)
1364
+ template = $context_blocks[action_group.to_sym][action.to_sym] rescue nil
1365
+
1366
+ log_string = " Rendering template: #{File.join("bot","templates",action_group.to_s,"#{action.to_s}.erb")}"
1367
+ log_string = "#{log_string} with params #{params.to_h}" unless params.to_h.empty?
1368
+ logger.write log_string, :pink
1369
+
1370
+ unless template.nil?
1371
+ if !returnable
1372
+ template.render(instance, params)
1373
+ else
1374
+ template
1375
+ end
1376
+ else
1377
+ logger.write " ERROR: Template #{action_group}/#{action}.erb not found.", :red
1378
+ end
1379
+ end
1380
+
1381
+ def template(action_group,action, params={})
1382
+ self.class.base_template(action_group, action, params, false, self)
1383
+ end
1384
+
1385
+ # def self.html_template(action_group,action, params={}, instance=nil)
1386
+ # instance = self if instance.nil?
1387
+ # template = $context_html_templates[action_group.to_sym][action.to_sym]
1388
+ # if template.nil?
1389
+ # logger.write "Template bot/templates/#{action_group}/#{action}.rhtml not found.", :red
1390
+ # return ""
1391
+ # else
1392
+ # return template.render(instance, params)
1393
+ # end
1394
+ # end
1395
+
1396
+ # def html_template(route,params,instance)
1397
+ # self.class.html_template(action_group, action, params, false, self)
1398
+ # end
1399
+
1400
+ def root
1401
+ class_name = self.class.to_s
1402
+ last_occurence = class_name.rindex("Context") - 1
1403
+ class_name[0..last_occurence].underscore
1404
+ end
1405
+
1406
+ # Sequences
1407
+
1408
+ def sequences
1409
+
1410
+ #You should overwrite this in the Context
1411
+
1412
+ end
1413
+
1414
+ def start_sequence(stage_route, context_name=nil)
1415
+ stage_array = stage_route.to_s.split("/")
1416
+ if stage_array.count == 2
1417
+ context_name = stage_array[0]
1418
+ stage = stage_array[1]
1419
+ else
1420
+ context_name = self.name
1421
+ stage = stage_route
1422
+ end
1423
+ @user.set_sequence(stage, context_name)
1424
+ end
1425
+
1426
+ def stop_sequence(stage_route)
1427
+ stage_array = stage_route.to_s.split("/")
1428
+ if stage_array.count == 2
1429
+ context_name = stage_array[0]
1430
+ stage = stage_array[1]
1431
+ else
1432
+ context_name = self.name
1433
+ stage = stage_route
1434
+ end
1435
+ @user.exit_sequence(stage, context_name)
1436
+ end
1437
+
1438
+
1439
+ def sequence(stage_name, &block)
1440
+ @current_sequence_stage = stage_name.to_sym
1441
+ sequence = sequence_name()
1442
+ @sequences[sequence] = {} if @sequences[sequence].nil?
1443
+ block.call
1444
+ @current_sequence_stage = nil
1445
+ end
1446
+
1447
+ def past(time_elapsed,&block)
1448
+ return false if @current_sequence_stage.nil?
1449
+ sequence = sequence_name()
1450
+ @sequences[sequence][time_elapsed.to_i] = block
1451
+ end
1452
+
1453
+ def sequence_name
1454
+ return nil if @current_sequence_stage.nil?
1455
+ "#{self.name}.#{@current_sequence_stage}"
1456
+ end
1457
+
1458
+
1459
+ def self.run_sequence(action)
1460
+ logger.write "Running delayed action ##{action.id}|#{action.route} for user ##{action.user.psid}"
1461
+ action.user.get_session_vars
1462
+ if action.user.platform == "messenger"
1463
+ notification = Kogno::Messenger::Notification.new(action.user)
1464
+ elsif action.user.platform == "telegram"
1465
+ notification = Kogno::Telegram::Notification.new(action.user)
1466
+ else
1467
+ notification = Kogno::Notification.new(action.user)
1468
+ end
1469
+ context = self.router(action.route)[:class].new(action.user,{},notification)
1470
+ context.sequences()
1471
+ user = action.user
1472
+ # context.get_sequences[action.route].sort{|x,y| y<=>x}.each do |past,block|
1473
+ if !context.get_sequences[action.route].nil?
1474
+ context.get_sequences[action.route].sort.each do |past,block|
1475
+ if past > action.last_executed
1476
+ execution_time = action.last_hit_at+past
1477
+ if Time.now.utc > execution_time
1478
+ block.call
1479
+ notification.send
1480
+ user.log_response(notification) if Kogno::Application.config.store_log_in_database
1481
+ action.last_executed = past
1482
+ action.save
1483
+ else
1484
+ action.execution_time = execution_time
1485
+ action.save
1486
+ logger.write "It's not time yet. Execution time at #{execution_time} it will be executed at #{execution_time-Time.now.utc} seconds.", :red
1487
+ return false
1488
+ end
1489
+ end
1490
+ end
1491
+ logger.write "End of cycle, there is no more in this sequence so this user will be exited from the stage:#{action.route}"
1492
+ context.stop_sequence
1493
+ return false
1494
+ else
1495
+ logger.write "Not action found for #{action.route}"
1496
+ context.stop_sequence
1497
+ return false
1498
+ end
1499
+ end
1500
+
1501
+ def get_sequences
1502
+ @sequences
1503
+ end
1504
+
1505
+ def memorize_message
1506
+ @user.vars[:memory] = @message.data
1507
+ end
1508
+
1509
+ def remember_message
1510
+ unless @user.vars[:memory].nil?
1511
+ @memorized_message = @user.vars[:memory]
1512
+ @user.vars.delete(:memory)
1513
+ end
1514
+ end
1515
+
1516
+ def handle_message_from_memory(platform=nil)
1517
+ unless @memorized_message.nil?
1518
+ if platform == :telegram
1519
+ message = Kogno::Telegram::Message.new(@memorized_message)
1520
+ else
1521
+ message = Kogno::Messenger::Message.new(@memorized_message)
1522
+ end
1523
+ message.handle_event()
1524
+ @memorized_message = nil
1525
+ end
1526
+ end
1527
+
1528
+ def change_locale(locale)
1529
+ @user.set_locale(locale)
1530
+ @message.set_nlp(locale)
1531
+ I18n.locale = locale
1532
+ end
1533
+
1534
+ def get_default_context_for_command(command)
1535
+ default_context = Kogno::Application.config.routes.commands[command.to_sym]
1536
+ default_context = Kogno::Application.config.routes.default if default_context.nil?
1537
+ return default_context
1538
+ end
1539
+
1540
+ def debugger
1541
+ {
1542
+ blocks: @blocks,
1543
+ deep_blocks: @deep_blocks,
1544
+ callbacks: @callbacks
1545
+ }
1546
+ end
1547
+
1548
+ protected
1549
+
1550
+ def logger_call(action_name, argument, params=[], found=false, deep_action=false)
1551
+ sub_context = current_sub_context
1552
+ sub_context_string = ".#{sub_context}" unless sub_context.empty?
1553
+ argument_string = "(\"#{argument}\")" unless argument.nil?
1554
+ deep_action_string = "deep." if deep_action
1555
+ params = params.map{|param| param.class == String ? "\"#{param}\"" : param.to_s }.join(", ") if params.class == Kogno::BlockParams
1556
+ unless params.nil? || params.empty?
1557
+ logger.write "- #{deep_action_string}#{action_name}#{argument_string} => |#{params}|", found ? :green : :white
1558
+ else
1559
+ logger.write "- #{deep_action_string}#{action_name}#{argument_string}#{deep_action_string}", found ? :green : :white
1560
+ end
1561
+
1562
+ end
1563
+
1564
+ def nice_current_route
1565
+ current_sub_context = self.current_sub_context()
1566
+ unless current_sub_context.empty?
1567
+ "#{self.class.name.to_s}.#{current_sub_context}"
1568
+ else
1569
+ self.class.name.to_s
1570
+ end
1571
+ end
1572
+
1573
+ end