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,36 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'net/http'
|
3
|
+
require 'openssl'
|
4
|
+
|
5
|
+
module Kogno
|
6
|
+
class ErrorHandler
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def notify_by_slack(username, error, token)
|
10
|
+
|
11
|
+
error_message = %{```
|
12
|
+
Error Message: #{error.message[0..256]}
|
13
|
+
Error Token: #{token}
|
14
|
+
Backtrace:#{error.backtrace.join("\n\t")[0..1024]}
|
15
|
+
```}
|
16
|
+
|
17
|
+
url = URI(Kogno::Application.config.error_notifier.slack[:webhook])
|
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
|
+
request = Net::HTTP::Post.new(url)
|
24
|
+
request["content-type"] = 'application/json'
|
25
|
+
request.body = {
|
26
|
+
username: username,
|
27
|
+
text: error_message
|
28
|
+
}.to_json
|
29
|
+
|
30
|
+
response = http.request(request)
|
31
|
+
logger.write "-- This error was notified in Slack ---", :red
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
module Kogno
|
2
|
+
class Message
|
3
|
+
|
4
|
+
attr_accessor :event
|
5
|
+
|
6
|
+
@overwritten_payload = nil
|
7
|
+
|
8
|
+
def type
|
9
|
+
:message
|
10
|
+
end
|
11
|
+
|
12
|
+
def sender_id
|
13
|
+
0
|
14
|
+
end
|
15
|
+
|
16
|
+
def page_id
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def stickers
|
21
|
+
[]
|
22
|
+
end
|
23
|
+
|
24
|
+
def attachments
|
25
|
+
[]
|
26
|
+
end
|
27
|
+
|
28
|
+
def referral(field=nil)
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def deep_link_data
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
|
36
|
+
def set_nlp(lang)
|
37
|
+
@nlp = Nlp.new(self.text,lang)
|
38
|
+
end
|
39
|
+
|
40
|
+
def nlp
|
41
|
+
@nlp
|
42
|
+
end
|
43
|
+
|
44
|
+
def deep_link_param
|
45
|
+
@data[:deep_link_param] rescue nil
|
46
|
+
end
|
47
|
+
|
48
|
+
def webhook_data
|
49
|
+
@data
|
50
|
+
end
|
51
|
+
|
52
|
+
def overwrite_postback(payload)
|
53
|
+
@overwritten_payload = payload
|
54
|
+
end
|
55
|
+
|
56
|
+
def payload(action_only=false)
|
57
|
+
if action_only
|
58
|
+
pl = self.raw_payload.split(":",2)[0] rescue nil
|
59
|
+
if pl.nil?
|
60
|
+
return pl
|
61
|
+
else
|
62
|
+
return pl.split(Regexp.union(["/","-"])).last
|
63
|
+
end
|
64
|
+
else
|
65
|
+
self.raw_payload.split(":",2)[0] rescue nil
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def payload_action
|
70
|
+
self.payload(true)
|
71
|
+
end
|
72
|
+
|
73
|
+
def postback_payload
|
74
|
+
self.payload(true)
|
75
|
+
end
|
76
|
+
|
77
|
+
def postback_params
|
78
|
+
self.params
|
79
|
+
end
|
80
|
+
|
81
|
+
# def event
|
82
|
+
# JSON.parse(self.body,{:symbolize_names => true})
|
83
|
+
# end
|
84
|
+
|
85
|
+
def empty?
|
86
|
+
self.raw_payload.nil? && self.raw_message.nil? && self.attachments.nil?
|
87
|
+
end
|
88
|
+
|
89
|
+
def empty_thread_from_ad?
|
90
|
+
if self.empty?
|
91
|
+
unless self.referral.nil?
|
92
|
+
if self.referral(:source) == "ADS" and self.referral(:type) == "OPEN_THREAD"
|
93
|
+
return true
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
return false
|
99
|
+
end
|
100
|
+
|
101
|
+
def params
|
102
|
+
raw_params = self.raw_payload.to_s.split(":",2)[1].to_s
|
103
|
+
if raw_params.empty?
|
104
|
+
return({})
|
105
|
+
else
|
106
|
+
p = (JSON.parse(raw_params, {:symbolize_names => true}) rescue nil)
|
107
|
+
p = raw_params if p.nil?
|
108
|
+
return(p)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def raw_payload
|
113
|
+
# In order to implement in another platform, this function must exist
|
114
|
+
nil
|
115
|
+
end
|
116
|
+
|
117
|
+
def raw_message
|
118
|
+
# In order to implement in another platform, this function must exist
|
119
|
+
{}
|
120
|
+
end
|
121
|
+
|
122
|
+
def text
|
123
|
+
# In order to implement in another platform, this function must exist
|
124
|
+
""
|
125
|
+
end
|
126
|
+
|
127
|
+
def numbers_in_text
|
128
|
+
# self.text.scan(/\d+/).map{|n| n.to_i}
|
129
|
+
self.text.scan(/(\d+(?:\.\d+)?)/).map{|n| n.first}
|
130
|
+
end
|
131
|
+
|
132
|
+
def location
|
133
|
+
if (self.raw_message[:type] == :location rescue false)
|
134
|
+
return self.raw_message[:value]
|
135
|
+
else
|
136
|
+
return nil
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def get_context(user,msg,notification)
|
141
|
+
|
142
|
+
# Context::call_typed_postbacks(msg,user) if msg.payload.nil? && !user.vars[:typed_postbacks].nil?
|
143
|
+
# user.vars.delete(:typed_postbacks)
|
144
|
+
|
145
|
+
if !(context_from_postback = Context.get_from_payload(msg.payload)).nil?
|
146
|
+
context_route = context_from_postback
|
147
|
+
elsif !(context_from_typed_postback = Context.get_from_typed_postback(msg,user)).nil?
|
148
|
+
context_route = context_from_typed_postback
|
149
|
+
else
|
150
|
+
context_route = user.context
|
151
|
+
end
|
152
|
+
|
153
|
+
context_class = Context.router(context_route, msg.type)[:class]
|
154
|
+
if context_class.nil?
|
155
|
+
user.exit_context
|
156
|
+
context_class = Context.router(Kogno::Application.config.routes.default)[:class]
|
157
|
+
end
|
158
|
+
context = context_class.new(user,msg,notification,context_route)
|
159
|
+
context
|
160
|
+
end
|
161
|
+
|
162
|
+
|
163
|
+
def handle_event(debug=false)
|
164
|
+
# In order to implement in another platform, this function must exist
|
165
|
+
end
|
166
|
+
|
167
|
+
def log_message_info(user)
|
168
|
+
context = user.context
|
169
|
+
logger.write "\n-------- INCOMING MESSAGE -------", :yellow
|
170
|
+
logger.write "Text : \"#{self.text}\"", :yellow
|
171
|
+
unless self.params.empty?
|
172
|
+
logger.write "Payload : #{self.payload} => params: #{self.params}", :yellow
|
173
|
+
else
|
174
|
+
logger.write "Payload : #{self.payload}", :yellow
|
175
|
+
end
|
176
|
+
logger.write "User ID : #{user.psid}", :yellow
|
177
|
+
logger.write "Context : #{(context.nil? || context.empty?) ? Kogno::Application.config.routes.default : context}", :yellow
|
178
|
+
logger.write "---------------------------------\n", :yellow
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
182
|
+
end
|
@@ -0,0 +1,281 @@
|
|
1
|
+
module Kogno
|
2
|
+
module Messenger
|
3
|
+
class Api
|
4
|
+
|
5
|
+
require 'uri'
|
6
|
+
require 'net/http'
|
7
|
+
require 'json'
|
8
|
+
require 'openssl'
|
9
|
+
|
10
|
+
class << self
|
11
|
+
|
12
|
+
def request(body,access_token,type=:messages,method=:post) # :messages, :messenger_profile
|
13
|
+
url = URI("#{Kogno::Application.messenger.graph_url}/#{type}?access_token=#{access_token}")
|
14
|
+
|
15
|
+
logger.write "REQUEST TO: #{url}", :pink
|
16
|
+
logger.write "SENT: #{JSON.parse(body)}", :blue
|
17
|
+
|
18
|
+
http = Net::HTTP.new(url.host, url.port)
|
19
|
+
http.use_ssl = true
|
20
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
21
|
+
|
22
|
+
case method
|
23
|
+
when :delete
|
24
|
+
request = Net::HTTP::Delete.new(url)
|
25
|
+
else
|
26
|
+
request = Net::HTTP::Post.new(url)
|
27
|
+
end
|
28
|
+
|
29
|
+
request["content-type"] = 'application/json'
|
30
|
+
|
31
|
+
request.body = body
|
32
|
+
|
33
|
+
response = http.request(request)
|
34
|
+
# logger.debug_json JSON.parse(body), :green
|
35
|
+
response_hash = JSON.parse(response.read_body, {:symbolize_names => true})
|
36
|
+
logger.write "RESPONSE: #{response_hash}\n", :light_blue
|
37
|
+
return(response_hash)
|
38
|
+
end
|
39
|
+
|
40
|
+
def setting(body,page_id=nil)
|
41
|
+
access_token = self.get_access_token(page_id)
|
42
|
+
self.request(body,access_token,:messenger_profile)
|
43
|
+
end
|
44
|
+
|
45
|
+
def setting_delete(fields,page_id=nil)
|
46
|
+
access_token = self.get_access_token(page_id)
|
47
|
+
self.request({
|
48
|
+
fields: fields
|
49
|
+
}.to_json,access_token,:messenger_profile, :delete)
|
50
|
+
end
|
51
|
+
|
52
|
+
def send_message(recipient_psid,message,messaging_type="RESPONSE",page_id=nil)
|
53
|
+
|
54
|
+
body = {
|
55
|
+
messaging_type: messaging_type,
|
56
|
+
recipient: {
|
57
|
+
id: recipient_psid
|
58
|
+
},
|
59
|
+
message: message
|
60
|
+
}.to_json
|
61
|
+
|
62
|
+
access_token = self.get_access_token(page_id)
|
63
|
+
return(self.request(body,access_token))
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
def send_message_with_rn_token(notification_token,message,page_id=nil)
|
68
|
+
|
69
|
+
body = {
|
70
|
+
recipient: {
|
71
|
+
notification_messages_token: notification_token
|
72
|
+
},
|
73
|
+
message: message
|
74
|
+
}.to_json
|
75
|
+
|
76
|
+
access_token = self.get_access_token(page_id)
|
77
|
+
return(self.request(body,access_token))
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
def send_private_reply(recipient_id,message,page_id=nil)
|
83
|
+
|
84
|
+
body = {
|
85
|
+
:recipient => {
|
86
|
+
:comment_id => recipient_id
|
87
|
+
},
|
88
|
+
:message => message
|
89
|
+
}.to_json
|
90
|
+
|
91
|
+
access_token = self.get_access_token(page_id)
|
92
|
+
return(self.request(body,access_token))
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
|
97
|
+
def send_action(recipient_psid,action,messaging_type="RESPONSE",page_id=nil) # action => mark_seen, typing_on, typing_off
|
98
|
+
# body = %{{"recipient":{"id":"#{recipient_psid}"},"sender_action":"#{action}"}}
|
99
|
+
body = {
|
100
|
+
messaging_type: messaging_type,
|
101
|
+
recipient: {
|
102
|
+
id: recipient_psid
|
103
|
+
},
|
104
|
+
sender_action: action
|
105
|
+
}.to_json
|
106
|
+
|
107
|
+
access_token = self.get_access_token(page_id)
|
108
|
+
return(self.request(body,access_token))
|
109
|
+
end
|
110
|
+
|
111
|
+
def text_message(text)
|
112
|
+
|
113
|
+
{
|
114
|
+
:text => text
|
115
|
+
}
|
116
|
+
# return %{{"text":"#{text}"}}
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
def attachment_message(attachment)
|
122
|
+
|
123
|
+
{
|
124
|
+
attachment: attachment
|
125
|
+
}
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
def button_template(text,buttons)
|
130
|
+
|
131
|
+
attachment = {
|
132
|
+
type: :template,
|
133
|
+
payload: {
|
134
|
+
template_type: :button,
|
135
|
+
text: text,
|
136
|
+
buttons: buttons
|
137
|
+
}
|
138
|
+
}
|
139
|
+
return self.attachment_message(attachment)
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
def quick_replies(title,replies)
|
144
|
+
|
145
|
+
response = {
|
146
|
+
text: title,
|
147
|
+
quick_replies: replies
|
148
|
+
}
|
149
|
+
|
150
|
+
return response
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
def list_template(elements, buttons = [])
|
155
|
+
attachment = {
|
156
|
+
type: :template,
|
157
|
+
payload: {
|
158
|
+
template_type: :list,
|
159
|
+
top_element_style: :compact,
|
160
|
+
elements: elements,
|
161
|
+
buttons: buttons
|
162
|
+
}
|
163
|
+
}
|
164
|
+
|
165
|
+
return self.attachment_message(attachment)
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
def generic_template(elements,image_aspect_ratio=:horizontal)
|
170
|
+
|
171
|
+
elements = [elements] unless elements.class == Array
|
172
|
+
|
173
|
+
attachment = {
|
174
|
+
type: :template,
|
175
|
+
payload: {
|
176
|
+
template_type: :generic,
|
177
|
+
image_aspect_ratio: image_aspect_ratio,
|
178
|
+
elements: elements
|
179
|
+
}
|
180
|
+
}
|
181
|
+
|
182
|
+
return self.attachment_message(attachment)
|
183
|
+
|
184
|
+
end
|
185
|
+
|
186
|
+
def generic_template_with_replies(elements,image_aspect_ratio=:horizontal, quick_replies)
|
187
|
+
message = generic_template(elements,image_aspect_ratio=:horizontal)
|
188
|
+
message[:quick_replies] = quick_replies
|
189
|
+
return message
|
190
|
+
end
|
191
|
+
|
192
|
+
def show_ad_message(message)
|
193
|
+
|
194
|
+
JSON.pretty_generate(
|
195
|
+
{
|
196
|
+
message: message,
|
197
|
+
user_edit: true
|
198
|
+
}
|
199
|
+
)
|
200
|
+
|
201
|
+
end
|
202
|
+
|
203
|
+
def get_access_token(page_id=nil)
|
204
|
+
|
205
|
+
if page_id.nil?
|
206
|
+
Kogno::Application.messenger.pages.first[1][:token]
|
207
|
+
else
|
208
|
+
Kogno::Application.messenger.pages[page_id][:token] rescue nil
|
209
|
+
end
|
210
|
+
|
211
|
+
end
|
212
|
+
|
213
|
+
def persistent_menu(page_id=nil)
|
214
|
+
persistent_menu = Kogno::Application.messenger.persistent_menu
|
215
|
+
if persistent_menu.nil?
|
216
|
+
body = {
|
217
|
+
persistent_menu:[
|
218
|
+
{
|
219
|
+
locale: "default",
|
220
|
+
composer_input_disabled: false,
|
221
|
+
}
|
222
|
+
]
|
223
|
+
}
|
224
|
+
else
|
225
|
+
body = {
|
226
|
+
persistent_menu: persistent_menu
|
227
|
+
}
|
228
|
+
end
|
229
|
+
logger.write_json body, :bright
|
230
|
+
self.setting(body.to_json, page_id)
|
231
|
+
end
|
232
|
+
|
233
|
+
def get_started_button(page_id=nil)
|
234
|
+
body = {
|
235
|
+
get_started: {
|
236
|
+
payload: Kogno::Application.messenger.welcome_screen_payload
|
237
|
+
}
|
238
|
+
}
|
239
|
+
logger.write_json body, :bright
|
240
|
+
self.setting(body.to_json, page_id)
|
241
|
+
end
|
242
|
+
|
243
|
+
def ice_breakers(page_id=nil)
|
244
|
+
body = {
|
245
|
+
ice_breakers: Kogno::Application.messenger.ice_breakers
|
246
|
+
}
|
247
|
+
logger.write_json body, :bright
|
248
|
+
self.setting(body.to_json, page_id)
|
249
|
+
end
|
250
|
+
|
251
|
+
def add_domain(domain)
|
252
|
+
body = {
|
253
|
+
whitelisted_domains: [
|
254
|
+
domain
|
255
|
+
]
|
256
|
+
}
|
257
|
+
logger.write_json body, :bright
|
258
|
+
self.setting(body.to_json)
|
259
|
+
end
|
260
|
+
|
261
|
+
def update_whitelisted_domains
|
262
|
+
body = {
|
263
|
+
whitelisted_domains: Kogno::Application.messenger.whitelisted_domains
|
264
|
+
}
|
265
|
+
logger.write_json body, :bright
|
266
|
+
self.setting(body.to_json)
|
267
|
+
end
|
268
|
+
|
269
|
+
def greeting(page_id=nil)
|
270
|
+
body = {
|
271
|
+
greeting: Kogno::Application.messenger.greeting
|
272
|
+
}
|
273
|
+
logger.write_json body, :bright
|
274
|
+
self.setting(body.to_json, page_id)
|
275
|
+
end
|
276
|
+
|
277
|
+
end
|
278
|
+
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'net/http'
|
3
|
+
require 'openssl'
|
4
|
+
|
5
|
+
module Kogno
|
6
|
+
class FacebookGraph
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def fetch_user_data(psid,page_id=nil)
|
10
|
+
url = URI("https://graph.facebook.com/v3.3/#{psid}/?access_token=#{self.get_access_token(page_id)}&fields=name,first_name,last_name,timezone,locale,profile_pic")
|
11
|
+
puts url
|
12
|
+
http = Net::HTTP.new(url.host, url.port)
|
13
|
+
http.use_ssl = true
|
14
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
15
|
+
request = Net::HTTP::Get.new(url)
|
16
|
+
response = http.request(request)
|
17
|
+
return(JSON.parse(response.read_body, {:symbolize_names => true}))
|
18
|
+
end
|
19
|
+
|
20
|
+
def get_access_token(page_id=nil)
|
21
|
+
if page_id.nil?
|
22
|
+
Kogno::Application.messenger.pages.first[1][:token]
|
23
|
+
else
|
24
|
+
Kogno::Application.messenger.pages[page_id][:token] rescue nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|