bender-bot 0.6.0 → 0.6.1

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: 0c7feb7ff451b53acb7ea0171573c05e0658a217
4
- data.tar.gz: f337922a2e8310c05de4ad7ab7e44bb743a22a6a
3
+ metadata.gz: ba655e7af625e66dc7c4b9ddea408d69d9c87061
4
+ data.tar.gz: 63f6f815bd1b6bb72873b0caebe2c2b7f6c612be
5
5
  SHA512:
6
- metadata.gz: 15069ef3e4408d5166023b7031bbbf7e810350687c77738fff83c3cbe8430717a1eb6f7f0095a0c8e47e29e7ae8b92a62059845e89b2d66dbe5416585c07e853
7
- data.tar.gz: 6782aa69d9109eb06b92299f9246a552e80eef81f8cb5f80660ccb12c48fd6fac80d19d27116e362598d04e3af8509db59c48dcf877a7b17176a0a45d37dc3e6
6
+ metadata.gz: 61b6aefef6785b768ec92b534b962263dd02f2a3e80b167d97c2e89c23b2fba88149af9efd594e3f7c1bed72a785a24cd61e380f34aebe8848561648e5856538
7
+ data.tar.gz: 21af57fe2c566d1978817b2e41c0a97400f50854c0d7f651a8d1fa3bda00901ddbb9d46184275ff9246604f613d11fa3c995e9c8797a947650bb2de1c2e00daf
data/Readme.md CHANGED
@@ -1,3 +1,55 @@
1
1
  # Bender ![Version](https://img.shields.io/gem/v/bender-bot.svg?style=flat-square)
2
2
 
3
- Yet another HipChat bot.
3
+ Yet another HipChat bot.
4
+
5
+ Bender hooks into both HipChat and JIRA to report and manage incidents. It uses
6
+ quite a number of different APIs, and the workflow is is pretty rigid, so the
7
+ configuration is quite painful:
8
+
9
+ {
10
+ // NB: You actually can't use comments in your JSON. Such is life.
11
+ "hipchat_token": "asdaf98dfas89fdsa8", // Set HipChat v1 API token
12
+ "hipchat_v2_token": "afiadsaf98fdsa890da908", // Set HipChat v2 API token
13
+ "primary_room_id": "12341234", // Set HipChat primary room ID
14
+ "jid": "1234934_213434@chat.hipchat.com", // Set HipChat JID
15
+ "password": "asd12353521fasfda1243dfs", // Set HipChat password
16
+ "nick": "bender", // Set HipChat nick name (short)
17
+ "mention": "Bender Rodríguez", // Set HipChat mention name (long)
18
+ "rooms": [ "12341_frys_room@chat.hipchat.com" ], // Set HipChat rooms (comma-separated)
19
+ "database": "/var/data/bender.db", // Set path to application database
20
+ "jira_user": "bender, // Set JIRA username
21
+ "jira_pass": "1dkjasdflhkjaiyuwewqmnqew", // Set JIRA password
22
+ "jira_site": "http://jira", // Set JIRA site
23
+ "jira_project": "INC", // Set JIRA project
24
+ "jira_group": "ops", // Set JIRA group for write mode
25
+ "jira_type": "Incident", // Set JIRA issue type
26
+ "user_refresh": 300, // Set JIRA user refresh rate (seconds)
27
+ "issue_refresh": 5, // Set JIRA issue refresh rate (seconds)
28
+ "group_refresh": 60, // Set JIRA group refresh rate (seconds)
29
+ "room_base_name": "Incidents", // Base name for the primary room
30
+ "prefix": "inc", // Command prefix for bot interactions
31
+ "resolved_transitions": [ "51" ], // State IDs for resolution
32
+ "resolved_state": "(?i-mx:resolve)", // State name regex for resolution
33
+ "closed_transitions": [ "61", "71" ], // State IDs for close
34
+ "closed_state": "(?i-mx:close)", // State name regex for close
35
+ "severities": { // Severities (low to high): short number -> ID
36
+ "1": "1234124",
37
+ "2": "1324564",
38
+ "3": "0393103",
39
+ "4": "1092394",
40
+ "5": "1034937"
41
+ },
42
+ "show_fields": { // JIRA fields to show: ID -> human name
43
+ "summary": "Summary",
44
+ "description": "Description",
45
+ "customfield_12259": "Severity",
46
+ "customfield_12251": "Impact Started",
47
+ "customfield_12232": "Impact Ended",
48
+ "customfield_12253": "Reported By",
49
+ "customfield_12354": "Services Affected",
50
+ "customfield_11255": "Cause",
51
+ "status": "Status",
52
+ "created": "Created",
53
+ "updated": "Updated"
54
+ }
55
+ }
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.6.0
1
+ 0.6.1
@@ -36,8 +36,8 @@ class BenderBot
36
36
 
