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.
- checksums.yaml +7 -0
- data/bin/kogno +92 -0
- data/lib/boot.rb +9 -0
- data/lib/core/bin_helpers/messenger_ctl.rb +48 -0
- data/lib/core/bin_helpers/scaffolding.rb +203 -0
- data/lib/core/bin_helpers/scheduled_messages_ctl.rb +95 -0
- data/lib/core/bin_helpers/sequences_ctl.rb +95 -0
- data/lib/core/bin_helpers/server_ctl.rb +127 -0
- data/lib/core/bin_helpers/telegram_ctl.rb +48 -0
- data/lib/core/bin_helpers/webhook_ctl.rb +96 -0
- data/lib/core/db.rb +8 -0
- data/lib/core/extensions/array.rb +28 -0
- data/lib/core/extensions/hash.rb +12 -0
- data/lib/core/extensions/logger.rb +70 -0
- data/lib/core/extensions/string.rb +6 -0
- data/lib/core/extensions/wit.rb +60 -0
- data/lib/core/global_methods.rb +67 -0
- data/lib/core/helpers/string.rb +105 -0
- data/lib/core/lib/base_config.rb +17 -0
- data/lib/core/lib/block_params.rb +4 -0
- data/lib/core/lib/context.rb +1573 -0
- data/lib/core/lib/error_handler.rb +36 -0
- data/lib/core/lib/message.rb +182 -0
- data/lib/core/lib/messenger/api.rb +281 -0
- data/lib/core/lib/messenger/facebook_graph.rb +32 -0
- data/lib/core/lib/messenger/message.rb +202 -0
- data/lib/core/lib/messenger/notification.rb +351 -0
- data/lib/core/lib/messenger/post_comment.rb +104 -0
- data/lib/core/lib/messenger/recurring_notification.rb +81 -0
- data/lib/core/lib/nlp.rb +191 -0
- data/lib/core/lib/notification.rb +371 -0
- data/lib/core/lib/spelling.rb +13 -0
- data/lib/core/lib/telegram/api.rb +197 -0
- data/lib/core/lib/telegram/chat_activity.rb +111 -0
- data/lib/core/lib/telegram/inline_query.rb +112 -0
- data/lib/core/lib/telegram/message.rb +327 -0
- data/lib/core/lib/telegram/notification.rb +507 -0
- data/lib/core/lib/whatsapp/api.rb +153 -0
- data/lib/core/lib/whatsapp/message.rb +132 -0
- data/lib/core/lib/whatsapp/notification.rb +206 -0
- data/lib/core/lib/whatsapp/status_message.rb +58 -0
- data/lib/core/loaders/config_files.rb +15 -0
- data/lib/core/models/chat_log.rb +4 -0
- data/lib/core/models/long_payload.rb +25 -0
- data/lib/core/models/matched_message.rb +5 -0
- data/lib/core/models/messenger_recurring_notification.rb +16 -0
- data/lib/core/models/scheduled_message.rb +40 -0
- data/lib/core/models/sequence.rb +29 -0
- data/lib/core/models/telegram_chat_group.rb +26 -0
- data/lib/core/models/user.rb +285 -0
- data/lib/core/web/webhook.rb +198 -0
- data/lib/kogno.rb +130 -0
- data/scaffolding/new_project/Gemfile +3 -0
- data/scaffolding/new_project/application.rb +5 -0
- data/scaffolding/new_project/bot/contexts/main_context.rb +10 -0
- data/scaffolding/new_project/bot/conversation.rb +14 -0
- data/scaffolding/new_project/bot/models/user.rb +3 -0
- data/scaffolding/new_project/config/application.rb +28 -0
- data/scaffolding/new_project/config/database.yml +8 -0
- data/scaffolding/new_project/config/locales/en.yml +4 -0
- data/scaffolding/new_project/config/locales/es.yml +3 -0
- data/scaffolding/new_project/config/nlp.rb +23 -0
- data/scaffolding/new_project/config/platforms/messenger.rb +74 -0
- data/scaffolding/new_project/config/platforms/telegram.rb +45 -0
- data/scaffolding/new_project/config/platforms/whatsapp.rb +13 -0
- data/scaffolding/new_project/web/routes.rb +10 -0
- metadata +220 -0
| @@ -0,0 +1,132 @@ | |
| 1 | 
            +
            module Kogno
         | 
