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,197 @@
1
+ module Kogno
2
+ module Telegram
3
+
4
+ class Api
5
+
6
+ require 'uri'
7
+ require 'net/http'
8
+ require 'openssl'
9
+
10
+ class << self
11
+
12
+ def request(body,type,method=:post) # :messages, :messenger_profile
13
+
14
+ url = URI("#{Kogno::Application.config.telegram.api_url}/#{Kogno::Application.config.telegram.token}/#{type}")
15
+
16
+ logger.write "REQUEST TO: #{url}", :pink
17
+ logger.write "SENT: #{JSON.parse(body)}", :blue
18
+
19
+ http = Net::HTTP.new(url.host, url.port)
20
+ http.use_ssl = true
21
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
22
+
23
+ case method
24
+ when :delete
25
+ request = Net::HTTP::Delete.new(url)
26
+ else
27
+ request = Net::HTTP::Post.new(url)
28
+ end
29
+
30
+ request["content-type"] = 'application/json'
31
+
32
+ request.body = body
33
+
34
+ response = http.request(request)
35
+ # logger.debug_json JSON.parse(body), :green
36
+ response_hash = JSON.parse(response.read_body, {:symbolize_names => true})
37
+ logger.write "RESPONSE: #{response_hash}\n", :light_blue
38
+ return(response_hash)
39
+
40
+ end
41
+
42
+ def set_webhook(webhook, drop_pending_updates=true)
43
+ body = {
44
+ url: webhook,
45
+ drop_pending_updates: drop_pending_updates
46
+ }
47
+ logger.write_json body, :blue
48
+ request(body.to_json, "setWebhook")
49
+ end
50
+
51
+ def delete_webhook(drop_pending_updates=true)
52
+ request(
53
+ {
54
+ drop_pending_updates: drop_pending_updates
55
+ }.to_json,
56
+ "deleteWebhook"
57
+ )
58
+ end
59
+
60
+ def send(recipient_id,message, type="sendMessage")
61
+ body = {
62
+ chat_id: recipient_id
63
+ }.merge(message).to_json
64
+
65
+ return(self.request(body,type))
66
+
67
+ end
68
+
69
+ def answer_inline_query(inline_query_id, results, extra_params={})
70
+ body = {
71
+ inline_query_id: inline_query_id,
72
+ results: results
73
+ }.merge(extra_params)
74
+ logger.write_json body, :blue
75
+ return(self.request(body.to_json,"answerInlineQuery"))
76
+
77
+ end
78
+
79
+ def set_commands(commands,scope, lang=nil)
80
+ valid_scopes = self.command_valid_scopes
81
+ raise "Error, invalid scope. Must be one of these: #{valid_scopes.join(",")}" unless valid_scopes.include?(scope)
82
+ body = {
83
+ commands: commands.map{|command, description| {command: command, description: description}},
84
+ scope: {
85
+ type: scope
86
+ }
87
+ }
88
+ body[:language_code] = lang unless lang.nil?
89
+ logger.write "SET COMMAND"
90
+ logger.write_json body, :blue
91
+ return(self.request(body.to_json, "setMyCommands"))
92
+ end
93
+
94
+ def set_all_commands
95
+ Kogno::Application.config.telegram.commands.each do |conf|
96
+ set_commands(conf[:commands], conf[:scope], conf[:lang])
97
+ end
98
+ end
99
+
100
+ def set_scope_commands(scope)
101
+ Kogno::Application.config.telegram.commands.each do |conf|
102
+ set_commands(conf[:commands], conf[:scope], conf[:lang]) if scope == conf[:scope]
103
+ end
104
+ end
105
+
106
+ def delete_commands(scope, lang=nil)
107
+ body = {
108
+ scope: {
109
+ type: scope
110
+ }
111
+ }
112
+ body[:language_code] = lang unless lang.nil?
113
+ logger.write "DELETE COMMAND"
114
+ logger.write_json body, :blue
115
+ return(self.request(body.to_json, "deleteMyCommands"))
116
+ end
117
+
118
+
119
+ def command_valid_scopes
120
+ [:default, :all_private_chats, :all_group_chats, :all_chat_administrators]
121
+ end
122
+
123
+ def answer_callback_query(callback_query_id, params={})
124
+ body = {
125
+ callback_query_id: callback_query_id
126
+ }.merge(params).to_json
127
+ return(self.request(body,"answerCallbackQuery"))
128
+ end
129
+
130
+ def text(text, settings={})
131
+
132
+ result = {
133
+ :text => text
134
+ }
135
+
136
+ result = result.merge(self.add_remove_keyboard_reply_markup) if settings[:remove_keyboard]
137
+
138
+ result
139
+
140
+ end
141
+
142
+ def inline_keyboard_markup(text,replies, update={})
143
+
144
+ result = {
145
+ text: text,
146
+ disable_web_page_preview: true,
147
+ reply_markup: {
148
+ inline_keyboard: replies.first.class == Array ? replies : [replies] # needs to be an array of arrays
149
+ }
150
+ }
151
+
152
+ result = result.merge(update) unless update.nil?
153
+
154
+ result
155
+
156
+ end
157
+
158
+ def add_inline_keyboard_markup(replies)
159
+ {
160
+ reply_markup: {
161
+ inline_keyboard: replies # needs to be an array of arrays
162
+ }
163
+ }
164
+ end
165
+
166
+ def keyboard(text, buttons, one_time_keyboard=true) #type can be: :contact or :location
167
+ {
168
+ text: text,
169
+ disable_web_page_preview: true,
170
+ reply_markup: {
171
+ keyboard: buttons.first.class == Array ? buttons : [buttons], # needs to be an array of arrays
172
+ one_time_keyboard: one_time_keyboard
173
+ }
174
+ }
175
+ end
176
+
177
+ protected
178
+
179
+ def add_remove_keyboard_reply_markup
180
+ {
181
+ reply_markup: {
182
+ remove_keyboard: true
183
+ }
184
+ }
185
+ end
186
+
187
+ def add_reply_markup(settings)
188
+ reply_markup = {}
189
+ reply_markup[:inline_keyboard] = settings[:inline_keyboard]
190
+ end
191
+
192
+ end
193
+
194
+ end
195
+
196
+ end
197
+ end
@@ -0,0 +1,111 @@
1
+ module Kogno
2
+ module Telegram
3
+ class ChatActivity
4
+
5
+ def initialize(data)
6
+ @data = data
7
+ end
8
+
9
+ def type
10
+ :chat_activity
11
+ end
12
+
13
+ def chat
14
+ @data[:chat]
15
+ end
16
+
17
+ def chat_id
18
+ @data[:chat][:id]
19
+ end
20
+
21
+ def chat_title
22
+ @data[:chat][:title]
23
+ end
24
+
25
+ def chat_type
26
+ @data[:chat][:type]
27
+ end
28
+
29
+ def inviter_user_id
30
+ @data[:from][:id]
31
+ end
32
+
33
+ def get_kgn_id
34
+ nil
35
+ end
36
+
37
+ def chat_perms
38
+ perms = @data[:new_chat_member]
39
+ {
40
+ can_be_edited: perms[:can_be_edited],
41
+ can_manage_chat: perms[:can_be_edited],
42
+ can_change_info: perms[:can_be_edited],
43
+ can_post_messages: perms[:can_be_edited],
44
+ can_edit_messages: perms[:can_be_edited],
45
+ can_delete_messages: perms[:can_be_edited],
46
+ can_invite_users: perms[:can_be_edited],
47
+ can_restrict_members: perms[:can_be_edited],
48
+ can_promote_members: perms[:can_be_edited],
49
+ can_manage_voice_chats: perms[:can_be_edited]
50
+ }
51
+ end
52
+
53
+ def chat_membership_status
54
+ if ["member","administrator"].include?(@data[:new_chat_member][:status])
55
+ return true
56
+ elsif ["left","kicked"].include?(@data[:new_chat_member][:status])
57
+ return false
58
+ end
59
+ end
60
+
61
+ def get_context(user,notification, notification_group,chat=nil)
62
+ # Here you can use different contexts by self.post_id
63
+ context_class = Context::router(Kogno::Application.config.routes.chat_activity)[:class]
64
+ context_route = :main
65
+ context = context_class.new(user,self,notification,context_route, notification_group, chat)
66
+ return(context)
67
+ end
68
+
69
+ def handle_event(debug=false)
70
+
71
+ begin
72
+
73
+ user = User.find_or_create_by_psid(self.inviter_user_id, :telegram)
74
+ self.set_nlp(user.locale)
75
+ I18n.locale = user.locale unless user.locale.nil?
76
+ notification = Notification.new(user,self)
77
+ chat = TelegramChatGroup.find_or_create({
78
+ chat_id: self.chat_id,
79
+ title: self.chat_title,
80
+ kind: self.chat_type,
81
+ inviter_user_id: user.id,
82
+ membership: self.chat_membership_status
83
+ })
84
+
85
+ notification = Notification.new(user,self)
86
+ notification_group = Notification.new(chat,self)
87
+
88
+ context = get_context(user,notification, notification_group, chat)
89
+ context.run_for_chat_activity_only
90
+
91
+ notification.send
92
+ notification_group.send
93
+
94
+ if Kogno::Application.config.store_log_in_database
95
+ user.log_message(self, :chat_activity_received)
96
+ user.log_response(notification)
97
+ end
98
+
99
+ rescue StandardError => e
100
+ 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
101
+ logger.write e.message, :red
102
+ logger.write "Error Token: #{error_token}", :red
103
+ logger.write "Backtrace:\n\t#{e.backtrace.join("\n\t")}", :red
104
+ ErrorHandler.notify_by_slack(Kogno::Application.config.app_name,e, error_token) if Kogno::Application.config.error_notifier.slack[:enable] rescue false
105
+ end
106
+
107
+ end
108
+
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,112 @@
1
+ module Kogno
2
+ module Telegram
3
+ class InlineQuery < Kogno::Message
4
+
5
+ attr_accessor :event
6
+
7
+ @overwritten_payload = nil
8
+
9
+ def initialize(data)
10
+ @data = data
11
+ end
12
+
13
+ def type
14
+ :inline_query
15
+ end
16
+
17
+ def platform
18
+ :telegram
19
+ end
20
+
21
+ def page_id
22
+ nil
23
+ end
24
+
25
+ def chat_type
26
+ @data[:chat_type]
27
+ end
28
+
29
+ def id
30
+ return @data[:id]
31
+ end
32
+
33
+ def sender_id
34
+ return @data[:from][:id]
35
+ end
36
+
37
+ def sender_first_name
38
+ return @data[:from][:first_name] rescue ""
39
+ end
40
+
41
+ def sender_last_name
42
+ return @data[:from][:last_name] rescue ""
43
+ end
44
+
45
+ def payload_action
46
+ nil
47
+ end
48
+
49
+ def empty?
50
+ self.text.to_s.empty?
51
+ end
52
+
53
+
54
+ def text
55
+ return (@data[:query].to_s rescue "")
56
+ end
57
+
58
+ def get_context(user,notification)
59
+ # Here you can use different contexts by self.post_id
60
+ context_class = Context::router(Kogno::Application.config.routes.inline_query)[:class]
61
+ context = context_class.new(user,self,notification,nil, nil, user)
62
+ return(context)
63
+ end
64
+
65
+ def handle_event(debug=false)
66
+
67
+ begin
68
+
69
+ user = User.find_or_create_by_psid(self.sender_id, :telegram)
70
+ user.first_name = self.sender_first_name
71
+ user.last_name = self.sender_last_name
72
+ user.save
73
+
74
+ self.set_nlp(user.locale)
75
+ I18n.locale = user.locale unless user.locale.nil?
76
+
77
+ notification = Notification.new(user,self)
78
+
79
+ logger.write "--------------------MESSAGE: #{self.text}--------------------------", :green
80
+
81
+ context = get_context(user,notification)
82
+
83
+ return({post_comment: self, user: user, notification: notification, context: context}) if debug
84
+
85
+ context.run_for_text_only
86
+
87
+ notification.answer_inline_query(self.id, self.sender_id)
88
+
89
+ recipient_psid = notification.get_psid_from_response_log
90
+
91
+ user = User.find_or_create_by_psid(self.sender_id, :telegram)
92
+
93
+ if Kogno::Application.config.store_log_in_database
94
+ user.log_message(self, :inline_query_received)
95
+ user.log_response(notification)
96
+ end
97
+
98
+ logger.write "**********************#{user.context}***********************", :green
99
+
100
+ rescue StandardError => e
101
+ 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
102
+ logger.write e.message, :red
103
+ logger.write "Error Token: #{error_token}", :red
104
+ logger.write "Backtrace:\n\t#{e.backtrace.join("\n\t")}", :red
105
+ ErrorHandler.notify_by_slack(Kogno::Application.config.app_name,e, error_token) if Kogno::Application.config.error_notifier.slack[:enable] rescue false
106
+ end
107
+
108
+ end
109
+
110
+ end
111
+ end
112
+ end