botanalytics 0.2.2
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/lib/botanalytics.rb +13 -0
- data/lib/botanalytics/amazon.rb +64 -0
- data/lib/botanalytics/facebook.rb +106 -0
- data/lib/botanalytics/generic.rb +65 -0
- data/lib/botanalytics/google.rb +67 -0
- data/lib/botanalytics/slack.rb +301 -0
- data/lib/util/util.rb +113 -0
- metadata +64 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8118a81982da69c23bd717e419f7087cfad3c306b31e48ef3be04b54870f3a8c
|
4
|
+
data.tar.gz: 2c0e9688fdc4c7b798b0d609ccc742a81dffc1445f981f4092284913ab294e6b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e5b311fd5f1663438e79fae71f0fc95f749e1735c2dd63750cce3b5546750abfc7cfd48268ae41a723067b5ff6fca66dac714ef21d92489ed6c1b36ba9b8aa1e
|
7
|
+
data.tar.gz: ac70bd6d10c1440c3848d76206353d2a9943684f4888420aa2bfd97f08324b25758c8562db3a52e925512f393a92f904a1ecc0daddaf9d0f15a34e4d2a699612
|
data/lib/botanalytics.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require_relative 'botanalytics/google'
|
2
|
+
require_relative 'botanalytics/amazon'
|
3
|
+
require_relative 'botanalytics/facebook'
|
4
|
+
require_relative 'botanalytics/generic'
|
5
|
+
require_relative 'botanalytics/slack'
|
6
|
+
module Botanalytics
|
7
|
+
AmazonAlexa = ::AmazonAlexa
|
8
|
+
GoogleAssistant = ::GoogleAssistant
|
9
|
+
Generic = ::Generic
|
10
|
+
SlackRTMApi = ::SlackRTMApi
|
11
|
+
SlackEventApi = ::SlackEventApi
|
12
|
+
FacebookMessenger = ::FacebookMessenger
|
13
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require_relative '../util/util'
|
2
|
+
|
3
|
+
# Amazon Alexa Logger
|
4
|
+
class AmazonAlexa < Envoy
|
5
|
+
# @param params Hash
|
6
|
+
# @raise ArgumentError When token is nil
|
7
|
+
def initialize(params = {})
|
8
|
+
super(params)
|
9
|
+
@path = 'messages/amazon-alexa/'
|
10
|
+
@async = params.fetch(:async, false)
|
11
|
+
informs("Logging enabled for #{self.class.name}...")
|
12
|
+
if @async
|
13
|
+
require 'concurrent'
|
14
|
+
@executor_service = Concurrent::ThreadPoolExecutor.new(
|
15
|
+
min_threads: 1,
|
16
|
+
max_threads: Concurrent.processor_count,
|
17
|
+
max_queue: 100,
|
18
|
+
fallback_policy: :caller_runs
|
19
|
+
)
|
20
|
+
informs("Mode: Async...")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def log(req, res)
|
25
|
+
validation = validate(req, res)
|
26
|
+
if validation[:ok]
|
27
|
+
payload = {:request => req, :response => res}
|
28
|
+
informs("Logging messages...")
|
29
|
+
informs(payload)
|
30
|
+
if @async
|
31
|
+
@executor_service.post do
|
32
|
+
submits(payload, @path)
|
33
|
+
end
|
34
|
+
else
|
35
|
+
submits(payload, @path)
|
36
|
+
end
|
37
|
+
else
|
38
|
+
fails(validation[:err], validation[:reason], {:request => req, :response => res})
|
39
|
+
end
|
40
|
+
end
|
41
|
+
# @param req Hash
|
42
|
+
# @param res Hash
|
43
|
+
# @return Hash
|
44
|
+
def validate(req, res)
|
45
|
+
pv = is_valid(req, {}, 'request')
|
46
|
+
unless pv[:ok]
|
47
|
+
return pv
|
48
|
+
end
|
49
|
+
pv = is_valid(req, {}, 'request', 'request')
|
50
|
+
unless pv[:ok]
|
51
|
+
return pv
|
52
|
+
end
|
53
|
+
pv = is_valid(req, "", 'request', 'context', 'System', 'user', 'userId')
|
54
|
+
unless pv[:ok]
|
55
|
+
return pv
|
56
|
+
end
|
57
|
+
pv = is_valid(res, {}, 'response')
|
58
|
+
unless pv[:ok]
|
59
|
+
return pv
|
60
|
+
end
|
61
|
+
is_valid(res, {}, 'response', 'response')
|
62
|
+
end
|
63
|
+
private :validate
|
64
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require_relative "../util/util"
|
2
|
+
|
3
|
+
require_relative '../util/util'
|
4
|
+
|
5
|
+
# Facebook Logger
|
6
|
+
class FacebookMessenger < Envoy
|
7
|
+
# @param params Hash
|
8
|
+
# @raise ArgumentError When token is nil
|
9
|
+
def initialize(params = {})
|
10
|
+
super(params)
|
11
|
+
@fb_token = params.fetch(:fb_token, nil)
|
12
|
+
@path = 'messages/facebook-messenger/'
|
13
|
+
@profile_path = 'facebook-messenger/users/'
|
14
|
+
@async = params.fetch(:async, false)
|
15
|
+
informs("Logging enabled for #{self.class.name}...")
|
16
|
+
if @async
|
17
|
+
require 'concurrent'
|
18
|
+
@executor_service = Concurrent::ThreadPoolExecutor.new(
|
19
|
+
min_threads: 1,
|
20
|
+
max_threads: Concurrent.processor_count,
|
21
|
+
max_queue: 100,
|
22
|
+
fallback_policy: :caller_runs
|
23
|
+
)
|
24
|
+
informs("Mode: Async...")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
# @param message Hash
|
28
|
+
def log_incoming(message)
|
29
|
+
validation = validate(message, "Incoming message")
|
30
|
+
if validation[:ok]
|
31
|
+
payload = {
|
32
|
+
'recipient': nil,
|
33
|
+
'timestamp': (Time.new.to_f*1000).to_i,
|
34
|
+
'message': message
|
35
|
+
}
|
36
|
+
informs("Logging incoming message...")
|
37
|
+
informs(message)
|
38
|
+
if @async
|
39
|
+
@executor_service.post do
|
40
|
+
submits(payload, @path)
|
41
|
+
end
|
42
|
+
else
|
43
|
+
submits(payload, @path)
|
44
|
+
end
|
45
|
+
else
|
46
|
+
fails(validation[:err], validation[:reason], message)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
# @param message Hash
|
50
|
+
# @param sender_id String
|
51
|
+
def log_outgoing(message, sender_id)
|
52
|
+
validation = validate_outgoing(message, sender_id)
|
53
|
+
if validation[:ok]
|
54
|
+
payload = {
|
55
|
+
'recipient': sender_id,
|
56
|
+
'timestamp': (Time.new.to_f*1000).to_i,
|
57
|
+
'message': message,
|
58
|
+
'fb_token': @fb_token
|
59
|
+
}
|
60
|
+
informs("Logging outgoing message...")
|
61
|
+
informs(message)
|
62
|
+
if @async
|
63
|
+
@executor_service.post do
|
64
|
+
submits(payload, @path)
|
65
|
+
end
|
66
|
+
else
|
67
|
+
submits(payload, @path)
|
68
|
+
end
|
69
|
+
else
|
70
|
+
fails(validation[:err], validation[:reason], message)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# @param message Hash
|
75
|
+
def log_user_profile(message)
|
76
|
+
validation = validate(message, "User profile")
|
77
|
+
if validation[:ok]
|
78
|
+
informs("Logging user profile message...")
|
79
|
+
informs(message)
|
80
|
+
if @async
|
81
|
+
@executor_service.post do
|
82
|
+
submits(message, @profile_path)
|
83
|
+
end
|
84
|
+
else
|
85
|
+
submits(message, @profile_path)
|
86
|
+
end
|
87
|
+
else
|
88
|
+
fails(validation[:err], validation[:reason], message)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def validate(payload, name)
|
93
|
+
is_valid(payload, {}, name)
|
94
|
+
end
|
95
|
+
|
96
|
+
def validate_outgoing(message, sender_id)
|
97
|
+
mv = is_valid(message, {}, "Outgoing message")
|
98
|
+
unless mv[:ok]
|
99
|
+
return mv
|
100
|
+
end
|
101
|
+
is_valid(sender_id, "", "Sender id")
|
102
|
+
end
|
103
|
+
|
104
|
+
private :validate
|
105
|
+
private :validate_outgoing
|
106
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require_relative "../util/util"
|
2
|
+
|
3
|
+
# Generic Logger
|
4
|
+
class Generic < Envoy
|
5
|
+
# @param params Hash
|
6
|
+
# @raise ArgumentError When token is nil
|
7
|
+
def initialize(params = {})
|
8
|
+
super(params)
|
9
|
+
@async = params.fetch(:async, false)
|
10
|
+
informs("Logging enabled for #{self.class.name}...")
|
11
|
+
if @async
|
12
|
+
require 'concurrent'
|
13
|
+
@executor_service = Concurrent::ThreadPoolExecutor.new(
|
14
|
+
min_threads: 1,
|
15
|
+
max_threads: Concurrent.processor_count,
|
16
|
+
max_queue: 100,
|
17
|
+
fallback_policy: :caller_runs
|
18
|
+
)
|
19
|
+
informs("Mode: Async...")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param message Hash
|
24
|
+
def log(message)
|
25
|
+
validation = validate(message)
|
26
|
+
if validation[:ok]
|
27
|
+
informs("Logging messages...")
|
28
|
+
informs(message)
|
29
|
+
if @async
|
30
|
+
@executor_service.post do
|
31
|
+
submits(message)
|
32
|
+
end
|
33
|
+
else
|
34
|
+
submits(message)
|
35
|
+
end
|
36
|
+
else
|
37
|
+
fails(validation[:err], validation[:reason], message)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
# @param payload Hash
|
41
|
+
def validate(payload)
|
42
|
+
pv = is_valid(payload, {}, 'payload')
|
43
|
+
unless pv[:ok]
|
44
|
+
return pv
|
45
|
+
end
|
46
|
+
pv = is_valid(payload, Object.new, 'payload', 'is_sender_bot')
|
47
|
+
unless pv[:ok]
|
48
|
+
return pv
|
49
|
+
end
|
50
|
+
pv = is_valid(payload, "", 'payload', 'user', 'id')
|
51
|
+
unless pv[:ok]
|
52
|
+
return pv
|
53
|
+
end
|
54
|
+
pv = is_valid(payload, "", 'payload', 'user', 'name')
|
55
|
+
unless pv[:ok]
|
56
|
+
return pv
|
57
|
+
end
|
58
|
+
pv = is_valid(payload, "", 'payload', 'message', 'text')
|
59
|
+
unless pv[:ok]
|
60
|
+
return pv
|
61
|
+
end
|
62
|
+
is_valid(payload, 1, 'payload', 'message', 'timestamp')
|
63
|
+
end
|
64
|
+
private :validate
|
65
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require_relative '../util/util'
|
2
|
+
|
3
|
+
# GoogleAsistant Logger
|
4
|
+
class GoogleAssistant < Envoy
|
5
|
+
# @param params Hash
|
6
|
+
# @raise ArgumentError When token is nil
|
7
|
+
def initialize(params = {})
|
8
|
+
super(params)
|
9
|
+
@path = 'messages/google-assistant/'
|
10
|
+
@async = params.fetch(:async, false)
|
11
|
+
informs("Logging enabled for #{self.class.name}...")
|
12
|
+
if @async
|
13
|
+
require 'concurrent'
|
14
|
+
@executor_service = Concurrent::ThreadPoolExecutor.new(
|
15
|
+
min_threads: 1,
|
16
|
+
max_threads: Concurrent.processor_count,
|
17
|
+
max_queue: 100,
|
18
|
+
fallback_policy: :caller_runs
|
19
|
+
)
|
20
|
+
informs("Mode: Async...")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# @param req Hash
|
25
|
+
# @param res Hash
|
26
|
+
def log(req, res)
|
27
|
+
validation = validate(req, res)
|
28
|
+
if validation[:ok]
|
29
|
+
payload = {:request => req, :response => res}
|
30
|
+
informs("Logging messages...")
|
31
|
+
informs(payload)
|
32
|
+
if @async
|
33
|
+
@executor_service.post do
|
34
|
+
submits(payload, @path)
|
35
|
+
end
|
36
|
+
else
|
37
|
+
submits(payload, @path)
|
38
|
+
end
|
39
|
+
else
|
40
|
+
fails(validation[:err], validation[:reason], {:request => req, :response => res})
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def validate(req, res)
|
45
|
+
pv = is_valid(req, {}, 'request')
|
46
|
+
unless pv[:ok]
|
47
|
+
return pv
|
48
|
+
end
|
49
|
+
pv = is_valid(req, "", 'request', 'user', 'userId')
|
50
|
+
unless pv[:ok]
|
51
|
+
return pv
|
52
|
+
end
|
53
|
+
pv = is_valid(req, "", 'request', 'conversation', 'conversationId')
|
54
|
+
unless pv[:ok]
|
55
|
+
return pv
|
56
|
+
end
|
57
|
+
pv = is_valid(res, {}, 'response')
|
58
|
+
unless pv[:ok]
|
59
|
+
return pv
|
60
|
+
end
|
61
|
+
res['expectUserResponse'] ?
|
62
|
+
is_valid(res, {}, 'response', 'finalResponse'):
|
63
|
+
is_valid(res, [], 'response', 'expectedInputs')
|
64
|
+
|
65
|
+
end
|
66
|
+
private :validate
|
67
|
+
end
|
@@ -0,0 +1,301 @@
|
|
1
|
+
require_relative "../util/util"
|
2
|
+
require 'net/https'
|
3
|
+
require 'uri'
|
4
|
+
require 'json'
|
5
|
+
require 'thread'
|
6
|
+
|
7
|
+
class SlackRTMApi < Envoy
|
8
|
+
|
9
|
+
def initialize(params={})
|
10
|
+
super(params)
|
11
|
+
@slack_token = params.fetch(:slack_token, nil)
|
12
|
+
raise ArgumentError.new "slack_token can not be nil or empty" if @slack_token.nil? || @slack_token.to_s.empty?
|
13
|
+
@path = 'messages/slack/'
|
14
|
+
@initialize_path = 'bots/slack/initialize/'
|
15
|
+
@active_user = nil
|
16
|
+
@active_team = nil
|
17
|
+
@async = params.fetch(:async, false)
|
18
|
+
informs("Logging enabled for #{self.class.name}...")
|
19
|
+
if @async
|
20
|
+
require 'concurrent'
|
21
|
+
@executor_service = Concurrent::ThreadPoolExecutor.new(
|
22
|
+
min_threads: 1,
|
23
|
+
max_threads: Concurrent.processor_count+1,
|
24
|
+
max_queue: 100,
|
25
|
+
fallback_policy: :caller_runs
|
26
|
+
)
|
27
|
+
@executor_service.post do
|
28
|
+
fetch
|
29
|
+
end
|
30
|
+
informs("Mode: Async...")
|
31
|
+
else
|
32
|
+
Thread.new do
|
33
|
+
fetch
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def fetch
|
39
|
+
informs("Initializing slack rtm api bot...")
|
40
|
+
wait_interval = 2
|
41
|
+
update_interval = 3600
|
42
|
+
uri = URI.parse('https://slack.com/api/rtm.start')
|
43
|
+
while true
|
44
|
+
begin
|
45
|
+
response = Net::HTTP.post_form(uri, :token => @slack_token)
|
46
|
+
case response
|
47
|
+
when Net::HTTPSuccess
|
48
|
+
start_data = JSON.parse(response.body)
|
49
|
+
if start_data['ok']
|
50
|
+
|
51
|
+
@active_user = start_data['self']['id']
|
52
|
+
@active_team = start_data['team']['id']
|
53
|
+
|
54
|
+
if submits(start_data, @initialize_path, "Successfully updated bot '#{start_data['self']['name']}' info...")
|
55
|
+
informs("Botanalytics::#{self.class.name} initialized team info successfully...")
|
56
|
+
sleep update_interval
|
57
|
+
wait_interval = 2
|
58
|
+
else
|
59
|
+
sleep wait_interval
|
60
|
+
if wait_interval < 20
|
61
|
+
wait_interval += 3
|
62
|
+
end
|
63
|
+
informs("Botanalytics::#{self.class.name} can not initialize team info, retrying...")
|
64
|
+
end
|
65
|
+
else
|
66
|
+
# nope there is a problem here, try
|
67
|
+
sleep wait_interval
|
68
|
+
if wait_interval < 20
|
69
|
+
wait_interval += 3
|
70
|
+
end
|
71
|
+
informs("Botanalytics::#{self.class.name} can not initialize team info, retrying...")
|
72
|
+
end
|
73
|
+
else
|
74
|
+
informs("Botanalytics::#{self.class.name} can not initialize team info, Reason: statuscode:#{response.code} retrying...")
|
75
|
+
sleep wait_interval
|
76
|
+
if wait_interval < 20
|
77
|
+
wait_interval += 3
|
78
|
+
end
|
79
|
+
end
|
80
|
+
rescue Exception => e
|
81
|
+
informs("Botanalytics::#{self.class.name} can not initialize team info due to http error, Error: #{e.message}, retrying...")
|
82
|
+
sleep wait_interval
|
83
|
+
if wait_interval < 20
|
84
|
+
wait_interval += 3
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# @param payload Hash
|
91
|
+
def log_incoming(payload)
|
92
|
+
if @active_team.nil? or @active_user.nil?
|
93
|
+
fails(Exception.new('team and bot'), 'Not initialized yet...')
|
94
|
+
else
|
95
|
+
validation = validate(payload)
|
96
|
+
if validation[:ok]
|
97
|
+
informs('Logging message...')
|
98
|
+
payload['is_bot'] = false
|
99
|
+
payload['team']= @active_team
|
100
|
+
informs(payload)
|
101
|
+
if @async
|
102
|
+
@executor_service.post do
|
103
|
+
submits(payload, @path)
|
104
|
+
end
|
105
|
+
else
|
106
|
+
submits(payload, @path)
|
107
|
+
end
|
108
|
+
else
|
109
|
+
fails(validation[:err], validation[:reason], payload)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def log_outgoing(channel_id, message, params = {})
|
115
|
+
#thread=None, reply_broadcast=None, msg_payload=None
|
116
|
+
|
117
|
+
if @active_team.nil? or @active_user.nil?
|
118
|
+
fails(Exception.new('team and bot'), 'Not initialized yet...')
|
119
|
+
else
|
120
|
+
thread = params.fetch(:thread, nil)
|
121
|
+
reply_broadcast = params.fetch(:reply_broadcast, nil)
|
122
|
+
msg_payload = params.fetch(:msg_payload, nil)
|
123
|
+
if !msg_payload.nil?
|
124
|
+
unless msg_payload.is_a?(Hash)
|
125
|
+
fails(Exception.new("Expected format for msg_payload is Hash found #{msg_payload.class.name}"), 'Unexpected payload format!')
|
126
|
+
return
|
127
|
+
end
|
128
|
+
msg = msg_payload.clone
|
129
|
+
validation = validate(msg)
|
130
|
+
if validation[:ok]
|
131
|
+
msg['is_bot'] = true
|
132
|
+
msg['ts'] = (Time.new.to_f*1000).to_s
|
133
|
+
msg['team'] = @active_team
|
134
|
+
msg['user'] = @active_user
|
135
|
+
informs('Logging message...')
|
136
|
+
informs(msg)
|
137
|
+
if @async
|
138
|
+
@executor_service.post do
|
139
|
+
submits(msg, @path)
|
140
|
+
end
|
141
|
+
else
|
142
|
+
submits(msg, @path)
|
143
|
+
end
|
144
|
+
else
|
145
|
+
informs("Message does not contain 'type' field. Ignoring...")
|
146
|
+
end
|
147
|
+
else
|
148
|
+
payload = Hash.new
|
149
|
+
payload['type'] = 'message'
|
150
|
+
payload['channel'] = channel_id
|
151
|
+
payload['text'] = message
|
152
|
+
payload['thread_ts'] = thread unless thread.nil?
|
153
|
+
payload['reply_broadcast'] = reply_broadcast unless reply_broadcast.nil?
|
154
|
+
payload['is_bot'] = true
|
155
|
+
payload['ts'] = (Time.new.to_f*1000).to_s
|
156
|
+
payload['team'] = @active_team
|
157
|
+
payload['user'] = @active_user
|
158
|
+
if @async
|
159
|
+
@executor_service.post do
|
160
|
+
submits(payload, @path)
|
161
|
+
end
|
162
|
+
else
|
163
|
+
submits(payload, @path)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
|
170
|
+
def validate(payload)
|
171
|
+
is_valid(payload, "", 'message', 'type')
|
172
|
+
end
|
173
|
+
private :fetch
|
174
|
+
private :validate
|
175
|
+
end
|
176
|
+
|
177
|
+
class SlackEventApi < Envoy
|
178
|
+
|
179
|
+
def initialize(params={})
|
180
|
+
super(params)
|
181
|
+
@slack_token = params.fetch(:slack_token, nil)
|
182
|
+
raise ArgumentError.new "slack_token can not be nil or empty" if @slack_token.nil? || @slack_token.to_s.empty?
|
183
|
+
@path = 'messages/slack/event/'
|
184
|
+
@initialize_path = 'bots/slack/initialize/'
|
185
|
+
@interactive_path = 'messages/slack/interactive/'
|
186
|
+
@active_user = nil
|
187
|
+
@active_team = nil
|
188
|
+
@async = params.fetch(:async, false)
|
189
|
+
informs("Logging enabled for #{self.class.name}...")
|
190
|
+
if @async
|
191
|
+
require 'concurrent'
|
192
|
+
@executor_service = Concurrent::ThreadPoolExecutor.new(
|
193
|
+
min_threads: 1,
|
194
|
+
max_threads: Concurrent.processor_count+1,
|
195
|
+
max_queue: 100,
|
196
|
+
fallback_policy: :caller_runs
|
197
|
+
)
|
198
|
+
@executor_service.post do
|
199
|
+
fetch
|
200
|
+
end
|
201
|
+
informs("Mode: Async...")
|
202
|
+
else
|
203
|
+
Thread.new do
|
204
|
+
fetch
|
205
|
+
end
|
206
|
+
end
|
207
|
+
@accepted_types = %w(event_callback interactive_message)
|
208
|
+
end
|
209
|
+
|
210
|
+
def fetch
|
211
|
+
informs("Initializing slack event api bot...")
|
212
|
+
wait_interval = 2
|
213
|
+
update_interval = 3600
|
214
|
+
uri = URI.parse('https://slack.com/api/rtm.start')
|
215
|
+
while true
|
216
|
+
begin
|
217
|
+
response = Net::HTTP.post_form(uri, :token => @slack_token)
|
218
|
+
case response
|
219
|
+
when Net::HTTPSuccess
|
220
|
+
start_data = JSON.parse(response.body)
|
221
|
+
if start_data['ok']
|
222
|
+
|
223
|
+
@active_user = start_data['self']['id']
|
224
|
+
@active_team = start_data['team']['id']
|
225
|
+
|
226
|
+
if submits(start_data, @initialize_path, "Successfully updated bot '#{start_data['self']['name']}' info...")
|
227
|
+
informs("Botanalytics::#{self.class.name} initialized team info successfully...")
|
228
|
+
sleep update_interval
|
229
|
+
wait_interval = 2
|
230
|
+
else
|
231
|
+
sleep wait_interval
|
232
|
+
if wait_interval < 20
|
233
|
+
wait_interval += 3
|
234
|
+
end
|
235
|
+
informs("Botanalytics::#{self.class.name} can not initialize team info, retrying...")
|
236
|
+
end
|
237
|
+
else
|
238
|
+
# nope there is a problem here, try
|
239
|
+
sleep wait_interval
|
240
|
+
if wait_interval < 20
|
241
|
+
wait_interval += 3
|
242
|
+
end
|
243
|
+
informs("Botanalytics::#{self.class.name} can not initialize team info, retrying...")
|
244
|
+
end
|
245
|
+
else
|
246
|
+
informs("Botanalytics::#{self.class.name} can not initialize team info, Reason: statuscode:#{response.code} retrying...")
|
247
|
+
sleep wait_interval
|
248
|
+
if wait_interval < 20
|
249
|
+
wait_interval += 3
|
250
|
+
end
|
251
|
+
end
|
252
|
+
rescue Exception => e
|
253
|
+
informs("Botanalytics::#{self.class.name} can not initialize team info due to http error, Error: #{e.message}, retrying...")
|
254
|
+
sleep wait_interval
|
255
|
+
if wait_interval < 20
|
256
|
+
wait_interval += 3
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
# @param payload Hash
|
263
|
+
def log(payload)
|
264
|
+
if payload.is_a?(Hash)
|
265
|
+
return unless payload['challenge'].nil?
|
266
|
+
end
|
267
|
+
|
268
|
+
if @active_team.nil? or @active_user.nil?
|
269
|
+
fails(Exception.new('team and bot'), 'Not initialized yet...')
|
270
|
+
else
|
271
|
+
validation = validate(payload)
|
272
|
+
if validation[:ok]
|
273
|
+
if @accepted_types.include?(payload['type'])
|
274
|
+
informs('Logging message...')
|
275
|
+
informs(payload)
|
276
|
+
if @async
|
277
|
+
@executor_service.post do
|
278
|
+
submits(payload, payload['type'] == 'event_callback' ? @path : @interactive_path)
|
279
|
+
end
|
280
|
+
else
|
281
|
+
submits(payload, payload['type'] == 'event_callback' ? @path : @interactive_path)
|
282
|
+
end
|
283
|
+
else
|
284
|
+
fails(
|
285
|
+
Exception.new("Expected types, #{@accepted_types} but found #{payload['type']}"),
|
286
|
+
'If you are sure this is a new event type, contact us < tech@botanalytics.co >',
|
287
|
+
payload
|
288
|
+
)
|
289
|
+
end
|
290
|
+
else
|
291
|
+
fails(validation[:err], validation[:reason], payload)
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
def validate(payload)
|
297
|
+
is_valid(payload, "", 'event', 'type')
|
298
|
+
end
|
299
|
+
private :fetch
|
300
|
+
private :validate
|
301
|
+
end
|
data/lib/util/util.rb
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
# Validator method
|
5
|
+
def is_valid(payload, t, name, *keys)
|
6
|
+
|
7
|
+
return {
|
8
|
+
:ok=>false,
|
9
|
+
:reason => 'nil payload is not accepted!',
|
10
|
+
:err => Exception.new("payload is nil!")
|
11
|
+
} if payload.nil?
|
12
|
+
if keys.any?
|
13
|
+
path = ''
|
14
|
+
temp = nil
|
15
|
+
keys.each {
|
16
|
+
|key|
|
17
|
+
if temp.nil?
|
18
|
+
path += key
|
19
|
+
return {
|
20
|
+
:ok => false,
|
21
|
+
:reason => 'Field does not exist',
|
22
|
+
:err => Exception.new("Expected field <#{key}> can not be found in #{name}")
|
23
|
+
} if payload[key.to_sym].nil? && payload[key].nil?
|
24
|
+
temp = payload[key] || payload[key.to_sym]
|
25
|
+
else
|
26
|
+
path += '.'+key
|
27
|
+
return {
|
28
|
+
:ok => false,
|
29
|
+
:reason => 'Field does not exist',
|
30
|
+
:err => Exception.new("Expected field <#{path}> can not be found in #{name}")
|
31
|
+
} if temp[key.to_sym].nil? && temp[key].nil?
|
32
|
+
temp = temp[key] || temp[key.to_sym]
|
33
|
+
end
|
34
|
+
}
|
35
|
+
|
36
|
+
temp.is_a?(t.class) ? {:ok => true} : {
|
37
|
+
:ok => false,
|
38
|
+
:reason => 'Unexpected format!',
|
39
|
+
:err => Exception.new("Expected format for #{path} is #{t.class.name}, found #{temp.class.name}")
|
40
|
+
}
|
41
|
+
else
|
42
|
+
payload.is_a?(t.class) ? {:ok => true} : {
|
43
|
+
:ok => false,
|
44
|
+
:reason => 'Unexpected format!',
|
45
|
+
:err => Exception.new("Expected format for #{name} is #{t.class.name}, found #{payload.class.name}")
|
46
|
+
}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
# Main logging handler class
|
50
|
+
# Others will be derived from it
|
51
|
+
|
52
|
+
class Envoy
|
53
|
+
|
54
|
+
# @param params Hash
|
55
|
+
# @raise ArgumentError When token is nil
|
56
|
+
def initialize(params = {})
|
57
|
+
@debug = params.fetch(:debug, false)
|
58
|
+
@token = params.fetch(:token, nil)
|
59
|
+
raise ArgumentError.new 'Token can not be nil or empty' if @token.nil? || @token.to_s.empty?
|
60
|
+
@base_url = params.fetch(:base_url, "https://api.botanalytics.co/v1/")
|
61
|
+
@callback = params.fetch(:callback, nil)
|
62
|
+
end
|
63
|
+
|
64
|
+
# @param message Object
|
65
|
+
def informs(message)
|
66
|
+
if @debug
|
67
|
+
puts "[Botanalytics Debug]: #{message}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# @param err Exception
|
72
|
+
# @param reason String
|
73
|
+
# @param payload Hash
|
74
|
+
def fails(err, reason, payload = nil)
|
75
|
+
if @callback.nil?
|
76
|
+
puts "[Botanalytics Error]: #{reason['error_message'] || reason unless reason.nil?}, #{err.message unless err.nil?}...\n#{payload unless payload.nil?}"
|
77
|
+
else
|
78
|
+
send(@callback, err, reason, payload)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# @param parchment Hash (required)
|
83
|
+
# @param destination String
|
84
|
+
# @param word String
|
85
|
+
def submits(parchment, destination = 'messages/generic/', word = nil)
|
86
|
+
# Uri
|
87
|
+
uri = URI.parse(@base_url+destination)
|
88
|
+
# Header
|
89
|
+
header = {'Content-Type':'application/json', 'Authorization': 'Token '+@token}
|
90
|
+
begin
|
91
|
+
# Send request
|
92
|
+
response = Net::HTTP.post(uri, parchment.to_json, header)
|
93
|
+
if response.code == "200" or response.code == "201"
|
94
|
+
if word.nil?
|
95
|
+
informs("Successfully logged message(s)...")
|
96
|
+
else
|
97
|
+
informs(word)
|
98
|
+
end
|
99
|
+
return true
|
100
|
+
else
|
101
|
+
fails(Exception.new('Message(s) can not be logged! StatusCode:'+response.code), JSON.parse(response.body), parchment)
|
102
|
+
return false
|
103
|
+
end
|
104
|
+
rescue Exception => e
|
105
|
+
fails(e, 'Error during http request', parchment)
|
106
|
+
return false
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
protected :informs
|
111
|
+
protected :fails
|
112
|
+
protected :submits
|
113
|
+
end
|
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: botanalytics
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Beyhan Esen
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-04-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: concurrent-ruby
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.9'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.9'
|
27
|
+
description: Analytics & engagement gem for chatbots
|
28
|
+
email: tech@botanalytics.co
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- lib/botanalytics.rb
|
34
|
+
- lib/botanalytics/amazon.rb
|
35
|
+
- lib/botanalytics/facebook.rb
|
36
|
+
- lib/botanalytics/generic.rb
|
37
|
+
- lib/botanalytics/google.rb
|
38
|
+
- lib/botanalytics/slack.rb
|
39
|
+
- lib/util/util.rb
|
40
|
+
homepage: https://github.com/Botanalyticsco/botanalytics-ruby
|
41
|
+
licenses:
|
42
|
+
- MIT
|
43
|
+
metadata: {}
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options: []
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 1.9.3
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
requirements: []
|
59
|
+
rubyforge_project:
|
60
|
+
rubygems_version: 2.7.6
|
61
|
+
signing_key:
|
62
|
+
specification_version: 4
|
63
|
+
summary: Tracker for bots
|
64
|
+
test_files: []
|