| 2 | 
            +
            module WhatsApp
         | 
| 3 | 
            +
            class Message  < Kogno::Message
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              @overwritten_payload = nil
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              def initialize(data, type=nil)
         | 
| 8 | 
            +
                @data = data
         | 
| 9 | 
            +
                @type = type
         | 
| 10 | 
            +
              end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              def type
         | 
| 13 | 
            +
                @type
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              def platform
         | 
| 17 | 
            +
                :whatsapp
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
              def metadata
         | 
| 21 | 
            +
                @data[:metadata]
         | 
| 22 | 
            +
              end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              def sender_id
         | 
| 25 | 
            +
                return @data[:contacts][0][:wa_id]
         | 
| 26 | 
            +
              end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
              def sender_name
         | 
| 29 | 
            +
                return @data[:contacts][0][:profile][:name] rescue ""
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              def attachments
         | 
| 33 | 
            +
                message = @data[:messages][0]
         | 
| 34 | 
            +
                a = nil
         | 
| 35 | 
            +
                a = message[:audio]
         | 
| 36 | 
            +
                a = message[:document] if a.nil?
         | 
| 37 | 
            +
                a = message[:image] if a.nil?
         | 
| 38 | 
            +
                a = message[:video] if a.nil?
         | 
| 39 | 
            +
                return a
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
             | 
| 43 | 
            +
              def raw_message
         | 
| 44 | 
            +
                t = @data[:messages][0][:text][:body] rescue nil
         | 
| 45 | 
            +
                unless t.nil?
         | 
| 46 | 
            +
                  return({
         | 
| 47 | 
            +
                    :type => :text,
         | 
| 48 | 
            +
                    :value => t
         | 
| 49 | 
            +
                  })
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
              end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
              def raw_payload
         | 
| 54 | 
            +
                payload = @data[:messages][0][:button][:payload] rescue nil
         | 
| 55 | 
            +
                payload = (@data[:messages][0][:interactive][:button_reply][:id] rescue nil) if payload.nil?
         | 
| 56 | 
            +
                payload = (@data[:messages][0][:interactive][:list_reply][:id] rescue nil) if payload.nil?
         | 
| 57 | 
            +
                payload = @overwritten_payload if payload.nil?
         | 
| 58 | 
            +
                return(payload)
         | 
| 59 | 
            +
              end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
              def text
         | 
| 62 | 
            +
                return self.raw_message[:value].to_s rescue ""
         | 
| 63 | 
            +
              end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
             | 
| 66 | 
            +
              def handle_event(debug=false)
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                begin
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                  user = User.find_or_create_by_psid(self.sender_id, :whatsapp)
         | 
| 71 | 
            +
                  user.get_session_vars
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                  self.set_nlp(user.locale)
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                  I18n.locale = user.locale unless user.locale.nil?
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                  self.set_nlp(I18n.locale)
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                  unless user.vars[:nlp_context_ref].nil?
         | 
| 80 | 
            +
                    self.nlp.set_context_reference(user.vars[:nlp_context_ref])
         | 
| 81 | 
            +
                    user.vars.delete(:nlp_context_ref) # context references will only be used once
         | 
| 82 | 
            +
                  end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                  notification = Notification.new(user,self)
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                  self.log_message_info(user)
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                  context = get_context(user,self,notification)
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                  return({msg: self, user: user, notification: notification, context: context}) if debug
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                  unless empty_thread_from_ad?
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                    called_action = context.run      
         | 
| 95 | 
            +
                    if Kogno::Application.config.store_log_in_database   
         | 
| 96 | 
            +
                      message_log_id = user.log_message(self).id
         | 
| 97 | 
            +
                    else
         | 
| 98 | 
            +
                      message_chat_log_id = 0
         | 
| 99 | 
            +
                    end     
         | 
| 100 | 
            +
                         
         | 
| 101 | 
            +
                    notification.send
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                    response_log_id = 0
         | 
| 104 | 
            +
                    if Kogno::Application.config.store_log_in_database
         | 
| 105 | 
            +
                      response_log = user.log_response(notification)          
         | 
| 106 | 
            +
                      response_log_id = response_log.id unless response_log.nil?        
         | 
| 107 | 
            +
                    end  
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                    # user.set_last_usage
         | 
| 110 | 
            +
                    user.save_session_vars
         | 
| 111 | 
            +
                    context.handle_message_from_memory
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                  else
         | 
