diff --git a/endpoints/notificationmethod.py b/endpoints/notificationmethod.py index a6d958037..9650e79f6 100644 --- a/endpoints/notificationmethod.py +++ b/endpoints/notificationmethod.py @@ -5,6 +5,7 @@ import tarfile import base64 import json import requests +import re from flask.ext.mail import Message from app import mail, app, get_app_url @@ -302,3 +303,80 @@ class HipchatMethod(NotificationMethod): return False return True + + +class SlackMethod(NotificationMethod): + """ Method for sending notifications to Slack via the API: + https://api.slack.com/docs/attachments + """ + @classmethod + def method_name(cls): + return 'slack' + + def validate(self, repository, config_data): + if not config_data.get('token', ''): + raise CannotValidateNotificationMethodException('Missing Slack Token') + + if not config_data.get('subdomain', '').isalnum(): + raise CannotValidateNotificationMethodException('Missing Slack Subdomain Name') + + def formatForSlack(self, message): + message = message.replace('\n', '') + message = re.sub(r'\s+', ' ', message) + message = message.replace('
', '\n') + message = re.sub(r'(.+)', '<\\1|\\2>', message) + return message + + def perform(self, notification, event_handler, notification_data): + config_data = json.loads(notification.config_json) + + token = config_data.get('token', '') + subdomain = config_data.get('subdomain', '') + + if not token or not subdomain: + return False + + owner = model.get_user(notification.repository.namespace) + if not owner: + # Something went wrong. + return False + + url = 'https://%s.slack.com/services/hooks/incoming-webhook?token=%s' % (subdomain, token) + + level = event_handler.get_level(notification_data['event_data'], notification_data) + color = { + 'info': '#ffffff', + 'warning': 'warning', + 'error': 'danger', + 'primary': 'good' + }.get(level, '#ffffff') + + summary = event_handler.get_summary(notification_data['event_data'], notification_data) + message = event_handler.get_message(notification_data['event_data'], notification_data) + + headers = {'Content-type': 'application/json'} + payload = { + 'text': summary, + 'username': 'quayiobot', + 'attachments': [ + { + 'fallback': summary, + 'text': self.formatForSlack(message), + 'color': color + } + ] + } + + try: + resp = requests.post(url, data=json.dumps(payload), headers=headers) + if resp.status_code/100 != 2: + logger.error('%s response for Slack to url: %s' % (resp.status_code, + url)) + logger.error(resp.content) + return False + + except requests.exceptions.RequestException as ex: + logger.exception('Slack method was unable to be sent: %s' % ex.message) + return False + + return True diff --git a/initdb.py b/initdb.py index 6b68cee85..7cadbee87 100644 --- a/initdb.py +++ b/initdb.py @@ -253,6 +253,7 @@ def initialize_database(): ExternalNotificationMethod.create(name='flowdock') ExternalNotificationMethod.create(name='hipchat') + ExternalNotificationMethod.create(name='slack') NotificationKind.create(name='repo_push') NotificationKind.create(name='build_queued') diff --git a/static/css/quay.css b/static/css/quay.css index bd8f571e8..2a4696551 100644 --- a/static/css/quay.css +++ b/static/css/quay.css @@ -4573,6 +4573,13 @@ i.hipchat-icon { height: 16px; } +i.slack-icon { + background-image: url(/static/img/slack.ico); + background-size: 16px; + width: 16px; + height: 16px; +} + .external-notification-view-element { margin: 10px; padding: 6px; diff --git a/static/img/slack.ico b/static/img/slack.ico new file mode 100644 index 000000000..d32c25280 Binary files /dev/null and b/static/img/slack.ico differ diff --git a/static/js/app.js b/static/js/app.js index 3394543e7..c272b7662 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -1141,6 +1141,24 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading 'title': 'Notification Token' } ] + }, + { + 'id': 'slack', + 'title': 'Slack Room Notification', + 'icon': 'slack-icon', + 'fields': [ + { + 'name': 'subdomain', + 'type': 'string', + 'title': 'Slack Subdomain' + }, + { + 'name': 'token', + 'type': 'string', + 'title': 'Token', + 'help_url': 'https://{subdomain}.slack.com/services/new/outgoing-webhook' + } + ] } ]; diff --git a/test/data/test.db b/test/data/test.db index a947e5964..ddfe7166b 100644 Binary files a/test/data/test.db and b/test/data/test.db differ