UNPKG

7.71 kBtext/coffeescriptView Raw
1_ = require "underscore"
2
3Config = require "../config"
4Jira = require "../jira"
5Utils = require "../utils"
6GenericAdapter = require "./generic"
7
8class Slack extends GenericAdapter
9 constructor: (@robot) ->
10 super @robot
11 @queue = {}
12
13 @robot.router.post "/hubot/slack-events", (req, res) =>
14 try
15 payload = JSON.parse req.body.payload
16 return unless payload.token is Config.slack.verification.token
17 return @robot.emit "SlackEvents", payload, res unless @shouldJiraBotHandle payload
18 catch e
19 @robot.logger.debug e
20 Utils.Stats.increment "jirabot.webhook.failed"
21 return
22
23 @onButtonActions(payload).then ->
24 res.json payload.original_message
25 .catch (error) ->
26 @robot.logger.error error
27
28 getRoom: (context) ->
29 context = @normalizeContext context
30 room = @robot.adapter.client.rtm.dataStore.getChannelOrGroupByName context.message.room
31 room = @robot.adapter.client.rtm.dataStore.getChannelGroupOrDMById context.message.room unless room
32 room
33
34 getUsers: ->
35 @robot.adapter.client.rtm.dataStore.users
36
37 send: (context, message) ->
38 payload = text: ""
39 room = @getRoom context
40 return unless room
41
42 if _(message).isString()
43 payload.text = message
44 else
45 payload = _(payload).chain().extend(message).pick("text", "attachments").value()
46
47 if Config.slack.verification.token and payload.attachments?.length > 0
48 attachments = []
49 for a in payload.attachments
50 attachments.push a
51 attachments.push @buttonAttachmentsForState "mention", a if a and a.type is "JiraTicketAttachment"
52 payload.attachments = attachments
53
54 payload.text = " " if payload.attachments?.length > 0 and payload.text.length is 0
55 if payload.text.length > 0
56 @robot.adapter.send
57 room: room.id
58 message: thread_ts: context.message.thread_ts
59 , payload
60
61 shouldJiraBotHandle: (context) ->
62 id = context.callback_id
63 matches = id.match Config.ticket.regexGlobal
64
65 if matches and matches[0]
66 return yes
67 else if ~id.indexOf "JiraBotDuplicate"
68 return yes
69 else
70 return no
71
72 onButtonActions: (payload) ->
73 Promise.all payload.actions.map (action) => @handleButtonAction payload, action
74
75 handleDuplicateReponse: (payload, action) ->
76 id = payload.callback_id.split(":")[1]
77 msg = payload.original_message
78 msg.attachments.pop()
79 if item = @queue[id]
80 clearTimeout item.timer
81 delete @queue[id]
82
83 if action.name is "create" and action.value is "yes"
84 msg.attachments.push text: "Creating ticket..."
85 item.action()
86
87 if action.name is "create" and action.value is "no"
88 msg.attachments.push text: "Ticket creation has been cancelled"
89 Utils.Stats.increment "jirabot.slack.button.duplicate.#{action.name}.#{action.value}"
90
91 return Promise.resolve()
92
93 handleButtonAction: (payload, action) ->
94 key = payload.callback_id
95 return @handleDuplicateReponse payload, action if ~key.indexOf "JiraBotDuplicate"
96
97 return new Promise (resolve, reject) =>
98 key = payload.callback_id
99 user = payload.user
100 msg = payload.original_message
101 envelope = message: user: user
102
103 switch action.name
104 when "rank"
105 Jira.Rank.forTicketKeyByDirection key, "up", envelope, no, no
106 msg.attachments.push
107 text: "<@#{user.id}> ranked this ticket to the top"
108 resolve()
109 when "watch"
110 Jira.Create.fromKey(key)
111 .then (ticket) =>
112 watchers = Utils.lookupChatUsersWithJira ticket.watchers
113 if _(watchers).findWhere(id: user.id)
114 msg.attachments.push
115 text: "<@#{user.id}> has stopped watching this ticket"
116 Jira.Watch.forTicketKeyRemovePerson key, null, envelope, no, no
117 else
118 msg.attachments.push
119 text: "<@#{user.id}> is now watching this ticket"
120 Jira.Watch.forTicketKeyForPerson key, user.name, envelope, no, no, no
121 resolve()
122 when "assign"
123 Jira.Create.fromKey(key)
124 .then (ticket) =>
125 assignee = Utils.lookupChatUserWithJira ticket.fields.assignee
126 if assignee and assignee.id is user.id
127 Jira.Assign.forTicketKeyToUnassigned key, envelope, no, no
128 msg.attachments.push
129 text: "<@#{user.id}> has unassigned themself"
130 else
131 Jira.Assign.forTicketKeyToPerson key, user.name, envelope, no, no
132 msg.attachments.push
133 text: "<@#{user.id}> is now assigned to this ticket"
134 resolve()
135 else
136 result = Utils.fuzzyFind action.value, Config.maps.transitions, ['jira']
137 if result
138 msg.attachments.push @buttonAttachmentsForState action.name,
139 key: key
140 text: "<@#{user.id}> transitioned this ticket to #{result.jira}"
141 Jira.Transition.forTicketKeyToState key, result.name, envelope, no, no
142 else
143 msg.attachments.push
144 text: "Unable to to process #{action.name}"
145 resolve()
146 Utils.Stats.increment "jirabot.slack.button.#{action.name}"
147
148 getPermalink: (context) ->
149 team = _(context.robot.adapter.client.rtm.dataStore.teams).pairs()
150 if domain = team[0]?[1]?.domain
151 "https://#{domain}.slack.com/archives/#{context.message.room}/p#{context.message.id.replace '.', ''}"
152 else
153 ""
154
155 buttonAttachmentsForState: (state="mention", details) ->
156 key = details.author_name or details.key
157 return {} unless key and key.length > 0
158 project = key.split("-")[0]
159 return {} unless project
160 buttons = Config.slack.buttons
161 return {} unless buttons
162 map = Config.slack.project.button.state.map[project] or Config.slack.project.button.state.map.default
163 return {} unless map
164 actions = []
165 actions.push buttons[button] for button in map[state] if map[state]
166
167 fallback: "Unable to display quick action buttons"
168 attachment_type: "default"
169 callback_id: key
170 color: details.color
171 actions: actions
172 text: details.text
173
174 detectForDuplicates: (project, type, summary, context) ->
175 original = summary
176 create = -> Jira.Create.with project, type, original, context
177 { summary } = Utils.extract.all summary
178
179 Jira.Search.withQueryForProject(summary, project, context, 20)
180 .then (results) =>
181 if duplicate = Utils.detectPossibleDuplicate summary, results.tickets
182 now = Date.now()
183 @queue[now] =
184 timer: setTimeout =>
185 create()
186 delete @queue[now]
187 , Config.duplicates.timeout
188 action: create
189
190 attachments = [ duplicate.toAttachment no ]
191 attachments.push
192 fallback: "Unable to display quick action buttons"
193 attachment_type: "default"
194 callback_id: "JiraBotDuplicate:#{now}"
195 text: """
196 There are potential duplicates of this issue.
197 If you do not respond, the ticket will be created in #{Config.duplicates.timeout/1000} seconds
198
199 What would you like to do?
200 """
201 actions: [
202 name: "create"
203 text: "Create anyways"
204 style: "primary"
205 type: "button"
206 value: "yes"
207 ,
208 name: "create"
209 text: "Do not create"
210 style: "danger"
211 type: "button"
212 value: "no"
213 ]
214
215 @send context,
216 text: results.text
217 attachments: attachments
218 , no
219 else
220 create()
221 .catch ->
222 create()
223
224module.exports = Slack