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 +4 -4
- data/VERSION +1 -1
- data/lib/bender/bot.rb +18 -143
- data/lib/bender/helpers.rb +136 -0
- data/lib/bender/main.rb +96 -10
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 50e516ad8b6f747483302fc79ab811a8cccf2368
|
4
|
+
data.tar.gz: bd931995c92e814c96bbcea6f305534c2941cede
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b43003dffdf9a21bb11877116e287e04b80a84618a3d2743b7a83a030025d7c0303d6842a5d478c048c49680e9a0955576de7497ae0275e08baa26540701abe5
|
7
|
+
data.tar.gz: 23de30730b6a02d26c1e71603966bb6811ef7ef4a7037c978f4dc69e75e6d3afe4abccafdd8d4eae6c9afd0d40eae674d3a47f470e52a8cffc56d14e1d9c07e9
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
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
|
-
|
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 =~ /
|
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
|
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 :
|
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
|
-
|
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
|
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.
|
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.
|
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-
|
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
|