| 114 | 
            +
                    context.run_class_callbacks_only
         | 
| 115 | 
            +
                    user.save_session_vars
         | 
| 116 | 
            +
                    user.log_message(self) if Kogno::Application.config.store_log_in_database         
         | 
| 117 | 
            +
                  end
         | 
| 118 | 
            +
                  logger.write "- Current user context: #{user.context}", :blue unless user.context.nil?
         | 
| 119 | 
            +
                 
         | 
| 120 | 
            +
                rescue StandardError => e
         | 
| 121 | 
            +
                  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      
         | 
| 122 | 
            +
                  logger.write e.message, :red
         | 
| 123 | 
            +
                  logger.write "Error Token: #{error_token}", :red
         | 
| 124 | 
            +
                  logger.write "Backtrace:\n\t#{e.backtrace.join("\n\t")}", :red
         | 
| 125 | 
            +
                  ErrorHandler.notify_by_slack(Kogno::Application.config.app_name,e, error_token) if Kogno::Application.config.error_notifier.slack[:enable] rescue false
         | 
| 126 | 
            +
                end
         | 
| 127 | 
            +
             | 
| 128 | 
            +
              end
         | 
| 129 | 
            +
             | 
| 130 | 
            +
            end
         | 
| 131 | 
            +
            end
         | 
| 132 | 
            +
            end
         | 
| @@ -0,0 +1,206 @@ | |
| 1 | 
            +
            module Kogno
         | 
| 2 | 
            +
            module WhatsApp
         | 
| 3 | 
            +
            class Notification < Kogno::Notification
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              def send(recipient_id=nil,delete=true)    
         | 
| 6 | 
            +
                recipient_id = @recipient.psid if recipient_id.nil?
         | 
| 7 | 
            +
                page_id = @recipient.page_id if page_id.nil?
         | 
| 8 | 
            +
                messages = @before_messages+@messages+@after_messages
         | 
| 9 | 
            +
                @message_log = messages
         | 
| 10 | 
            +
                messages.each do |message|
         | 
| 11 | 
            +
                  if message[:type].to_sym == :action
         | 
| 12 | 
            +
                    sleep(message[:value][:duration]) if message[:value][:action].to_sym == :typing_on
         | 
| 13 | 
            +
                  else
         | 
| 14 | 
            +
                    message = self.replace_place_holders(message[:value])
         | 
| 15 | 
            +
                    @response_log << Api::send(recipient_id,message)
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
                end    
         | 
| 18 | 
            +
                self.delete_messages() if delete
         | 
| 19 | 
            +
                @recipient.mark_last_message_as_unread unless @recipient.nil?
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              def text(text, extra_params={})
         | 
| 23 | 
            +
                extra_params = {preview_url: false}.merge(extra_params)  
         | 
| 24 | 
            +
                params = {
         | 
| 25 | 
            +
                  type: :text,
         | 
| 26 | 
            +
                  text: {
         | 
| 27 | 
            +
                    body: text
         | 
| 28 | 
            +
                  }.merge(extra_params)
         | 
| 29 | 
            +
                }  
         | 
| 30 | 
            +
                self.push_message(params, :message)
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
             | 
| 34 | 
            +
              def typing_on(duration)
         | 
| 35 | 
            +
                self.push_message({:action => :typing_on, :duration => duration}, :action)
         | 
| 36 | 
            +
              end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
              def whatsapp_template(name, components=[], lang="en_US")
         | 
| 39 | 
            +
                self.push_message(Api::template_message(name, components, lang), :message)
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
              def button(text, replies, extra_settings={})
         | 
| 43 | 
            +
                replies = [replies] if replies.class == Hash
         | 
| 44 | 
            +
                settings = {typed_postbacks: Kogno::Application.config.typed_postbacks}.merge(extra_settings) # defaults
         | 
| 45 | 
            +
                replies = replies.replace_keys({payload: :id})
         | 
| 46 | 
            +
                buttons = replies.map do |reply|
         | 
| 47 | 
            +
                  {
         | 
| 48 | 
            +
                    type: :reply,
         | 
| 49 | 
            +
                    reply: reply
         | 
| 50 | 
            +
                  }
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                set_typed_postbacks(replies.map{|button|
         | 
| 54 | 
            +
                  [button[:title].to_payload, button[:id]] unless button[:title].nil?
         | 
| 55 | 
            +
                }.compact.to_h) if settings[:typed_postbacks]
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                self.push_message(Api::interactive_buttons(text, buttons), :message)
         | 
