bender-bot 0.2.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 702cbc0ab493486d62b7c9db41dfc1191a5c9868
4
- data.tar.gz: 3e6535a990ed42e03aa8d6b93c4a24bd83643791
3
+ metadata.gz: 50e516ad8b6f747483302fc79ab811a8cccf2368
4
+ data.tar.gz: bd931995c92e814c96bbcea6f305534c2941cede
5
5
  SHA512:
6
- metadata.gz: 10f0310b8a034da123d18f08da7b22698b57acc31520c66a2e9d9306d64c028f161e3f0a77b123fca96b50ff56ece5e9434a924f0481d2bc34859d971b80225e
7
- data.tar.gz: c8b49ba2fa7bb1f1b715fbbb04dea79ed361657a2483d7fc4f8a8fbe2a61694a28c6992ad0cffd7edc19e0fc81a6e03b09b1abd6ed8dd08300d91826a2cd63b2
6
+ metadata.gz: b43003dffdf9a21bb11877116e287e04b80a84618a3d2743b7a83a030025d7c0303d6842a5d478c048c49680e9a0955576de7497ae0275e08baa26540701abe5
7
+ data.tar.gz: 23de30730b6a02d26c1e71603966bb6811ef7ef4a7037c978f4dc69e75e6d3afe4abccafdd8d4eae6c9afd0d40eae674d3a47f470e52a8cffc56d14e1d9c07e9
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.4
1
+ 0.3.0
data/lib/bender/bot.rb CHANGED
@@ -8,6 +8,8 @@ require 'robut/storage/yaml_store'
8
8
  require 'fuzzystringmatch'
9
9
  require 'queryparams'
10
10
 
11
+ require_relative 'helpers'
12
+
11
13
  Bot = Robut # alias
12
14
 
13
15
 
@@ -29,59 +31,10 @@ end
29
31
 
30
32
  class BenderBot
31
33
  include Bot::Plugin
34
+ include Helpers
32
35
 
33
36
  JARO = FuzzyStringMatch::JaroWinkler.create :native
34
37
 
