bender-bot 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/lib/bender/bot.rb +152 -52
- data/lib/bender/main.rb +1 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 268fd0e2852e1f26a640003d74b3b6bd7ed12700
|
4
|
+
data.tar.gz: 107352d30d4f117cc3d99b1110105db9a557c357
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c7bc07efab94683ef5825319cef8fd436f24e24f60440982d8f7ffda57d2d6fb6a24de70cd3ecfae5f03413266e348426b486dd9c3f1535c7213f418757ce92d
|
7
|
+
data.tar.gz: bf6b12223f0fe6262079e021b144389d8a80559e6932bb5603c50fc1747c0d0c215d7607b3748ba6404750d5b1c7ac53db9fc9c505004fa6ec78b606040ce91c
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.1
|
data/lib/bender/bot.rb
CHANGED
@@ -30,6 +30,8 @@ class BenderBot
|
|
30
30
|
|
31
31
|
CLOSE_TRANSITIONS = %w[ 21 41 ]
|
32
32
|
|
33
|
+
CLOSE_STATE = /done/i
|
34
|
+
|
33
35
|
SEVERITIES = {
|
34
36
|
1 => '10480',
|
35
37
|
2 => '10481',
|
@@ -54,6 +56,9 @@ class BenderBot
|
|
54
56
|
|
55
57
|
|
56
58
|
def handle time, sender, message
|
59
|
+
severity_field = SHOW_FIELDS.key 'Severity'
|
60
|
+
severities = Hash.new { |h,k| h[k] = [] }
|
61
|
+
|
57
62
|
case message
|
58
63
|
|
59
64
|
|
@@ -68,79 +73,127 @@ class BenderBot
|
|
68
73
|
u = user_where(name: $1) || user_where(nick: $1)
|
69
74
|
reply '%s: %s (%s)' % [ u[:nick], u[:name], u[:email] ]
|
70
75
|
|
76
|
+
# ?inc - This help text
|
71
77
|
when /^\s*\?inc\s*$/
|
72
78
|
reply [
|
73
|
-
'?inc -
|
79
|
+
'?inc - Display this help text',
|
74
80
|
'/inc - List open incidents',
|
75
|
-
'/inc
|
76
|
-
'/inc close
|
77
|
-
'/inc SEVERITY
|
78
|
-
'/inc summary - Summarize
|
81
|
+
'/inc [INCIDENT_NUMBER] - Display incident details',
|
82
|
+
'/inc close [INCIDENT_NUMBER] - Close an incident',
|
83
|
+
'/inc open [SEVERITY=1,2,3,4,5] [SUMMARY_TEXT] - Open a new incident',
|
84
|
+
'/inc summary - Summarize incidents from past 24 hours (open or closed)',
|
85
|
+
'/inc comment [INCIDENT_NUMBER] [COMMENT_TEXT] - Add a comment to an incident'
|
79
86
|
].join("\n")
|
80
87
|
|
88
|
+
# /inc - List open incidents
|
81
89
|
when /^\s*\/inc\s*$/
|
82
90
|
refresh_incidents
|
83
91
|
|
84
|
-
is = store['incidents'].map do |i|
|
92
|
+
is = store['incidents'].reverse.map do |i|
|
85
93
|
status = normalize_value i['fields']['status']
|
86
94
|
unless status =~ /done|complete|closed/i
|
87
|
-
'%s
|
95
|
+
'%s-%s (%s - %s) [%s]: %s' % [
|
96
|
+
options.jira_project,
|
97
|
+
i['num'],
|
98
|
+
short_severity(i['fields'][severity_field]['value']),
|
99
|
+
normalize_value(i['fields']['status']),
|
100
|
+
friendly_date(i['fields']['created']),
|
101
|
+
i['fields']['summary']
|
102
|
+
]
|
88
103
|
end
|
89
104
|
end.compact.join("\n")
|
90
105
|
|
106
|
+
is = 'No open incidents at the moment!' if is.empty?
|
107
|
+
|
91
108
|
reply is
|
92
109
|
|
110
|
+
# /inc summary - Summarize recent incidents
|
93
111
|
when /^\s*\/inc\s+summary\s*$/
|
94
112
|
refresh_incidents
|
95
113
|
|
96
|
-
|
97
|
-
severities = Hash.new { |h,k| h[k] = [] }
|
114
|
+
statuses = Hash.new { |h,k| h[k] = 0 }
|
98
115
|
|
99
|
-
store['incidents'].each do |i|
|
116
|
+
store['incidents'].reverse.each do |i|
|
100
117
|
if recent_incident? i
|
101
|
-
|
102
|
-
|
118
|
+
status = normalize_value(i['fields']['status'])
|
119
|
+
|
120
|
+
repr = '%s-%s (%s) [%s]: %s' % [
|
121
|
+
options.jira_project,
|
122
|
+
i['num'],
|
123
|
+
status,
|
124
|
+
friendly_date(i['fields']['created']),
|
125
|
+
i['fields']['summary']
|
103
126
|
]
|
127
|
+
|
104
128
|
sev = i['fields'][severity_field]['value']
|
105
129
|
severities[sev] << repr
|
130
|
+
statuses[status] += 1
|
106
131
|
end
|
107
132
|
end
|
108
133
|
|
109
|
-
|
110
|
-
|
111
|
-
|
134
|
+
summary = []
|
135
|
+
summary << 'By Status:'
|
136
|
+
statuses.each do |status, size|
|
137
|
+
summary << '%s: %d ticket(s)' % [ status, size ]
|
138
|
+
end
|
139
|
+
summary << ''
|
140
|
+
summary << 'By Severity:'
|
141
|
+
severities.keys.sort.each do |severity|
|
142
|
+
summary << '%s: %d ticket(s)' % [
|
143
|
+
short_severity(severity),
|
144
|
+
severities[severity].size
|
145
|
+
]
|
146
|
+
end
|
112
147
|
|
113
|
-
|
148
|
+
if severities.empty?
|
149
|
+
reply 'No recent incidents! Woohoo!'
|
114
150
|
|
115
|
-
|
116
|
-
|
117
|
-
|
151
|
+
else
|
152
|
+
is = severities.keys.sort.map do |sev|
|
153
|
+
"%s:\n%s" % [ sev, severities[sev].join("\n") ]
|
154
|
+
end.join("\n\n")
|
118
155
|
|
119
|
-
|
156
|
+
reply(summary.join("\n") + "\n\n" + is)
|
157
|
+
end
|
120
158
|
|
121
|
-
i = fields.map do |f|
|
122
|
-
val = incident['fields'][f]
|
123
|
-
if val
|
124
|
-
key = SHOW_FIELDS[f]
|
125
|
-
val = normalize_value val
|
126
|
-
'%s: %s' % [ key, val ]
|
127
|
-
end
|
128
|
-
end.compact
|
129
159
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
incident['fields']['summary'],
|
134
|
-
i.join("\n")
|
135
|
-
]
|
160
|
+
# /inc NUM - Show incident details
|
161
|
+
when /^\s*\/inc\s+(\d+)\s*$/
|
162
|
+
incident = select_incident $1
|
136
163
|
|
137
|
-
|
138
|
-
|
139
|
-
|
164
|
+
if incident.nil?
|
165
|
+
reply 'Sorry, no such incident!'
|
166
|
+
else
|
167
|
+
fields = SHOW_FIELDS.keys - %w[ summary ]
|
168
|
+
|
169
|
+
i = fields.map do |f|
|
170
|
+
val = incident['fields'][f]
|
171
|
+
if val
|
172
|
+
key = SHOW_FIELDS[f]
|
173
|
+
val = normalize_value val
|
174
|
+
'%s: %s' % [ key, val ]
|
175
|
+
end
|
176
|
+
end.compact
|
177
|
+
|
178
|
+
reply "%s\n%s: %s\n%s" % [
|
179
|
+
(options.jira_site + '/browse/' + incident['key']),
|
180
|
+
incident['key'],
|
181
|
+
incident['fields']['summary'],
|
182
|
+
i.join("\n")
|
183
|
+
]
|
184
|
+
end
|
140
185
|
|
141
|
-
|
186
|
+
# /inc close NUM - Close an incident
|
187
|
+
when /^\s*\/inc\s+close\s+(\d+)\s*$/
|
188
|
+
incident = select_incident $1
|
189
|
+
if incident
|
190
|
+
reply close_incident(incident)
|
191
|
+
else
|
192
|
+
reply 'Sorry, no such incident!'
|
193
|
+
end
|
142
194
|
|
143
|
-
|
195
|
+
# /inc open SEVERITY SUMMARY - File a new incident
|
196
|
+
when /^\s*\/inc\s+open\s+(severity|sev|s|p)?(\d+)\s+(.*?)\s*$/i
|
144
197
|
user = user_where name: sender
|
145
198
|
data = {
|
146
199
|
fields: {
|
@@ -155,6 +208,15 @@ class BenderBot
|
|
155
208
|
}
|
156
209
|
|
157
210
|
reply file_incident(data)
|
211
|
+
|
212
|
+
|
213
|
+
# /inc comment [INCIDENT_NUMBER] [COMMENT_TEXT]
|
214
|
+
when /^\s*\/inc\s+comment\s+(\d+)\s+(.*?)\s*$/i
|
215
|
+
incident = select_incident $1
|
216
|
+
comment = $2
|
217
|
+
user = user_where name: sender
|
218
|
+
|
219
|
+
reply comment_on_incident(incident, comment, user)
|
158
220
|
end
|
159
221
|
|
160
222
|
return true
|
@@ -226,7 +288,11 @@ private
|
|
226
288
|
resp = http.request req
|
227
289
|
issue = JSON.parse(resp.body)
|
228
290
|
|
229
|
-
|
291
|
+
if issue.has_key? 'key'
|
292
|
+
options.jira_site + '/browse/' + issue['key']
|
293
|
+
else
|
294
|
+
"Sorry, I couldn't file that!"
|
295
|
+
end
|
230
296
|
end
|
231
297
|
|
232
298
|
|
@@ -242,26 +308,44 @@ private
|
|
242
308
|
req['Content-Type'] = 'application/json'
|
243
309
|
req['Accept'] = 'application/json'
|
244
310
|
|
245
|
-
closed = false
|
246
311
|
CLOSE_TRANSITIONS.each do |tid|
|
247
312
|
req.body = {
|
248
313
|
transition: { id: tid }
|
249
314
|
}.to_json
|
250
|
-
|
251
|
-
case resp
|
252
|
-
when Net::HTTPBadRequest
|
253
|
-
next
|
254
|
-
else
|
255
|
-
closed = true
|
256
|
-
break
|
257
|
-
end
|
315
|
+
http.request req
|
258
316
|
end
|
259
317
|
|
260
|
-
|
318
|
+
incident = select_incident incident['key'].split('-',2).last
|
319
|
+
status = normalize_value incident['fields']['status']
|
320
|
+
|
321
|
+
if status =~ CLOSE_STATE
|
261
322
|
'Closed: ' + options.jira_site + '/browse/' + incident['key']
|
262
323
|
else
|
263
324
|
[
|
264
|
-
|
325
|
+
'Failed to close automatically, you might try yourself',
|
326
|
+
(options.jira_site + '/browse/' + incident['key'])
|
327
|
+
].join("\n")
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
|
332
|
+
def comment_on_incident incident, comment, user
|
333
|
+
req_path = '/rest/api/2/issue/%s/comment' % incident['key']
|
334
|
+
uri = URI(options.jira_site + req_path)
|
335
|
+
http = Net::HTTP.new uri.hostname, uri.port
|
336
|
+
|
337
|
+
req = Net::HTTP::Post.new uri
|
338
|
+
req.basic_auth options.jira_user, options.jira_pass
|
339
|
+
req['Content-Type'] = 'application/json'
|
340
|
+
req['Accept'] = 'application/json'
|
341
|
+
req.body = { body: '_[~%s]_ says: %s' % [ user[:nick], comment ] }.to_json
|
342
|
+
|
343
|
+
case http.request(req)
|
344
|
+
when Net::HTTPCreated
|
345
|
+
'Added: ' + options.jira_site + '/browse/' + incident['key']
|
346
|
+
else
|
347
|
+
[
|
348
|
+
'Sorry, I had trouble adding your comment',
|
265
349
|
(options.jira_site + '/browse/' + incident['key'])
|
266
350
|
].join("\n")
|
267
351
|
end
|
@@ -283,7 +367,12 @@ private
|
|
283
367
|
|
284
368
|
|
285
369
|
def normalize_date val
|
286
|
-
Time.parse(val).utc.iso8601(
|
370
|
+
Time.parse(val).utc.iso8601(0).sub(/Z$/, 'UTC')
|
371
|
+
end
|
372
|
+
|
373
|
+
|
374
|
+
def friendly_date val
|
375
|
+
Time.parse(val).strftime('%Y-%m-%d %H:%M %Z')
|
287
376
|
end
|
288
377
|
|
289
378
|
|
@@ -297,4 +386,15 @@ private
|
|
297
386
|
24 * 60 * 60 # seconds/day
|
298
387
|
end
|
299
388
|
|
389
|
+
|
390
|
+
def select_incident num, refresh=true
|
391
|
+
refresh_incidents if refresh
|
392
|
+
store['incidents'].select { |i| i['num'] == num }.first
|
393
|
+
end
|
394
|
+
|
395
|
+
|
396
|
+
def short_severity s
|
397
|
+
s.split(' - ', 2).first
|
398
|
+
end
|
399
|
+
|
300
400
|
end
|
data/lib/bender/main.rb
CHANGED
@@ -153,7 +153,6 @@ module Bender
|
|
153
153
|
Thread.new do
|
154
154
|
loop do
|
155
155
|
resp = http.request req
|
156
|
-
|
157
156
|
users = JSON.parse(resp.body).inject({}) do |h, user|
|
158
157
|
h[user['key']] = {
|
159
158
|
nick: user['key'],
|
@@ -189,4 +188,4 @@ module Bender
|
|
189
188
|
|
190
189
|
|
191
190
|
end
|
192
|
-
end
|
191
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bender-bot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sean Clemmer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-08-
|
11
|
+
date: 2015-08-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|