bender-bot 0.1.0 → 0.1.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 (5) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/lib/bender/bot.rb +152 -52
  4. data/lib/bender/main.rb +1 -2
  5. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2eff1d2551be6a4039e117067c97b71a246488bc
4
- data.tar.gz: 9b7f0a4415aa65ce54a69c28ee873db38811d706
3
+ metadata.gz: 268fd0e2852e1f26a640003d74b3b6bd7ed12700
4
+ data.tar.gz: 107352d30d4f117cc3d99b1110105db9a557c357
5
5
  SHA512:
6
- metadata.gz: be3ff23c1ed7b956262236ccfd94596d88a97ad027dc7c9643accad7a7a60f4dbf808698275700092af2d000bd0a0753a736e2766757842c0573d71456e2071a
7
- data.tar.gz: 8436e940e11db1d586ad20fe783a6321dd705be6319141a4b657b06f96fbe0fce6230b155f225a4cdbd7a2e6f6a78986d861ea599fa56e8a5e0c585a7ff9e15a
6
+ metadata.gz: c7bc07efab94683ef5825319cef8fd436f24e24f60440982d8f7ffda57d2d6fb6a24de70cd3ecfae5f03413266e348426b486dd9c3f1535c7213f418757ce92d
7
+ data.tar.gz: bf6b12223f0fe6262079e021b144389d8a80559e6932bb5603c50fc1747c0d0c215d7607b3748ba6404750d5b1c7ac53db9fc9c505004fa6ec78b606040ce91c
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.1.1
@@ -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 - This help text',
79
+ '?inc - Display this help text',
74
80
  '/inc - List open incidents',
75
- '/inc NUM - Show incident details',
76
- '/inc close NUM - Close an incident',
77
- '/inc SEVERITY SUMMARY - File a new incident',
78
- '/inc summary - Summarize recent incidents'
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: %s' % [ i['num'], i['fields']['summary'] ]
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
- severity_field = SHOW_FIELDS.key 'Severity'
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
- repr = '%s-%s: %s' % [
102
- options.jira_project, i['num'], i['fields']['summary']
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
- is = severities.keys.sort.map do |sev|
110
- "%s:\n%s" % [ sev, severities[sev].join("\n") ]
111
- end.join("\n\n")
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
- reply is
148
+ if severities.empty?
149
+ reply 'No recent incidents! Woohoo!'
114
150
 
115
- when /^\s*\/inc\s+(\d+)\s*$/
116
- refresh_incidents
117
- incident = store['incidents'].select { |i| i['num'] == $1 }.first
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
- fields = SHOW_FIELDS.keys - %w[ summary ]
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
- reply "%s\n%s: %s\n%s" % [
131
- (options.jira_site + '/browse/' + incident['key']),
132
- incident['key'],
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
- when /^\s*\/inc\s+close\s+(\d+)\s*$/
138
- refresh_incidents
139
- incident = store['incidents'].select { |i| i['num'] == $1 }.first
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
- reply close_incident(incident)
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
- when /^\s*\/inc\s+(sev|s|p)?(\d+)\s+(.*?)\s*$/i
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
- return options.jira_site + '/browse/' + issue['key']
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
- resp = http.request req
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
- if closed
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
- "Failed to close, make sure you've got the ticket filled out",
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(3).sub(/Z$/, 'UTC')
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
@@ -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.0
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-14 00:00:00.000000000 Z
11
+ date: 2015-08-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor