##// END OF EJS Templates
slack: add log messages for successful events
dan -
r414:b06eab49 default
parent child Browse files
Show More
@@ -1,199 +1,202 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2012-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 from __future__ import unicode_literals
22 22
23 23 import re
24 24 import logging
25 25 import requests
26 26 import colander
27 27 from celery.task import task
28 28 from mako.template import Template
29 29
30 30 from rhodecode import events
31 31 from rhodecode.translation import lazy_ugettext
32 32 from rhodecode.lib import helpers as h
33 33 from rhodecode.lib.celerylib import run_task
34 34 from rhodecode.lib.colander_utils import strip_whitespace
35 35 from rhodecode.integrations.types.base import IntegrationTypeBase
36 36 from rhodecode.integrations.schema import IntegrationSettingsSchemaBase
37 37
38 38 log = logging.getLogger()
39 39
40 40
41 41 class SlackSettingsSchema(IntegrationSettingsSchemaBase):
42 42 service = colander.SchemaNode(
43 43 colander.String(),
44 44 title=lazy_ugettext('Slack service URL'),
45 45 description=h.literal(lazy_ugettext(
46 46 'This can be setup at the '
47 47 '<a href="https://my.slack.com/services/new/incoming-webhook/">'
48 48 'slack app manager</a>')),
49 49 default='',
50 50 placeholder='https://hooks.slack.com/services/...',
51 51 preparer=strip_whitespace,
52 52 validator=colander.url,
53 53 widget='string'
54 54 )
55 55 username = colander.SchemaNode(
56 56 colander.String(),
57 57 title=lazy_ugettext('Username'),
58 58 description=lazy_ugettext('Username to show notifications coming from.'),
59 59 missing='Rhodecode',
60 60 preparer=strip_whitespace,
61 61 widget='string',
62 62 placeholder='Rhodecode'
63 63 )
64 64 channel = colander.SchemaNode(
65 65 colander.String(),
66 66 title=lazy_ugettext('Channel'),
67 67 description=lazy_ugettext('Channel to send notifications to.'),
68 68 missing='',
69 69 preparer=strip_whitespace,
70 70 widget='string',
71 71 placeholder='#general'
72 72 )
73 73 icon_emoji = colander.SchemaNode(
74 74 colander.String(),
75 75 title=lazy_ugettext('Emoji'),
76 76 description=lazy_ugettext('Emoji to use eg. :studio_microphone:'),
77 77 missing='',
78 78 preparer=strip_whitespace,
79 79 widget='string',
80 80 placeholder=':studio_microphone:'
81 81 )
82 82
83 83
84 84 repo_push_template = Template(r'''
85 85 *${data['actor']['username']}* pushed to \
86 86 %if data['push']['branches']:
87 87 ${len(data['push']['branches']) > 1 and 'branches' or 'branch'} \
88 88 ${', '.join('<%s|%s>' % (branch['url'], branch['name']) for branch in data['push']['branches'])} \
89 89 %else:
90 90 unknown branch \
91 91 %endif
92 92 in <${data['repo']['url']}|${data['repo']['repo_name']}>
93 93 >>>
94 94 %for commit in data['push']['commits']:
95 95 <${commit['url']}|${commit['short_id']}> - ${commit['message_html']|html_to_slack_links}
96 96 %endfor
97 97 ''')
98 98
99 99
100 100 class SlackIntegrationType(IntegrationTypeBase):
101 101 key = 'slack'
102 102 display_name = lazy_ugettext('Slack')
103 103 SettingsSchema = SlackSettingsSchema
104 104 valid_events = [
105 105 events.PullRequestCloseEvent,
106 106 events.PullRequestMergeEvent,
107 107 events.PullRequestUpdateEvent,
108 108 events.PullRequestReviewEvent,
109 109 events.PullRequestCreateEvent,
110 110 events.RepoPushEvent,
111 111 events.RepoCreateEvent,
112 112 ]
113 113
114 114 def send_event(self, event):
115 115 if event.__class__ not in self.valid_events:
116 116 log.debug('event not valid: %r' % event)
117 117 return
118 118
119 119 if event.name not in self.settings['events']:
120 120 log.debug('event ignored: %r' % event)
121 121 return
122 122
123 123 data = event.as_dict()
124 124
125 125 text = '*%s* caused a *%s* event' % (
126 126 data['actor']['username'], event.name)
127 127
128 log.debug('handling slack event for %s' % event.name)
129
128 130 if isinstance(event, events.PullRequestEvent):
129 131 text = self.format_pull_request_event(event, data)
130 132 elif isinstance(event, events.RepoPushEvent):
131 133 text = self.format_repo_push_event(data)
132 134 elif isinstance(event, events.RepoCreateEvent):
133 135 text = self.format_repo_create_event(data)
134 136 else:
135 137 log.error('unhandled event type: %r' % event)
136 138
137 139 run_task(post_text_to_slack, self.settings, text)
138 140
139 141 @classmethod
140 142 def settings_schema(cls):
141 143 schema = SlackSettingsSchema()
142 144 schema.add(colander.SchemaNode(
143 145 colander.Set(),
144 146 widget='checkbox_list',
145 147 choices=sorted([e.name for e in cls.valid_events]),
146 148 description="Events activated for this integration",
147 149 default=[e.name for e in cls.valid_events],
148 150 name='events'
149 151 ))
150 152 return schema
151 153
152 154 def format_pull_request_event(self, event, data):
153 155 action = {
154 156 events.PullRequestCloseEvent: 'closed',
155 157 events.PullRequestMergeEvent: 'merged',
156 158 events.PullRequestUpdateEvent: 'updated',
157 159 events.PullRequestReviewEvent: 'reviewed',
158 160 events.PullRequestCreateEvent: 'created',
159 161 }.get(event.__class__, '<unknown action>')
160 162
161 163 return ('Pull request <{url}|#{number}> ({title}) '
162 164 '{action} by {user}').format(
163 165 user=data['actor']['username'],
164 166 number=data['pullrequest']['pull_request_id'],
165 167 url=data['pullrequest']['url'],
166 168 title=data['pullrequest']['title'],
167 169 action=action
168 170 )
169 171
170 172 def format_repo_push_event(self, data):
171 173 result = repo_push_template.render(
172 174 data=data,
173 175 html_to_slack_links=html_to_slack_links,
174 176 )
175 177 return result
176 178
177 179 def format_repo_create_msg(self, data):
178 180 return '<{}|{}> ({}) repository created by *{}*'.format(
179 181 data['repo']['url'],
180 182 data['repo']['repo_name'],
181 183 data['repo']['repo_type'],
182 184 data['actor']['username'],
183 185 )
184 186
185 187
186 188 def html_to_slack_links(message):
187 189 return re.compile(r'<a .*?href=["\'](.+?)".*?>(.+?)</a>').sub(
188 190 r'<\1|\2>', message)
189 191
190 192
191 193 @task(ignore_result=True)
192 194 def post_text_to_slack(settings, text):
195 log.debug('sending %s to slack %s' % (text, settings['service'])
193 196 resp = requests.post(settings['service'], json={
194 197 "channel": settings.get('channel', ''),
195 198 "username": settings.get('username', 'Rhodecode'),
196 199 "text": text,
197 200 "icon_emoji": settings.get('icon_emoji', ':studio_microphone:')
198 201 })
199 202 resp.raise_for_status() # raise exception on a failed request
General Comments 0
You need to be logged in to leave comments. Login now