| 58 | 
            +
              end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
              def quick_reply(text, replies, extra_settings={})
         | 
| 61 | 
            +
                self.button(text, replies, extra_settings)
         | 
| 62 | 
            +
              end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
              def list(params,header={},footer={})
         | 
| 65 | 
            +
                params[:sections] = params[:sections].map{|s| 
         | 
| 66 | 
            +
                  {
         | 
| 67 | 
            +
                    title: s[:title],
         | 
| 68 | 
            +
                    rows: s[:rows].replace_keys({payload: :id})
         | 
| 69 | 
            +
                  }
         | 
| 70 | 
            +
                }
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                params[:header] = header unless header.empty?
         | 
| 73 | 
            +
                params[:footer] = footer unless footer.empty?
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                self.push_message(Api::interactive_list(params), :message)
         | 
| 76 | 
            +
              end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
              def url(params)
         | 
| 79 | 
            +
                self.raw(
         | 
| 80 | 
            +
                    {  
         | 
| 81 | 
            +
                      type: :image,
         | 
| 82 | 
            +
                      image: {          
         | 
| 83 | 
            +
                        link: params[:image],
         | 
| 84 | 
            +
                        caption: "#{params[:title]}\n#{params[:sub_title]}\n#{params[:url]}"
         | 
| 85 | 
            +
                      }
         | 
| 86 | 
            +
                    }
         | 
| 87 | 
            +
                  )
         | 
| 88 | 
            +
              end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
              def location(params)
         | 
| 91 | 
            +
                self.push_message(Api::location(params), :message)
         | 
| 92 | 
            +
              end
         | 
| 93 | 
            +
              
         | 
| 94 | 
            +
              def image(params)
         | 
| 95 | 
            +
                # params = params.replace_keys({url: :link})
         | 
| 96 | 
            +
                if params[:buttons].nil?
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                  self.raw(
         | 
| 99 | 
            +
                    {  
         | 
| 100 | 
            +
                      type: :image,
         | 
| 101 | 
            +
                      image: {
         | 
| 102 | 
            +
                        link: params[:url],
         | 
| 103 | 
            +
                        caption: params[:caption]
         | 
| 104 | 
            +
                      }
         | 
| 105 | 
            +
                    }
         | 
| 106 | 
            +
                  )
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                else
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                  buttons = params[:buttons]
         | 
| 111 | 
            +
                  buttons = [buttons] if buttons.class == Hash
         | 
| 112 | 
            +
                  buttons = buttons.replace_keys({payload: :id})
         | 
| 113 | 
            +
                  replies = buttons.map do |reply|
         | 
| 114 | 
            +
                    {
         | 
| 115 | 
            +
                      type: :reply,
         | 
| 116 | 
            +
                      reply: reply
         | 
| 117 | 
            +
                    }
         | 
| 118 | 
            +
                  end
         | 
| 119 | 
            +
                  self.raw(
         | 
| 120 | 
            +
                    {  
         | 
| 121 | 
            +
                      type: :interactive,
         | 
| 122 | 
            +
                      interactive: {
         | 
| 123 | 
            +
                        type: :button,
         | 
| 124 | 
            +
                        header: {
         | 
| 125 | 
            +
                          type: :image,
         | 
| 126 | 
            +
                          image: {
         | 
| 127 | 
            +
                            link: params[:url]
         | 
| 128 | 
            +
                          }
         | 
| 129 | 
            +
                        },
         | 
| 130 | 
            +
                        body:{
         | 
| 131 | 
            +
                          text: params[:caption]
         | 
| 132 | 
            +
                        },
         | 
| 133 | 
            +
                        action:{
         | 
| 134 | 
            +
                          buttons: replies
         | 
| 135 | 
            +
                        }
         | 
| 136 | 
            +
                      }
         | 
| 137 | 
            +
                    }
         | 
| 138 | 
            +
                  )
         | 
| 139 | 
            +
             | 
| 140 | 
            +
                end
         | 
| 141 | 
            +
             | 
| 142 | 
            +
              end
         | 
| 143 | 
            +
             | 
| 144 | 
            +
              def video(params)
         | 
| 145 | 
            +
             | 