37
37
  JARO = FuzzyStringMatch::JaroWinkler.create :native
38
38
 
39
- def self.set_commands
40
- BenderBot.const_set :COMMANDS, {
39
+ def self.set_commands without
40
+ commands = {
41
41
  help: {
42
42
  desc: 'Display this help text',
43
43
  },
@@ -70,10 +70,11 @@ class BenderBot
70
70
  desc: 'Add a comment to an incident'
71
71
  }
72
72
  }
73
+ without.each { |wo| commands.delete wo.to_sym }
74
+ BenderBot.const_set :COMMANDS, commands
73
75
  end
74
76
 
75
77
 
76
-
77
78
  def reply_html message, color=:yellow
78
79
  tries ||= 3
79
80
  @@hipchat[@this_room.name].send(nick, message, color: color)
@@ -94,7 +95,7 @@ class BenderBot
94
95
  opts[:cmd] = opts[:cmd].to_s.insert(0, ' ') if opts[:cmd]
95
96
  opts[:fmt] ||= ''
96
97
  opts[:fmt] = opts[:fmt].to_s.insert(0, ' ') if opts[:fmt]
97
- '<code>/inc%{cmd}%{fmt}</code> - %{desc}' % opts
98
+ "<code>/#{prefix}%{cmd}%{fmt}</code> - %{desc}" % opts
98
99
  end.join('<br />')
99
100
 
100
101
  reply_html help
@@ -123,12 +124,12 @@ class BenderBot
123
124
  severities = Hash.new { |h,k| h[k] = [] }
124
125
 
125
126
 
126
- case message
127
+ case message.strip
127
128
 
128
- when /^\s*\/bender\s*$/
129
+ when /^\/#{config[:jira_user]}$/
129
130
  reply_html QUOTES.sample(1).first, :red
130
131
 
131
- when /^\s*\/whoami\s*$/
132
+ when /^\/whoami$/
132
133
  u = user_where name: sender
133
134
  if u
134
135
  m = '<b>%{nick}</b>: %{name} (<a href="mailto:%{email}">%{email}</a>)' % u
@@ -137,7 +138,7 @@ class BenderBot
137
138
  reply_html "Couldn't find the associated user in JIRA", :red
138
139
  end
139
140
 
140
- when /^\s*\/lookup\s+(.+)\s*$/
141
+ when /^\/lookup\s+(.+)$/
141
142
  u = user_where(name: $1) || user_where(nick: $1)
142
143
  if u
143
144
  m = '<b>%{nick}</b>: %{name} (<a href="mailto:%{email}">%{email}</a>)' % u
@@ -146,12 +147,12 @@ class BenderBot
146
147
  reply_html "Couldn't find the associated user in JIRA", :red
147
148
  end
148
149
 
149
- # /inc help - This help text
150
- when /^\s*(\?inc|\/inc\s+help)\s*$/
150
+ # /prefix help - This help text
151
+ when /^(\?#{prefix}|\/#{prefix}\s+help)$/
151
152
  reply_with_help
152
153
 
153
- # /inc - List open incidents
154
- when /^\s*\/inc\s*$/
154
+ # /prefix - List open incidents
155
+ when /^\/#{prefix}$/
155
156
  refresh_incidents
156
157
 
157
158
  is = store['incidents'].reverse.map do |i|
@@ -173,8 +174,8 @@ class BenderBot
173
174
  reply_html is
174
175
  end
175
176
 
176
- # /inc summary - Summarize recent incidents
177
- when /^\s*\/inc\s+summary\s*$/
177
+ # /prefix summary - Summarize recent incidents
178
+ when /^\/#{prefix}\s+summary$/
178
179
  refresh_incidents
179
180
 
180
181
  statuses = Hash.new { |h,k| h[k] = 0 }
@@ -222,8 +223,8 @@ class BenderBot
222
223
  end
223
224
 
224
225
 
225
- # /inc NUM - Show incident details
226
- when /^\s*\/inc\s+(\d+)\s*$/
226
+ # /prefix NUM - Show incident details
227
+ when /^\/#{prefix}\s+(\d+)$/
227
228
  incident = select_incident $1
228
229
 
229
230
  if incident.nil?
@@ -247,8 +248,8 @@ class BenderBot
247
248
  ]
