##// END OF EJS Templates
licenses: Add tornado license information....
licenses: Add tornado license information. We are using a modified version of some code which originates from the tornado web server project. Therfore we include the original license information in our project to comply to the license.

File last commit:

r443:c2778156 default
r476:16ff155b default
Show More
slack.py
246 lines | 8.6 KiB | text/x-python | PythonLexer
dan
integrations: add integration support...
r411 # -*- coding: utf-8 -*-
# Copyright (C) 2012-2016 RhodeCode GmbH
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
from __future__ import unicode_literals
import re
import logging
import requests
import colander
dan
events: add an event for pull request comments with review status
r443 import textwrap
dan
integrations: add integration support...
r411 from celery.task import task
from mako.template import Template
from rhodecode import events
from rhodecode.translation import lazy_ugettext
from rhodecode.lib import helpers as h
from rhodecode.lib.celerylib import run_task
from rhodecode.lib.colander_utils import strip_whitespace
from rhodecode.integrations.types.base import IntegrationTypeBase
from rhodecode.integrations.schema import IntegrationSettingsSchemaBase
log = logging.getLogger()
class SlackSettingsSchema(IntegrationSettingsSchemaBase):
service = colander.SchemaNode(
colander.String(),
title=lazy_ugettext('Slack service URL'),
description=h.literal(lazy_ugettext(
'This can be setup at the '
'<a href="https://my.slack.com/services/new/incoming-webhook/">'
'slack app manager</a>')),
default='',
placeholder='https://hooks.slack.com/services/...',
preparer=strip_whitespace,
validator=colander.url,
widget='string'
)
username = colander.SchemaNode(
colander.String(),
title=lazy_ugettext('Username'),
description=lazy_ugettext('Username to show notifications coming from.'),
missing='Rhodecode',
preparer=strip_whitespace,
widget='string',
placeholder='Rhodecode'
)
channel = colander.SchemaNode(
colander.String(),
title=lazy_ugettext('Channel'),
description=lazy_ugettext('Channel to send notifications to.'),
missing='',
preparer=strip_whitespace,
widget='string',
placeholder='#general'
)
icon_emoji = colander.SchemaNode(
colander.String(),
title=lazy_ugettext('Emoji'),
description=lazy_ugettext('Emoji to use eg. :studio_microphone:'),
missing='',
preparer=strip_whitespace,
widget='string',
placeholder=':studio_microphone:'
)
repo_push_template = Template(r'''
*${data['actor']['username']}* pushed to \
%if data['push']['branches']:
${len(data['push']['branches']) > 1 and 'branches' or 'branch'} \
${', '.join('<%s|%s>' % (branch['url'], branch['name']) for branch in data['push']['branches'])} \
%else:
unknown branch \
%endif
in <${data['repo']['url']}|${data['repo']['repo_name']}>
>>>
%for commit in data['push']['commits']:
<${commit['url']}|${commit['short_id']}> - ${commit['message_html']|html_to_slack_links}
%endfor
''')
class SlackIntegrationType(IntegrationTypeBase):
key = 'slack'
display_name = lazy_ugettext('Slack')
SettingsSchema = SlackSettingsSchema
valid_events = [
events.PullRequestCloseEvent,
events.PullRequestMergeEvent,
events.PullRequestUpdateEvent,
dan
events: add an event for pull request comments with review status
r443 events.PullRequestCommentEvent,
dan
integrations: add integration support...
r411 events.PullRequestReviewEvent,
events.PullRequestCreateEvent,
events.RepoPushEvent,
events.RepoCreateEvent,
]
def send_event(self, event):
if event.__class__ not in self.valid_events:
log.debug('event not valid: %r' % event)
return
if event.name not in self.settings['events']:
log.debug('event ignored: %r' % event)
return
data = event.as_dict()
text = '*%s* caused a *%s* event' % (
data['actor']['username'], event.name)
dan
slack: add log messages for successful events
r414 log.debug('handling slack event for %s' % event.name)
dan
events: add an event for pull request comments with review status
r443 if isinstance(event, events.PullRequestCommentEvent):
text = self.format_pull_request_comment_event(event, data)
elif isinstance(event, events.PullRequestReviewEvent):
text = self.format_pull_request_review_event(event, data)
elif isinstance(event, events.PullRequestEvent):
dan
integrations: add integration support...
r411 text = self.format_pull_request_event(event, data)
elif isinstance(event, events.RepoPushEvent):
text = self.format_repo_push_event(data)
elif isinstance(event, events.RepoCreateEvent):
text = self.format_repo_create_event(data)
else:
log.error('unhandled event type: %r' % event)
run_task(post_text_to_slack, self.settings, text)
@classmethod
def settings_schema(cls):
schema = SlackSettingsSchema()
schema.add(colander.SchemaNode(
colander.Set(),
widget='checkbox_list',
choices=sorted([e.name for e in cls.valid_events]),
description="Events activated for this integration",
name='events'
))
return schema
dan
events: add an event for pull request comments with review status
r443 def format_pull_request_comment_event(self, event, data):
comment_text = data['comment']['text']
if len(comment_text) > 200:
comment_text = '<{comment_url}|{comment_text}...>'.format(
comment_text=comment_text[:200],
comment_url=data['comment']['url'],
)
comment_status = ''
if data['comment']['status']:
comment_status = '[{}]: '.format(data['comment']['status'])
return (textwrap.dedent(
'''
{user} commented on pull request <{pr_url}|#{number}> - {pr_title}:
>>> {comment_status}{comment_text}
''').format(
comment_status=comment_status,
user=data['actor']['username'],
number=data['pullrequest']['pull_request_id'],
pr_url=data['pullrequest']['url'],
pr_status=data['pullrequest']['status'],
pr_title=data['pullrequest']['title'],
comment_text=comment_text
)
)
def format_pull_request_review_event(self, event, data):
return (textwrap.dedent(
'''
Status changed to {pr_status} for pull request <{pr_url}|#{number}> - {pr_title}
''').format(
user=data['actor']['username'],
number=data['pullrequest']['pull_request_id'],
pr_url=data['pullrequest']['url'],
pr_status=data['pullrequest']['status'],
pr_title=data['pullrequest']['title'],
)
)
dan
integrations: add integration support...
r411 def format_pull_request_event(self, event, data):
action = {
events.PullRequestCloseEvent: 'closed',
events.PullRequestMergeEvent: 'merged',
events.PullRequestUpdateEvent: 'updated',
events.PullRequestCreateEvent: 'created',
dan
events: add an event for pull request comments with review status
r443 }.get(event.__class__, str(event.__class__))
dan
integrations: add integration support...
r411
dan
events: add an event for pull request comments with review status
r443 return ('Pull request <{url}|#{number}> - {title} '
dan
integrations: add integration support...
r411 '{action} by {user}').format(
user=data['actor']['username'],
number=data['pullrequest']['pull_request_id'],
url=data['pullrequest']['url'],
title=data['pullrequest']['title'],
action=action
)
def format_repo_push_event(self, data):
result = repo_push_template.render(
data=data,
html_to_slack_links=html_to_slack_links,
)
return result
dan
slack: fix wrong named function
r417 def format_repo_create_event(self, data):
dan
integrations: add integration support...
r411 return '<{}|{}> ({}) repository created by *{}*'.format(
data['repo']['url'],
data['repo']['repo_name'],
data['repo']['repo_type'],
data['actor']['username'],
)
def html_to_slack_links(message):
return re.compile(r'<a .*?href=["\'](.+?)".*?>(.+?)</a>').sub(
r'<\1|\2>', message)
@task(ignore_result=True)
def post_text_to_slack(settings, text):
slack: fix log typo
r415 log.debug('sending %s to slack %s' % (text, settings['service']))
dan
integrations: add integration support...
r411 resp = requests.post(settings['service'], json={
"channel": settings.get('channel', ''),
"username": settings.get('username', 'Rhodecode'),
"text": text,
"icon_emoji": settings.get('icon_emoji', ':studio_microphone:')
})
slack: fix log typo
r415 resp.raise_for_status() # raise exception on a failed request