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 +4 -4
- data/Readme.md +53 -1
- data/VERSION +1 -1
- data/lib/bender/bot.rb +30 -26
- data/lib/bender/helpers.rb +30 -3
- data/lib/bender/main.rb +25 -28
- data/test/reports/TEST-TestBender.xml +8 -0
- data/test/test_bender.rb +19 -0
- data/test/test_helper.rb +7 -0
- metadata +9 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ba655e7af625e66dc7c4b9ddea408d69d9c87061
|
4
|
+
data.tar.gz: 63f6f815bd1b6bb72873b0caebe2c2b7f6c612be
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 61b6aefef6785b768ec92b534b962263dd02f2a3e80b167d97c2e89c23b2fba88149af9efd594e3f7c1bed72a785a24cd61e380f34aebe8848561648e5856538
|
7
|
+
data.tar.gz: 21af57fe2c566d1978817b2e41c0a97400f50854c0d7f651a8d1fa3bda00901ddbb9d46184275ff9246604f613d11fa3c995e9c8797a947650bb2de1c2e00daf
|
data/Readme.md
CHANGED
@@ -1,3 +1,55 @@
|
|
1
1
|
# Bender 
|
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.
|
1
|
+
0.6.1
|
data/lib/bender/bot.rb
CHANGED
@@ -36,8 +36,8 @@ class BenderBot
|
|
36
36
|
|
37
37
|
JARO = FuzzyStringMatch::JaroWinkler.create :native
|
38
38
|
|
39
|
-
def self.set_commands
|
40
|
-
|
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
|
-
|
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
|
129
|
+
when /^\/#{config[:jira_user]}$/
|
129
130
|
reply_html QUOTES.sample(1).first, :red
|
130
131
|
|
131
|
-
when
|
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
|
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
|
-
# /
|
150
|
-
when
|
150
|
+
# /prefix help - This help text
|
151
|
+
when /^(\?#{prefix}|\/#{prefix}\s+help)$/
|
151
152
|
reply_with_help
|
152
153
|
|
153
|
-
# /
|
154
|
-
when
|
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
|
-
# /
|
177
|
-
when
|
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
|
-
# /
|
226
|
-
when
|
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
|
-
# /
|
251
|
-
when
|
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
|
-
# /
|
264
|
-
when
|
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
|
-
# /
|
277
|
-
when
|
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
|
-
|
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
|
-
# /
|
299
|
-
when
|
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
|
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
|
data/lib/bender/helpers.rb
CHANGED
@@ -16,10 +16,37 @@ module Helpers
|
|
16
16
|
jira_group: nil,
|
17
17
|
jira_type: nil,
|
18
18
|
user_refresh: 300,
|
19
|
-
issue_refresh:
|
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
|
|
data/lib/bender/main.rb
CHANGED
@@ -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,
|
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,
|
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,
|
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,
|
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
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
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
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
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,
|
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
|
-
|
128
|
-
|
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>
|
data/test/test_bender.rb
ADDED
@@ -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
|
data/test/test_helper.rb
ADDED
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.
|
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-
|
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
|