| 146 | 
            +
                if params[:buttons].nil?
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                  self.raw(
         | 
| 149 | 
            +
                    {  
         | 
| 150 | 
            +
                      type: :video,
         | 
| 151 | 
            +
                      video: {
         | 
| 152 | 
            +
                        link: params[:url],
         | 
| 153 | 
            +
                        caption: params[:caption]
         | 
| 154 | 
            +
                      }
         | 
| 155 | 
            +
                    }
         | 
| 156 | 
            +
                  )
         | 
| 157 | 
            +
             | 
| 158 | 
            +
                else
         | 
| 159 | 
            +
             | 
| 160 | 
            +
                  buttons = params[:buttons]
         | 
| 161 | 
            +
                  buttons = [buttons] if buttons.class == Hash
         | 
| 162 | 
            +
                  buttons = buttons.replace_keys({payload: :id})
         | 
| 163 | 
            +
                  replies = buttons.map do |reply|
         | 
| 164 | 
            +
                    {
         | 
| 165 | 
            +
                      type: :reply,
         | 
| 166 | 
            +
                      reply: reply
         | 
| 167 | 
            +
                    }
         | 
| 168 | 
            +
                  end
         | 
| 169 | 
            +
                  self.raw(
         | 
| 170 | 
            +
                    {  
         | 
| 171 | 
            +
                      type: :interactive,
         | 
| 172 | 
            +
                      interactive: {
         | 
| 173 | 
            +
                        type: :button,
         | 
| 174 | 
            +
                        header: {
         | 
| 175 | 
            +
                          type: :video,
         | 
| 176 | 
            +
                          video: {
         | 
| 177 | 
            +
                            link: params[:url]
         | 
| 178 | 
            +
                          }
         | 
| 179 | 
            +
                        },
         | 
| 180 | 
            +
                        body:{
         | 
| 181 | 
            +
                          text: params[:caption]
         | 
| 182 | 
            +
                        },
         | 
| 183 | 
            +
                        action:{
         | 
| 184 | 
            +
                          buttons: replies
         | 
| 185 | 
            +
                        }
         | 
| 186 | 
            +
                      }
         | 
| 187 | 
            +
                    }
         | 
| 188 | 
            +
                  )
         | 
| 189 | 
            +
             | 
| 190 | 
            +
                end
         | 
| 191 | 
            +
             | 
| 192 | 
            +
              end  
         | 
| 193 | 
            +
             | 
| 194 | 
            +
              def document(params)
         | 
| 195 | 
            +
                self.push_message(Api::media(:document, params), :message)
         | 
| 196 | 
            +
              end
         | 
| 197 | 
            +
             | 
| 198 | 
            +
              def contact(params)
         | 
| 199 | 
            +
                self.push_message(Api::contacts(params), :message)
         | 
| 200 | 
            +
              end
         | 
| 201 | 
            +
             | 
| 202 | 
            +
             | 
| 203 | 
            +
             | 
| 204 | 
            +
            end
         | 
| 205 | 
            +
            end
         | 
| 206 | 
            +
            end
         | 
| @@ -0,0 +1,58 @@ | |
| 1 | 
            +
            module Kogno
         | 
| 2 | 
            +
            module WhatsApp
         | 
| 3 | 
            +
            class StatusMessage  < Kogno::Message
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              @overwritten_payload = nil
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              def initialize(data, type=nil)
         | 
| 8 | 
            +
                @data = data
         | 
| 9 | 
            +
                @type = type
         | 
| 10 | 
            +
              end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              def type
         | 
| 13 | 
            +
                @type
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              def platform
         | 
| 17 | 
            +
                :whatsapp
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
              def status_raw
         | 
| 21 | 
            +
                @data[:statuses][0]
         | 
| 22 | 
            +
              end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              def status
         | 
| 25 | 
            +
                self.status_raw[:status]
         | 
| 26 | 
            +
              end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
              def metadata
         | 
| 29 | 
            +
                @data[:metadata]
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              def sender_id
         | 
| 33 | 
            +
                return self.status_raw[:recipient_id]
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
             | 
| 37 | 
            +
              def handle_event(debug=false)
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                begin
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                  user = User.find_or_create_by_psid(self.sender_id, :whatsapp)
         | 
| 42 | 
            +
                  if self.status == "read"
         | 
| 43 | 
            +
                    user.mark_last_message_as_read
         | 
