This repository has been archived by the owner on Jun 8, 2023. It is now read-only.
/
jira.coffee
230 lines (188 loc) · 6.78 KB
/
jira.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# Description:
# Messing with the JIRA REST API
#
# Dependencies:
# None
#
# Configuration:
# HUBOT_JIRA_URL
# HUBOT_JIRA_USER
# HUBOT_JIRA_PASSWORD
# Optional environment variables:
# HUBOT_JIRA_USE_V2
# HUBOT_JIRA_MAXLIST
# HUBOT_JIRA_ISSUEDELAY
# HUBOT_JIRA_IGNOREUSERS
#
# Commands:
# <Project Key>-<Issue ID> - Displays information about the ticket (if it exists)
# hubot show watchers for <Issue Key> - Shows watchers for the given issue
# hubot search for <JQL> - Search JIRA with JQL
# hubot save filter <JQL> as <name> - Save JQL as filter in the brain
# hubot use filter - Use a filter from the brain
# hubot show filter(s) - Show all filters
# hubot show filter <name> - Show a specific filter
#
# Author:
# codec
class IssueFilters
constructor: (@robot) ->
@cache = []
@robot.brain.on 'loaded', =>
@cache = @robot.brain.data.jqls
add: (filter) ->
@cache.push filter
@robot.brain.data.jqls = @cache
delete: (name) ->
result = []
@cache.forEach (filter) ->
if filter.name isnt name
result.push filter
@cache = result
@robot.brain.data.jqls = @cache
get: (name) ->
result = null
@cache.forEach (filter) ->
if filter.name is name
result = filter
result
all: ->
return @cache
class IssueFilter
constructor: (@name, @jql) ->
return {name: @name, jql: @jql}
# keeps track of recently displayed issues, to prevent spamming
class RecentIssues
constructor: (@maxage) ->
@issues = []
cleanup: ->
for issue,time of @issues
age = Math.round(((new Date()).getTime() - time) / 1000)
if age > @maxage
#console.log 'removing old issue', issue
delete @issues[issue]
0
contains: (issue) ->
@cleanup()
@issues[issue]?
add: (issue,time) ->
time = time || (new Date()).getTime()
@issues[issue] = time
module.exports = (robot) ->
filters = new IssueFilters robot
useV2 = process.env.HUBOT_JIRA_USE_V2 || false
# max number of issues to list during a search
maxlist = process.env.HUBOT_JIRA_MAXLIST || 10
# how long (seconds) to wait between repeating the same JIRA issue link
issuedelay = process.env.HUBOT_JIRA_ISSUEDELAY || 30
# array of users that are ignored
ignoredusers = (process.env.HUBOT_JIRA_IGNOREUSERS.split(',') if process.env.HUBOT_JIRA_IGNOREUSERS?) || []
recentissues = new RecentIssues issuedelay
get = (msg, where, cb) ->
console.log(process.env.HUBOT_JIRA_URL + "/rest/api/latest/" + where)
authdata = new Buffer(process.env.HUBOT_JIRA_USER+':'+process.env.HUBOT_JIRA_PASSWORD).toString('base64')
msg.http(process.env.HUBOT_JIRA_URL + "/rest/api/latest/" + where).
header('Authorization', 'Basic ' + authdata).
get() (err, res, body) ->
cb JSON.parse(body)
watchers = (msg, issue, cb) ->
get msg, "issue/#{issue}/watchers", (watchers) ->
if watchers.errors?
return
cb watchers.watchers.map((watcher) -> return watcher.displayName).join(", ")
info = (msg, issue, cb) ->
get msg, "issue/#{issue}", (issues) ->
if issues.errors?
return
if useV2
issue =
key: issues.key
summary: issues.fields.summary
assignee: ->
if issues.fields.assignee != null
issues.fields.assignee.displayName
else
"no assignee"
status: issues.fields.status.name
fixVersion: ->
if issues.fields.fixVersions? and issues.fields.fixVersions.length > 0
issues.fields.fixVersions.map((fixVersion) -> return fixVersion.name).join(", ")
else
"no fix version"
url: process.env.HUBOT_JIRA_URL + '/browse/' + issues.key
else
issue =
key: issues.key
summary: issues.fields.summary.value
assignee: ->
if issues.fields.assignee.value != undefined
issues.fields.assignee.value.displayName
else
"no assignee"
status: issues.fields.status.value.name
fixVersion: ->
if issues.fields.fixVersions? and issues.fields.fixVersions.value != undefined
issues.fields.fixVersions.value.map((fixVersion) -> return fixVersion.name).join(", ")
else
"no fix version"
url: process.env.HUBOT_JIRA_URL + '/browse/' + issues.key
cb "[#{issue.key}] #{issue.summary}. #{issue.assignee()} / #{issue.status}, #{issue.fixVersion()} #{issue.url}"
search = (msg, jql, cb) ->
get msg, "search/?jql=#{escape(jql)}", (result) ->
if result.errors?
return
resultText = "I found #{result.total} issues for your search. #{process.env.HUBOT_JIRA_URL}/secure/IssueNavigator.jspa?reset=true&jqlQuery=#{escape(jql)}"
if result.issues.length <= maxlist
cb resultText
result.issues.forEach (issue) ->
info msg, issue.key, (info) ->
cb info
else
cb resultText + " (too many to list)"
robot.respond /(show )?watchers (for )?(\w+-[0-9]+)/i, (msg) ->
if msg.message.user.id is robot.name
return
watchers msg, msg.match[3], (text) ->
msg.send text
robot.respond /search (for )?(.*)/i, (msg) ->
if msg.message.user.id is robot.name
return
search msg, msg.match[2], (text) ->
msg.reply text
robot.respond /([^\w\-]|^)(\w+-[0-9]+)(?=[^\w]|$)/ig, (msg) ->
if msg.message.user.id is robot.name
return
if (ignoredusers.some (user) -> user == msg.message.user.name)
console.log 'ignoring user due to blacklist:', msg.message.user.name
return
for matched in msg.match
ticket = (matched.match /(\w+-[0-9]+)/)[0]
if !recentissues.contains msg.message.user.room+ticket
info msg, ticket, (text) ->
msg.send text
recentissues.add msg.message.user.room+ticket
robot.respond /save filter (.*) as (.*)/i, (msg) ->
filter = filters.get msg.match[2]
if filter
filters.delete filter.name
msg.reply "Updated filter #{filter.name} for you"
filter = new IssueFilter msg.match[2], msg.match[1]
filters.add filter
robot.respond /delete filter (.*)/i, (msg) ->
filters.delete msg.match[1]
robot.respond /(use )?filter (.*)/i, (msg) ->
name = msg.match[2]
filter = filters.get name
search msg, filter.jql, (text) ->
msg.reply text
robot.respond /(show )?filter(s)? ?(.*)?/i, (msg) ->
if filters.all().length == 0
msg.reply "Sorry, I don't remember any filters."
return
if msg.match[3] == undefined
msg.reply "I remember #{filters.all().length} filters"
filters.all().forEach (filter) ->
msg.reply "#{filter.name}: #{filter.jql}"
else
filter = filters.get msg.match[3]
msg.reply "#{filter.name}: #{filter.jql}"