35
- RESOLVED_TRANSITIONS = %w[ 51 ]
36
-
37
- RESOLVED_STATE = /resolve/i
38
-
39
- CLOSED_TRANSITIONS = %w[ 61 71 ]
40
-
41
- CLOSED_STATE = /close/i
42
-
43
- SEVERITIES = {
44
- 1 => '10480',
45
- 2 => '10481',
46
- 3 => '10482',
47
- 4 => '10483',
48
- 5 => '10484'
49
- }
50
-
51
- SHOW_FIELDS = {
52
- 'summary' => 'Summary',
53
- 'description' => 'Description',
54
- 'customfield_11250' => 'Severity',
55
- 'customfield_11251' => 'Impact Started',
56
- 'customfield_11252' => 'Impact Ended',
57
- 'customfield_11253' => 'Reported By',
58
- 'customfield_11254' => 'Services Affected',
59
- 'customfield_11255' => 'Cause',
60
- 'status' => 'Status',
61
- 'created' => 'Created',
62
- 'updated' => 'Updated'
63
- }
64
-
65
-
66
- QUOTES = [
67
- 'Bite my shiny metal ass!',
68
- 'This is the worst kind of discrimination there is: the kind against me!',
69
- 'I guess if you want children beaten, you have to do it yourself.',
70
- "Hahahahaha! Oh wait you're serious. Let me laugh even harder.",
71
- "You know what cheers me up? Other people's misfortune.",
72
- 'Anything less than immortality is a complete waste of time.',
73
- "Blackmail is such an ugly word. I prefer extortion. The 'x' makes it sound cool.",
74
- 'Have you tried turning off the TV, sitting down with your children, and hitting them?',
75
- "You're a pimple on society’s ass and you'll never amount to anything!",
76
- 'Shut up baby, I know it!',
77
- "I'm so embarrassed. I wish everyone else was dead!",
78
- "Afterlife? If I thought I had to live another life, I'd kill myself right now!",
79
- "I'm back baby!",
80
- "LET'S GO ALREADYYYYYY!",
81
- "I don't have emotions and sometimes that makes me very sad",
82
- "Life is hilariously cruel"
83
- ]
84
-
85
38
  COMMANDS = {
86
39
  help: {
87
40
  desc: 'Display this help text',
@@ -134,7 +87,14 @@ class BenderBot
134
87
 
135
88
 
136
89
  def handle room, sender, message
137
- @room_name = @@rooms.select { |r| r.xmpp_jid == room }.first.name
90
+ begin
91
+ @room_name = @@rooms.select { |r| r.xmpp_jid == room }.first.name
92
+ @@hipchat[@room_name].get_room # test
93
+ rescue
94
+ @@hipchat = HipChat::Client.new(@@options.hipchat_token)
95
+ @@rooms = @@hipchat.rooms
96
+ @room_name = @@rooms.select { |r| r.xmpp_jid == room }.first.name
97
+ end
138
98
  @room = room
139
99
  @sender = sender
140
100
  @message = message
@@ -142,13 +102,6 @@ class BenderBot
142
102
  severity_field = SHOW_FIELDS.key 'Severity'
143
103
  severities = Hash.new { |h,k| h[k] = [] }
144
104
 
145
- if message =~ /\/inc\s+(\w+)?\s*/
146
- unless COMMANDS.include? $1.to_sym
147
- reply_html 'Invalid usage', :red
148
- reply_with_help
149
- return
150
- end
151
- end
152
105
 
153
106
  case message
154
107
 
@@ -175,7 +128,7 @@ class BenderBot
175
128
 
176
129
  is = store['incidents'].reverse.map do |i|
177
130
  status = normalize_value i['fields']['status']
178
- unless status =~ /done|complete|closed/i
131
+ unless status =~ /resolved|closed/i
179
132
  '%s (%s - %s) [%s]: %s' % [
180
133
  incident_link(i),
181
134
  short_severity(i['fields'][severity_field]['value']),
@@ -230,7 +183,7 @@ class BenderBot
230
183
  end
231
184
 
232
185
  if severities.empty?
233
- reply_html 'No recent incidents! Woohoo!', :green
186
+ reply_html 'Good news everyone! No open incidents at the moment', :green
234
187
 
235
188
  else
236
189
  is = severities.keys.sort.map do |sev|
@@ -313,6 +266,11 @@ class BenderBot
313
266
  else
314
267
  reply_html 'Sorry, no such incident!', :red
315
268
  end
269
+
270
+
271
+ when /^\s*\/inc/i
272
+ reply_html 'Invalid usage', :red
273
+ reply_with_help
316
274
  end
317
275
 
318
276
 
@@ -346,31 +304,6 @@ private
346
304
 
347
305
 
348
306
 
349
- def refresh_incidents
350
- req_path = '/rest/api/2/search'
351
- req_params = QueryParams.encode \
352
- jql: "project = #{options.jira_project} ORDER BY created ASC, priority DESC",
353
- fields: SHOW_FIELDS.keys.join(','),
354
- startAt: 0,
355
- maxResults: 1_000_000
356
-
357
- uri = URI(options.jira_site + req_path + '?' + req_params)
358
- http = Net::HTTP.new uri.hostname, uri.port
359
-
360
- req = Net::HTTP::Get.new uri
361
- req.basic_auth options.jira_user, options.jira_pass
362
- req['Content-Type'] = 'application/json'
363
- req['Accept'] = 'application/json'
364
-
365
- resp = http.request req
366
- issues = JSON.parse(resp.body)['issues']
367
-
368
- store['incidents'] = issues.map! do |i|
369
- i['num'] = i['key'].split('-', 2).last ; i
370
- end
371
- end
372
-
373
-
374
307
  def file_incident data
375
308
  req_path = '/rest/api/2/issue'
376
309
  uri = URI(options.jira_site + req_path)
@@ -499,62 +432,4 @@ private
499
432
  end
500
433
  end
501
434
 
502
-
503
- def normalize_value val
504
- case val
505
- when Hash
506
- val['name'] || val['value'] || val
507
- when Array
508
- val.map { |v| v['value'] }.join(', ')
509
- when /^\d{4}\-\d{2}\-\d{2}/
510
- '%s (%s)' % [ val, normalize_date(val) ]
511
- else
512
- val
513
- end
514
- end
515
-
516
-
517
- def normalize_date val
518
- Time.parse(val).utc.iso8601(0).sub(/Z$/, 'UTC')
519
- end
520
-
521
-
522
- def friendly_date val
523
- Time.parse(val).strftime('%Y-%m-%d %H:%M %Z')
524
- end
525
-
526
-
527
- def recent_incident? i
528
- it = Time.parse(i['fields']['created'])
529
- Time.now - it < one_day
530
- end
531
-
532
-
533
- def one_day
534
- 24 * 60 * 60 # seconds/day
535
- end
536
-
537
-
538
- def select_incident num, refresh=true
539
- refresh_incidents if refresh
540
- store['incidents'].select { |i| i['num'] == num }.first
541
- end
542
-
543
-
544
- def short_severity s
545
- s.split(' - ', 2).first
546
- end
547
-
548
-
549
- def incident_url incident
550
- options.jira_site + '/browse/' + incident['key']
551
- end
552
-
553
- def incident_link incident
554
- '<a href="%s">%s</a>' % [
555
- incident_url(incident),
556
- incident['key']
557
- ]
558
- end
559
-
560
435
  end
@@ -0,0 +1,136 @@
1
+ module Helpers
2
+
3
+ RESOLVED_TRANSITIONS = %w[ 51 ]
4
+
5
+ RESOLVED_STATE = /resolve/i
6
+
7
+ CLOSED_TRANSITIONS = %w[ 61 71 ]
8
+
9
+ CLOSED_STATE = /close/i
10
+
11
+ SEVERITIES = {
12
+ 1 => '10480',
13
+ 2 => '10481',
14
+ 3 => '10482',
15
+ 4 => '10483',
16
+ 5 => '10484'
17
+ }
18
+
19
+ SHOW_FIELDS = {
20
+ 'summary' => 'Summary',
21
+ 'description' => 'Description',
22
+ 'customfield_11250' => 'Severity',
23
+ 'customfield_11251' => 'Impact Started',
24
+ 'customfield_11252' => 'Impact Ended',
25
+ 'customfield_11253' => 'Reported By',
26
+ 'customfield_11254' => 'Services Affected',
27
+ 'customfield_11255' => 'Cause',
28
+ 'status' => 'Status',
29
+ 'created' => 'Created',
30
+ 'updated' => 'Updated'
31
+ }
32
+
33
+
34
+ QUOTES = [
35
+ 'Bite my shiny metal ass!',
36
+ 'This is the worst kind of discrimination there is: the kind against me!',
37
+ 'I guess if you want children beaten, you have to do it yourself.',
38
+ "Hahahahaha! Oh wait you're serious. Let me laugh even harder.",
39
+ "You know what cheers me up? Other people's misfortune.",
40
+ 'Anything less than immortality is a complete waste of time.',
41
+ "Blackmail is such an ugly word. I prefer extortion. The 'x' makes it sound cool.",
42
+ 'Have you tried turning off the TV, sitting down with your children, and hitting them?',
43
+ "You're a pimple on society’s ass and you'll never amount to anything!",
44
+ 'Shut up baby, I know it!',
45
+ "I'm so embarrassed. I wish everyone else was dead!",
46
+ "Afterlife? If I thought I had to live another life, I'd kill myself right now!",
47
+ "I'm back baby!",
48
+ "LET'S GO ALREADYYYYYY!",
49
+ "I don't have emotions and sometimes that makes me very sad",
50
+ "Life is hilariously cruel"
51
+ ]
52
+
53
+
54
+ def refresh_incidents bot=self
55
+ req_path = '/rest/api/2/search'
56
+ req_params = QueryParams.encode \
57
+ jql: "project = #{options.jira_project} ORDER BY created ASC, priority DESC",
58
+ fields: SHOW_FIELDS.keys.join(','),
59
+ startAt: 0,
60
+ maxResults: 1_000_000
61
+
62
+ uri = URI(options.jira_site + req_path + '?' + req_params)
63
+ http = Net::HTTP.new uri.hostname, uri.port
64
+
65
+ req = Net::HTTP::Get.new uri
66
+ req.basic_auth options.jira_user, options.jira_pass
67
+ req['Content-Type'] = 'application/json'
68
+ req['Accept'] = 'application/json'
69
+
70
+ resp = http.request req
71
+ issues = JSON.parse(resp.body)['issues']
72
+
73
+ bot.store['incidents'] = issues.map! do |i|
74
+ i['num'] = i['key'].split('-', 2).last ; i
75
+ end
76
+ end
77
+
78
+
79
+ def normalize_value val
80
+ case val
81
+ when Hash
82
+ val['name'] || val['value'] || val
83
+ when Array
84
+ val.map { |v| v['value'] }.join(', ')
85
+ when /^\d{4}\-\d{2}\-\d{2}/
86
+ '%s (%s)' % [ val, normalize_date(val) ]
87
+ else
88
+ val
89
+ end
90
+ end
91
+
92
+
93
+ def normalize_date val
94
+ Time.parse(val).utc.iso8601(0).sub(/Z$/, 'UTC')
95
+ end
96
+
97
+
98
+ def friendly_date val
99
+ Time.parse(val).strftime('%Y-%m-%d %H:%M %Z')
100
+ end
101
+
102
+
103
+ def recent_incident? i
104
+ it = Time.parse(i['fields']['created'])
105
+ Time.now - it < one_day
106
+ end
107
+
108
+
109
+ def one_day
110
+ 24 * 60 * 60 # seconds/day
111
+ end
112
+
113
+
114
+ def select_incident num, refresh=true
115
+ refresh_incidents if refresh
116
+ store['incidents'].select { |i| i['num'] == num }.first
117
+ end
118
+
119
+
120
+ def short_severity s
121
+ s.split(' - ', 2).first
122
+ end
123
+
124
+
125
+ def incident_url incident
126
+ options.jira_site + '/browse/' + incident['key']
127
+ end
128
+
129
+ def incident_link incident
130
+ '<a href="%s">%s</a>' % [
131
+ incident_url(incident),
132
+ incident['key']
133
+ ]
134
+ end
135
+
136
+ end
data/lib/bender/main.rb CHANGED
@@ -6,12 +6,16 @@ require 'queryparams'
6
6
 
7
7
  require_relative 'metadata'
8
8
  require_relative 'mjolnir'
9
+ require_relative 'helpers'
9
10
  require_relative 'web'
10
11
  require_relative 'bot'
11
12
 
13
+ Thread.abort_on_exception = true
14
+
12
15
 
13
16
  module Bender
14
17
  class Main < Mjolnir
18
+ include Helpers
15
19
 
16
20
  desc 'version', 'Echo the application version'
17
21
  def version
@@ -47,6 +51,16 @@ module Bender
47
51
  aliases: %w[ -t ],
48
52
  desc: 'Set HipChat v1 API token',
49
53
  required: true
54
+ option :hipchat_v2_token, \
55
+ type: :string,
56
+ aliases: %w[ -c ],
57
+ desc: 'Set HipChat v2 API token',
58
+ required: true
59
+ option :primary_room_id, \
60
+ type: :string,
61
+ aliases: %w[ -i ],
62
+ desc: 'Set HipChat primary room ID',
63
+ required: true
50
64
  option :jid, \
51
65
  type: :string,
52
66
  aliases: %w[ -j ],
@@ -77,11 +91,6 @@ module Bender
77
91
  aliases: %w[ -d ],
78
92
  desc: 'Set path to application database',
79
93
  required: true
80
- option :rooms, \
81
- type: :string,
82
- aliases: %w[ -r ],
83
- desc: 'Set HipChat rooms (comma-separated)',
84
- required: true
85
94
  option :jira_user, \
86
95
  type: :string,
87
96
  aliases: %w[ -U ],
@@ -107,15 +116,21 @@ module Bender
107
116
  aliases: %w[ -T ],
108
117
  desc: 'Set JIRA issue type',
109
118
  required: true
110
- option :refresh, \
119
+ option :user_refresh, \
111
120
  type: :numeric,
112
121
  aliases: %w[ -R ],
113
- desc: 'Set JIRA refresh rate',
122
+ desc: 'Set JIRA user refresh rate',
114
123
  default: 300
124
+ option :issue_refresh, \
125
+ type: :numeric,
126
+ aliases: %w[ -S ],
127
+ desc: 'Set JIRA issue refresh rate',
128
+ default: 5
115
129
  include_common_options
116
130
  def start
117
131
  bot = start_bot
118
- refresh_users bot
132
+ periodically_refresh_users bot
133
+ periodically_refresh_incidents bot
119
134
  serve_web bot
120
135
  end
121
136
 
@@ -141,7 +156,78 @@ module Bender
141
156
  end
142
157
 
143
158
 
144
- def refresh_users bot
159
+ def set_room_name_and_topic room_id, incidents, hipchat, bot
160
+ room = hipchat[room_id]
161
+ new_room = room.get_room
162
+
163
+ open_incidents = incidents.select do |i|
164
+ status = normalize_value i['fields']['status']
165
+ !(status =~ /resolved|closed/i)
166
+ end
167
+
168
+ @room_name ||= bot.store['primary_room_name'] || new_room['name']
169
+ @room_topic ||= bot.store['primary_room_topic'] || new_room['topic']
170
+
171
+ @open = false unless defined? @open
172
+
173
+ log.info \
174
+ primary_room_name: bot.store['primary_room_name'],
175
+ primary_room_topic: bot.store['primary_room_topic'],
176
+ new_room_name: new_room['name'],
177
+ new_room_topic: new_room['topic'],
178
+ room_name: @room_name,
179
+ room_topic: @room_topic,
180
+ open: @open
181
+
182
+ if open_incidents.empty?
183
+ if @open
184
+ new_room['name'] = @room_name
185
+ new_room['topic'] = @room_topic
186
+ room.update_room(new_room)
187
+ end
188
+
189
+ @open = false
190
+
191
+ else
192
+ unless @open
193
+ @room_name = new_room['name']
194
+ @room_topic = new_room['topic']
195
+ bot.store['primary_room_name'] = @room_name
196
+ bot.store['primary_room_topic'] = @room_topic
197
+ new_room['name'] = 'DANGER WILL ROBINSON'
198
+ new_room['topic'] = '%d open incicents!' % open_incidents.size
199
+ room.update_room(new_room)
200
+ end
201
+
202
+ @open = true
203
+ end
204
+ end
205
+
206
+
207
+ def periodically_refresh_incidents bot
208
+ Thread.new do
209
+ hipchat_v2 = HipChat::Client.new \
210
+ options.hipchat_v2_token, api_version: 'v2'
211
+
212
+ hipchat_v1 = HipChat::Client.new \
213
+ options.hipchat_token, api_version: 'v1'
214
+
215
+ room_id = options.primary_room_id
216
+
217
+ loop do
218
+ is = refresh_incidents bot
219
+ begin
220
+ set_room_name_and_topic room_id, is, hipchat_v2, bot
221
+ sleep options.issue_refresh
222
+ rescue NoMethodError
223
+ sleep 1
224
+ end
225
+ end
226
+ end
227
+ end
228
+
229
+
230
+ def periodically_refresh_users bot
145
231
  req_path = '/rest/api/2/user/assignable/search'
146
232
  req_params = QueryParams.encode \
147
233
  project: options.jira_project,
@@ -169,7 +255,7 @@ module Bender
169
255
 
170
256
  bot.store['users'] = users
171
257
 
172
- sleep options.refresh
258
+ sleep options.user_refresh
173
259
  end
174
260
  end
175
261
  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.2.4
4
+ version: 0.3.0
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-26 00:00:00.000000000 Z
11
+ date: 2015-08-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -177,6 +177,7 @@ files:
177
177
  - bin/bender
178
178
  - lib/bender.rb
179
179
  - lib/bender/bot.rb
180
+ - lib/bender/helpers.rb
180
181
  - lib/bender/main.rb
181
182
  - lib/bender/metadata.rb
182
183
  - lib/bender/mjolnir.rb