| 44 | 
            +
                  end
         | 
| 45 | 
            +
                 
         | 
| 46 | 
            +
                rescue StandardError => e
         | 
| 47 | 
            +
                  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      
         | 
| 48 | 
            +
                  logger.write e.message, :red
         | 
| 49 | 
            +
                  logger.write "Error Token: #{error_token}", :red
         | 
| 50 | 
            +
                  logger.write "Backtrace:\n\t#{e.backtrace.join("\n\t")}", :red
         | 
| 51 | 
            +
                  ErrorHandler.notify_by_slack(Kogno::Application.config.app_name,e, error_token) if Kogno::Application.config.error_notifier.slack[:enable] rescue false
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
              end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
            end
         | 
| 57 | 
            +
            end
         | 
| 58 | 
            +
            end
         | 
| @@ -0,0 +1,15 @@ | |
| 1 | 
            +
            module Kogno
         | 
| 2 | 
            +
             | 
| 3 | 
            +
              Dir[File.join(Application.project_path,'config','*.rb')].each do |required_file|
         | 
| 4 | 
            +
                require required_file
         | 
| 5 | 
            +
              end
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              Dir[File.join(Application.project_path,'config','platforms','*.rb')].each do |required_file|
         | 
| 8 | 
            +
                require required_file
         | 
| 9 | 
            +
              end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              Dir[File.join(Application.project_path,'config','initializers','*.rb')].each do |required_file|
         | 
| 12 | 
            +
                require required_file
         | 
| 13 | 
            +
              end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            end
         | 
| @@ -0,0 +1,25 @@ | |
| 1 | 
            +
            class LongPayload < ActiveRecord::Base
         | 
| 2 | 
            +
                self.table_name = "kogno_long_payloads"
         | 
| 3 | 
            +
                before_create :generate_token    
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                def generate_token
         | 
| 6 | 
            +
                  self.token = Digest::MD5.hexdigest "#{Time.now}#{rand(100000)}"
         | 
| 7 | 
            +
                end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                def self.set(data)
         | 
| 10 | 
            +
                  data = data.split(":",2)
         | 
| 11 | 
            +
                  payload = data[0]
         | 
| 12 | 
            +
                  params = data[1]
         | 
| 13 | 
            +
                  payload_param = create(payload: payload, params: params)
         | 
| 14 | 
            +
                  return payload_param.token
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                def self.get(token)
         | 
| 18 | 
            +
                  payload = find_by_token(token)
         | 
| 19 | 
            +
                  unless payload.nil?
         | 
| 20 | 
            +
                    return ("#{payload.payload}:#{payload.params}")
         | 
| 21 | 
            +
                  else
         | 
| 22 | 
            +
                    return {}
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
            end
         | 
| @@ -0,0 +1,16 @@ | |
| 1 | 
            +
            class MessengerRecurringNotification < ActiveRecord::Base
         | 
| 2 | 
            +
              self.table_name = "kogno_messenger_recurring_notifications"
         | 
| 3 | 
            +
              belongs_to :user
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              def data
         | 
| 6 | 
            +
                {
         | 
| 7 | 
            +
                  token: self.token,
         | 
| 8 | 
            +
                  frecuency: self.frecuency,
         | 
| 9 | 
            +
                  expires_at: self.expires_at,
         | 
| 10 | 
            +
                  token_status: self.token_status,
         | 
| 11 | 
            +
                  timezone: self.timezone,
         | 
| 12 | 
            +
                  status: self.active ? :active : :stopped  
         | 
| 13 | 
            +
                }
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            end
         | 
| @@ -0,0 +1,40 @@ | |
| 1 | 
            +
            class ScheduledMessage < ActiveRecord::Base
         | 
| 2 | 
            +
              self.table_name = "kogno_scheduled_messages"
         | 
| 3 | 
            +
              belongs_to :user
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              def self.process_all(sleep=60)
         | 
| 6 | 
            +
                loop do
         | 
| 7 | 
            +
                  scheduled_messages = where("'#{Time.now.utc}' > send_at").includes(:user).order(:send_at)
         | 
| 8 | 
            +
                  scheduled_messages.each do |scheduled_message|
         | 
| 9 | 
            +
                    if scheduled_message.user.last_usage > Kogno::Application.config.sequences.time_elapsed_after_last_usage
         | 
| 10 | 
            +
                      scheduled_message.execute
         | 