248
249
  end
249
250
 
250
- # /inc resolve NUM - Resolve an incident
251
- when /^\s*\/inc\s+resolve\s+(\d+)\s*$/
251
+ # /prefix resolve NUM - Resolve an incident
252
+ when /^\/#{prefix}\s+resolve\s+(\d+)$/
252
253
  unless allowed?
253
254
  reply_html "You're not allowed to do that!", :red
254
255
  else
@@ -260,8 +261,8 @@ class BenderBot
260
261
  end
261
262
  end
262
263
 
263
- # /inc close NUM - Close an incident
264
- when /^\s*\/inc\s+close\s+(\d+)\s*$/
264
+ # /prefix close NUM - Close an incident
265
+ when /^\/#{prefix}\s+close\s+(\d+)$/
265
266
  unless allowed?
266
267
  reply_html "You're not allowed to do that!", :red
267
268
  else
@@ -273,8 +274,8 @@ class BenderBot
273
274
  end
274
275
  end
275
276
 
276
- # /inc open SEVERITY SUMMARY - File a new incident
277
- when /^\s*\/inc\s+open\s+(severity|sev|s|p)?(\d+)\s+(.*)/im
277
+ # /prefix open SEVERITY SUMMARY - File a new incident
278
+ when /^\/#{prefix}\s+open\s+(severity|sev|s|p)?(\d+)\s+(.*)/im
278
279
  unless allowed?
279
280
  reply_html "You're not allowed to do that!", :red
280
281
  else
@@ -285,7 +286,7 @@ class BenderBot
285
286
  issuetype: { name: config[:jira_type] },
286
287
  reporter: { name: user[:nick] },
287
288
  summary: $3,
288
- SHOW_FIELDS.key('Severity') => {
289
+ SEVERITY_FIELD => {
289
290
  id: SEVERITIES[$2.to_i]
290
291
  }
291
292
  }
@@ -295,8 +296,8 @@ class BenderBot
295
296
  end
296
297
 
297
298
 
298
- # /inc comment [INCIDENT_NUMBER] [COMMENT_TEXT]
299
- when /^\s*\/inc\s+comment\s+(\d+)\s+(.*)/im
299
+ # /prefix comment [INCIDENT_NUMBER] [COMMENT_TEXT]
300
+ when /^\/#{prefix}\s+comment\s+(\d+)\s+(.*)/im
300
301
  incident = select_incident $1
301
302
  comment = $2
302
303
  user = user_where name: sender
@@ -310,7 +311,7 @@ class BenderBot
310
311
  end
311
312
 
312
313
 
313
- when /^\s*\/inc/i
314
+ when /^\/#{prefix}/i
314
315
  reply_html 'Invalid usage', :red
315
316
  reply_with_help
316
317
  end
@@ -498,4 +499,7 @@ private
498
499
  user && store['group'].include?(user[:name])
499
500
  end
500
501
 
502
+
503
+ def prefix ; config[:prefix] end
504
+
501
505
  end
@@ -16,10 +16,37 @@ module Helpers
16
16
  jira_group: nil,
17
17
  jira_type: nil,
18
18
  user_refresh: 300,
19
- issue_refresh: 5,
19
+ issue_refresh: 10,
20
20
  group_refresh: 60,
21
21
  room_base_name: nil,
