-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathslack.py
More file actions
228 lines (181 loc) · 9.1 KB
/
slack.py
File metadata and controls
228 lines (181 loc) · 9.1 KB
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
from future.utils import string_types
from twisted.internet import defer
from buildbot import config
from buildbot.process.results import statusToString
from buildbot.reporters import utils
from buildbot.reporters.http import HttpStatusPushBase
from buildbot.util import httpclientservice
from buildbot.util.logger import Logger
import utility as util
log = Logger()
HOSTED_BASE_URL = "https://slack.com"
class SlackStatusPush(HttpStatusPushBase):
name = "SlackStatusPush",
namespace = 'buildbot'
#map buildbot to slack color
BUILD_RESULT = {
'success': '#8d4',
'warnings': 'warning',
'failure': '#ff0000', #red
'skipped': '#AADDEE',
'exception': '#c6c',
'retry': '#ecc',
'cancelled': '#ecc',
'not finished' : 'warning',
'Invalid status': 'warning'
}
def checkConfig(self, auth_token, endpoint=HOSTED_BASE_URL,
builder_room_map=None, builder_user_map=None,
event_messages=None, builder_custom_message_property=None, **kwargs):
if not isinstance(auth_token, string_types):
config.error('auth_token must be a string')
if not isinstance(endpoint, string_types):
config.error('endpoint must be a string')
if builder_room_map and not isinstance(builder_room_map, dict):
config.error('builder_room_map must be a dict')
if builder_user_map and not isinstance(builder_user_map, dict):
config.error('builder_user_map must be a dict')
if builder_custom_message_property and not isinstance(builder_custom_message_property, dict):
config.error('builder_custom_message_property must be a dict')
@defer.inlineCallbacks
def reconfigService(self, auth_token, endpoint="https://slack.com",
builder_room_map=None, builder_user_map=None,
event_messages=None,builder_custom_message_property=None, **kwargs):
auth_token = yield self.renderSecrets(auth_token)
yield HttpStatusPushBase.reconfigService(self, **kwargs)
self._http = yield httpclientservice.HTTPClientService.getService(
self.master, endpoint,
debug=self.debug, verify=self.verify)
token_format = 'Bearer %s' % (auth_token)
self.auth_token = token_format
self.builder_room_map = builder_room_map
self.builder_user_map = builder_user_map
self.builder_custom_message_property = builder_custom_message_property
# returns a Deferred that returns None
def buildStarted(self, key, build):
return self.send(build, key[2])
# returns a Deferred that returns None
def buildFinished(self, key, build):
return self.send(build, key[2])
@defer.inlineCallbacks
def getBuildDetailsAndSendMessage(self, build, key):
yield utils.getDetailsForBuild(self.master, build, **self.neededDetails)
postData = yield self.getRecipientList(build, key)
postData['message'] = yield self.getMessage(build, key)
extra_params = yield self.getExtraParams(build, key)
postData.update(extra_params)
defer.returnValue(postData)
def getRecipientList(self, build, event_name):
result = {}
builder_name = build['builder']['name']
if self.builder_user_map and builder_name in self.builder_user_map:
result['id_or_email'] = self.builder_user_map[builder_name]
if self.builder_room_map and builder_name in self.builder_room_map:
result['room_id_or_name'] = self.builder_room_map[builder_name]
return result
def getCustomMessageProperties(self, build, event_name):
builder_name = build['builder']['name']
if self.builder_custom_message_property and builder_name in self.builder_custom_message_property:
return self.builder_custom_message_property[builder_name]
return None
def getMessage(self, build, event_name):
event_messages = {
'new': 'Buildbot started build %s here: %s' % (build['builder']['name'], build['url']),
'finished': 'Buildbot finished build %s with result %s here: %s'
% (build['builder']['name'], statusToString(build['results']), build['url'])
}
return event_messages.get(event_name, '')
# use this as an extension point to inject extra parameters into your
# postData
@defer.inlineCallbacks
def getExtraParams(self, build, event_name):
result = {}
state_message = build['state_string'] # // build text
build_url = build['url']
builder_name = build['builder']['name']
build_number = build['number']
result['slack_message'] = {
"channel" : "",
"attachments": [
{
"fallback": "%s - %s" % (state_message,build_url),
"text": "<%s|%s # %s> - %s" %(build_url, builder_name, build_number,state_message),
"color": self.BUILD_RESULT[statusToString(build['results'])]
}
]
}
# basic message for new event
if event_name == 'new':
defer.returnValue(result)
# build finished message
build_properties = build.get('properties')
if build_properties is not None:
#warning please use util.GetBuildPropertyValue(build_property, property_name)
build_commit_description = util.GetBuildPropertyValue(build_properties, 'commit-description')
build_worker_name = util.GetBuildPropertyValue(build_properties,'workername') # for environment
#https://api.slack.com/docs/messages#how_to_send_messages
result['slack_message']['attachments'][0]['fields'] = []
message_fields = result['slack_message']['attachments'][0]['fields']
if build_commit_description is not None:
commit_field = {
"title": "Tag",
"value": build_commit_description,
"short": "true"
}
message_fields.append(commit_field)
if build_worker_name is not None:
worker_name_field = {
"title": "Worker",
"value": build_worker_name,
"short": "true"
}
message_fields.append(worker_name_field)
#add custom property
custom_message_properties = yield self.getCustomMessageProperties(build, event_name)
if custom_message_properties is not None:
for custom_property in custom_message_properties:
custom_property_value = util.GetBuildPropertyValue(build_properties,custom_property) # for environment
custom_property_field = {
"title": custom_property,
"value": custom_property_value,
"short": "true"
}
message_fields.append(custom_property_field)
# Add Reponsable Users Field
blamelist = yield utils.getResponsibleUsersForBuild(self.master, build['buildid'])
if len(blamelist) > 0:
blamelist_str = ''.join(blamelist)
blameField = {
"title": "Responsable Users",
"value": blamelist_str,
"short": "true"
}
message_fields.append(blameField)
defer.returnValue(result)
# TODO : send slack message using api
@defer.inlineCallbacks
def send(self, build, key):
postData = yield self.getBuildDetailsAndSendMessage(build, key)
print("print postData object")
util.PrintDict(postData)
if not postData or 'message' not in postData or not postData['message'] :
return
if 'slack_message' not in postData or not postData['slack_message'] :
return
message = postData['slack_message']
postDataSlack = message
postDataSlack["channel"] = postData["room_id_or_name"]
urls = []
urls.append('/api/chat.postMessage')
# if 'id_or_email' in postData:
# urls.append('/v2/user/{}/message'.format(postData.pop('id_or_email')))
# if 'room_id_or_name' in postData:
# urls.append('/v2/room/{}/notification'.format(postData.pop('room_id_or_name')))
#####
# https://slack.com/api/chat.postMessage
for url in urls:
response = yield self._http.post(url, headers={'Authorization': self.auth_token}, json=postDataSlack)
if response.code != 200:
content = yield response.content()
log.error("Slack Reporter {code}: unable to upload status: {content}",
code=response.code, content=content)