| 11 | 
            +
                    else
         | 
| 12 | 
            +
                      logger.write "User ID #{scheduled_message.user.psid} wrote us recently, let's wait #{Kogno::Application.config.sequences.time_elapsed_after_last_usage - scheduled_message.user.last_usage} seconds before bother him.", :green
         | 
| 13 | 
            +
                    end
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
                  scheduled_messages.destroy_all
         | 
| 16 | 
            +
                  sleep(sleep)
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
              def execute
         | 
| 21 | 
            +
                if self.user.platform == "messenger"
         | 
| 22 | 
            +
                  notification = Kogno::Messenger::Notification.new(self.user)
         | 
| 23 | 
            +
                elsif self.user.platform == "telegram"
         | 
| 24 | 
            +
                  notification = Kogno::Telegram::Notification.new(self.user)
         | 
| 25 | 
            +
                elsif self.user.platform == "whatsapp"
         | 
| 26 | 
            +
                  notification = Kogno::WhatsApp::Notification.new(self.user)
         | 
| 27 | 
            +
                else
         | 
| 28 | 
            +
                  logger.write "Platform '#{self.user.platform} not supported. User ID: #{self.user.id}"
         | 
| 29 | 
            +
                  notification = nil
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
                unless notification.nil?
         | 
| 32 | 
            +
                  notification.import_messages(self.messages)
         | 
| 33 | 
            +
                  logger.write "Sending scheduled messages to #{self.user.psid}..", :green
         | 
| 34 | 
            +
                  logger.write_json JSON.parse(self.messages), :bright
         | 
| 35 | 
            +
                  notification.send
         | 
| 36 | 
            +
                  self.user.log_response(notification,true) if Kogno::Application.config.store_log_in_database
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
              end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
            end
         | 
| @@ -0,0 +1,29 @@ | |
| 1 | 
            +
            class Sequence < ActiveRecord::Base
         | 
| 2 | 
            +
              self.table_name = "kogno_sequences"
         | 
| 3 | 
            +
              belongs_to :user
         | 
| 4 | 
            +
              before_create :set_last_hit_at
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              def set_last_hit_at
         | 
| 7 | 
            +
                self.last_hit_at = Time.now.utc
         | 
| 8 | 
            +
              end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              def route
         | 
| 11 | 
            +
                "#{context}.#{stage}"
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
             | 
| 15 | 
            +
              def self.process_all(sleep=60)
         | 
| 16 | 
            +
                loop do
         | 
| 17 | 
            +
                  actions = where("'#{Time.now.utc}' > execution_time or execution_time is null").includes(:user).order(:execution_time)
         | 
| 18 | 
            +
                  actions.each do |action|
         | 
| 19 | 
            +
                    if action.user.last_usage > Kogno::Application.config.sequences.time_elapsed_after_last_usage
         | 
| 20 | 
            +
                      Kogno::Context.run_sequence(action)
         | 
| 21 | 
            +
                    else
         | 
| 22 | 
            +
                      logger.write "User ID #{action.user.psid} wrote us recently, let's wait #{Kogno::Application.config.sequences.time_elapsed_after_last_usage - action.user.last_usage} seconds before bother him.", :green
         | 
| 23 | 
            +
                    end
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
                  sleep(sleep)
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            end
         | 
| @@ -0,0 +1,26 @@ | |
| 1 | 
            +
            class TelegramChatGroup < ActiveRecord::Base
         | 
| 2 | 
            +
              self.table_name = "kogno_telegram_chat_groups"
         | 
| 3 | 
            +
              belongs_to :user, foreign_key: :inviter_user_id
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              def type
         | 
| 6 | 
            +
                self.kind.to_sym
         | 
| 7 | 
            +
              end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              def self.find_or_create(args)
         | 
| 10 | 
            +
                chat = find_by_chat_id(args[:chat_id])
         | 
| 11 | 
            +
                if chat.nil?
         | 
| 12 | 
            +
                  chat = create(args)
         | 
| 13 | 
            +
                else
         | 
| 14 | 
            +
                  chat.update(args)
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
                return chat
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
              def notification
         | 
| 20 | 
            +
                if @notification.nil?
         | 
| 21 | 
            +
                  @notification =  Kogno::Telegram::Notification.new(self)
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
                @notification
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
              
         | 
| 26 | 
            +
            end
         |