22
- order: false
22
+ order: false,
23
+ prefix: 'inc',
24
+ resolved_transitions: %w[ 51 ],
25
+ resolved_state: /resolve/i.to_s,
26
+ closed_transitions: %w[ 61 71 ],
27
+ closed_state: /close/i.to_s,
28
+ severities: {
29
+ 1 => '10480',
30
+ 2 => '10481',
31
+ 3 => '10482',
32
+ 4 => '10483',
33
+ 5 => '10484'
34
+ },
35
+ show_fields: {
36
+ summary: 'Summary',
37
+ description: 'Description',
38
+ customfield_11250: 'Severity',
39
+ customfield_11251: 'Impact Started',
40
+ customfield_11252: 'Impact Ended',
41
+ customfield_11253: 'Reported By',
42
+ customfield_11254: 'Services Affected',
43
+ customfield_11255: 'Cause',
44
+ status: 'Status',
45
+ created: 'Created',
46
+ updated: 'Updated'
47
+ },
48
+ severity_field: 'customfield_11250',
49
+ without_commands: []
23
50
  }
24
51
 
25
52
  QUOTES = [
@@ -114,7 +141,7 @@ module Helpers
114
141
 
115
142
 
116
143
  def short_severity s
117
- s.split(' - ', 2).first
144
+ s && s.include?(' - ') ? s.split(' - ', 2).first : s
118
145
  end
119
146
 
120
147
 
@@ -41,53 +41,41 @@ module Bender
41
41
  raw_config = JSON.parse File.read(options.config), symbolize_names: true
42
42
  @config = DEFAULT_CONFIG.merge! raw_config
43
43
 
44
- BenderBot.const_set :RESOLVED_TRANSITIONS, %w[ 51 ]
44
+ BenderBot.const_set :RESOLVED_TRANSITIONS, config[:resolved_transitions]
45
45
  Bender.const_set :RESOLVED_TRANSITIONS, BenderBot::RESOLVED_TRANSITIONS
46
46
  Helpers.const_set :RESOLVED_TRANSITIONS, BenderBot::RESOLVED_TRANSITIONS
47
47
 
48
- BenderBot.const_set :RESOLVED_STATE, /resolve/i
48
+ BenderBot.const_set :RESOLVED_STATE, Regexp.new(config[:resolved_state])
49
49
  Bender.const_set :RESOLVED_STATE, BenderBot::RESOLVED_STATE
50
50
  Helpers.const_set :RESOLVED_STATE, BenderBot::RESOLVED_STATE
51
51
 
52
- BenderBot.const_set :CLOSED_TRANSITIONS, %w[ 61 71 ]
52
+ BenderBot.const_set :CLOSED_TRANSITIONS, config[:closed_transitions]
53
53
  Bender.const_set :CLOSED_TRANSITIONS, BenderBot::CLOSED_TRANSITIONS
54
54
  Helpers.const_set :CLOSED_TRANSITIONS, BenderBot::CLOSED_TRANSITIONS
55
55
 
56
- BenderBot.const_set :CLOSED_STATE, /close/i
56
+ BenderBot.const_set :CLOSED_STATE, Regexp.new(config[:closed_state])
57
57
  Bender.const_set :CLOSED_STATE, BenderBot::CLOSED_STATE
58
58
  Helpers.const_set :CLOSED_STATE, BenderBot::CLOSED_STATE
59
59
 
60
- BenderBot.const_set :SEVERITIES, {
61
- 1 => '10480',
62
- 2 => '10481',
63
- 3 => '10482',
64
- 4 => '10483',
65
- 5 => '10484'
66
- }
60
+ # Integerify keys
61
+ severities = config[:severities].inject({}) { |h,(k,v)| h[k.to_s.to_i] = v ; h }
62
+
63
+ BenderBot.const_set :SEVERITIES, severities
67
64
  Bender.const_set :SEVERITIES, BenderBot::SEVERITIES
68
65
  Helpers.const_set :SEVERITIES, BenderBot::SEVERITIES
69
66
 
70
- BenderBot.const_set :SHOW_FIELDS, {
71
- 'summary' => 'Summary',
72
- 'description' => 'Description',
73
- 'customfield_11250' => 'Severity',
74
- 'customfield_11251' => 'Impact Started',
75
- 'customfield_11252' => 'Impact Ended',
76
- 'customfield_11253' => 'Reported By',
77
- 'customfield_11254' => 'Services Affected',
78
- 'customfield_11255' => 'Cause',
79
- 'status' => 'Status',
80
- 'created' => 'Created',
81
- 'updated' => 'Updated'
82
- }
67
+ # Stringify keys
68
+ show_fields = config[:show_fields].inject({}) { |h,(k,v)| h[k.to_s] = v ; h }
69
+
70
+ BenderBot.const_set :SHOW_FIELDS, show_fields
83
71
  Bender.const_set :SHOW_FIELDS, BenderBot::SHOW_FIELDS
84
72
  Helpers.const_set :SHOW_FIELDS, BenderBot::SHOW_FIELDS
85
73
 
86
- BenderBot.const_set :SEVERITY_FIELD, SHOW_FIELDS.key('Severity')
74
+ BenderBot.const_set :SEVERITY_FIELD, config[:severity_field]
87
75
  Bender.const_set :SEVERITY_FIELD, BenderBot::SEVERITY_FIELD
88
76
  Helpers.const_set :SEVERITY_FIELD, BenderBot::SEVERITY_FIELD
89
77
 
90
- BenderBot.set_commands
78
+ BenderBot.set_commands config[:without_commands]
91
79
 
92
80
  bot = start_bot
93
81
  ts = []
@@ -124,8 +112,17 @@ module Bender
124
112
 
125
113
 
126
114
  def set_room_name_and_topic room_id, incidents, hipchat, bot
127
- room = hipchat[room_id]
128
- new_room = room.get_room
115
+ begin
116
+ room = hipchat[room_id]
117
+ new_room = room.get_room
118
+ rescue HipChat::UnknownResponseCode
119
+ log.error \
120
+ error: 'Cannot get room name and topic',
121
+ reason: 'HipChat returned an unknown response code',
122
+ room_id: room_id
123
+ sleep 10
124
+ return
125
+ end
129
126
 
130
127
  if incidents.nil?
131
128
  log.error \
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <testsuite name="TestBender" tests="1" time="0.000726" failures="1" errors="0" skipped="0" assertions="1" timestamp="2016-02-18T11:16:28-08:00">
3
+ <testcase name="test_fails" time="2.5335990358144045e-05" assertions="1">
4
+ <failure type="Minitest::Assertion" message="Failed assertion, no message given.">
5
+ Failed assertion, no message given. (Minitest::Assertion)
6
+ /Users/sclemmer/Projects/bender/test/test_bender.rb:17 </failure>
7
+ </testcase>
8
+ </testsuite>
@@ -0,0 +1,19 @@
1
+ require 'minitest/autorun'
2
+
3
+ require_relative 'test_helper'
4
+ require_relative '../lib/bender'
5
+
6
+ Thread.abort_on_exception = true
7
+
8
+
9
+ class TestBender < MiniTest::Test
10
+ def setup
11
+ end
12
+
13
+ def teardown
14
+ end
15
+
16
+ def test_fails
17
+ assert false
18
+ end
19
+ end
@@ -0,0 +1,7 @@
1
+ require 'simplecov'
2
+ require 'simplecov-cobertura'
3
+ SimpleCov.formatters = [
4
+ SimpleCov::Formatter::HTMLFormatter,
5
+ SimpleCov::Formatter::CoberturaFormatter
6
+ ]
7
+ SimpleCov.start
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.6.0
4
+ version: 0.6.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: 2016-02-17 00:00:00.000000000 Z
11
+ date: 2016-02-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -153,6 +153,9 @@ files:
153
153
  - lib/bender/main.rb
154
154
  - lib/bender/metadata.rb
155
155
  - lib/bender/mjolnir.rb
156
+ - test/reports/TEST-TestBender.xml
157
+ - test/test_bender.rb
158
+ - test/test_helper.rb
156
159
  homepage: https://github.com/sczizzo/bender
157
160
  licenses:
158
161
  - ISC
@@ -177,4 +180,7 @@ rubygems_version: 2.4.5.1
177
180
  signing_key:
178
181
  specification_version: 4
179
182
  summary: Yet another HipChat bot
180
- test_files: []
183
+ test_files:
184
+ - test/reports/TEST-TestBender.xml
185
+ - test/test_bender.rb
186
+ - test/test_helper.rb