bender-bot 0.6.0 → 0.6.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.
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