├── .gitignore ├── MANIFEST.in ├── src └── sentry_webhooks │ ├── models.py │ ├── __init__.py │ └── plugin.py ├── setup.py └── README.rst /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.egg-info/ 3 | /dist 4 | /build -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include setup.py README.rst MANIFEST.in LICENSE 2 | recursive-include src/sentry_webhooks/templates * 3 | global-exclude *~ 4 | -------------------------------------------------------------------------------- /src/sentry_webhooks/models.py: -------------------------------------------------------------------------------- 1 | """ 2 | sentry_webhooks.models 3 | ~~~~~~~~~~~~~~~~~~~~~~ 4 | 5 | :copyright: (c) 2012 by the Sentry Team, see AUTHORS for more details. 6 | :license: BSD, see LICENSE for more details. 7 | """ 8 | -------------------------------------------------------------------------------- /src/sentry_webhooks/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | sentry_webhooks 3 | ~~~~~~~~~~~~~~~ 4 | 5 | :copyright: (c) 2012 by the Sentry Team, see AUTHORS for more details. 6 | :license: BSD, see LICENSE for more details. 7 | """ 8 | 9 | try: 10 | VERSION = __import__('pkg_resources') \ 11 | .get_distribution('sentry-webhooks').version 12 | except Exception, e: 13 | VERSION = 'unknown' 14 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | sentry-webhooks 4 | =============== 5 | 6 | An extension for Sentry which allows creation various web hooks. 7 | 8 | :copyright: (c) 2012 by the Sentry Team, see AUTHORS for more details. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | from setuptools import setup, find_packages 12 | 13 | 14 | install_requires = [ 15 | # 'sentry>=7.0.0', 16 | ] 17 | 18 | setup( 19 | name='sentry-webhooks', 20 | version='0.3.1', 21 | author='David Cramer', 22 | author_email='dcramer@gmail.com', 23 | url='http://github.com/getsentry/sentry-webhooks', 24 | description='A Sentry extension which integrates web hooks.', 25 | long_description=__doc__, 26 | license='BSD', 27 | package_dir={'': 'src'}, 28 | packages=find_packages('src'), 29 | zip_safe=False, 30 | install_requires=install_requires, 31 | include_package_data=True, 32 | entry_points={ 33 | 'sentry.apps': [ 34 | 'webhooks = sentry_webhooks', 35 | ], 36 | 'sentry.plugins': [ 37 | 'webhooks = sentry_webhooks.plugin:WebHooksPlugin' 38 | ], 39 | }, 40 | classifiers=[ 41 | 'Framework :: Django', 42 | 'Intended Audience :: Developers', 43 | 'Intended Audience :: System Administrators', 44 | 'Operating System :: OS Independent', 45 | 'Topic :: Software Development' 46 | ], 47 | ) 48 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | sentry-webhooks 2 | =============== 3 | 4 | An extension for Sentry that adds support for creating various web hooks. 5 | 6 | **This extension is now bundled as part of Sentry 7.3.0 and is no longer maintained** 7 | 8 | Install 9 | ------- 10 | 11 | Install the package via ``pip``:: 12 | 13 | pip install sentry-webhooks 14 | 15 | You can now configure webhooks via the plugin configuration panel within your project. 16 | 17 | Callback Receivers 18 | ------------------ 19 | 20 | Your callback will receive a POST request whenever there is a new event, with the following data 21 | as JSON: 22 | 23 | :: 24 | 25 | { 26 | "id": "27379932", 27 | "project": "project-slug", 28 | "project_name": "Project Name", 29 | "culprit": "raven.scripts.runner in main", 30 | "level": "error", 31 | "url": "https://app.getsentry.com/getsentry/project-slug/group/27379932/", 32 | "checksum": "c4a4d06bc314205bb3b6bdb612dde7f1", 33 | "logger": "root", 34 | "message": "This is an example Python exception", 35 | "event": { 36 | "extra": {}, 37 | "sentry.interfaces.Stacktrace": { 38 | "frames": [ 39 | { 40 | // stacktrace information 41 | } 42 | ] 43 | }, 44 | "tags": [ 45 | ["foo", "bar"], 46 | ], 47 | "sentry.interfaces.User": { 48 | // user information 49 | }, 50 | "sentry.interfaces.Http": { 51 | // HTTP request information 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/sentry_webhooks/plugin.py: -------------------------------------------------------------------------------- 1 | """ 2 | sentry_webhooks.plugin 3 | ~~~~~~~~~~~~~~~~~~~~~~ 4 | 5 | :copyright: (c) 2012 by the Sentry Team, see AUTHORS for more details. 6 | :license: BSD, see LICENSE for more details. 7 | """ 8 | 9 | import logging 10 | import sentry_webhooks 11 | 12 | from django.conf import settings 13 | from django import forms 14 | from django.utils import simplejson 15 | from django.utils.translation import ugettext_lazy as _ 16 | 17 | from sentry.plugins.bases import notify 18 | from sentry.http import safe_urlopen, is_valid_url 19 | from sentry.utils.safe import safe_execute 20 | 21 | 22 | class WebHooksOptionsForm(notify.NotificationConfigurationForm): 23 | urls = forms.CharField( 24 | label=_('Callback URLs'), 25 | widget=forms.Textarea(attrs={ 26 | 'class': 'span6', 'placeholder': 'https://getsentry.com/callback/url'}), 27 | help_text=_('Enter callback URLs to POST new events to (one per line).')) 28 | 29 | def clean_url(self): 30 | value = self.cleaned_data.get('url') 31 | if not is_valid_url(value): 32 | raise forms.ValidationError('Invalid hostname') 33 | return value 34 | 35 | 36 | class WebHooksPlugin(notify.NotificationPlugin): 37 | author = 'Sentry Team' 38 | author_url = 'https://github.com/getsentry/sentry' 39 | version = sentry_webhooks.VERSION 40 | description = "Integrates web hooks." 41 | resource_links = [ 42 | ('Bug Tracker', 'https://github.com/getsentry/sentry-webhooks/issues'), 43 | ('Source', 'https://github.com/getsentry/sentry-webhooks'), 44 | ] 45 | 46 | slug = 'webhooks' 47 | title = _('WebHooks') 48 | conf_title = title 49 | conf_key = 'webhooks' 50 | project_conf_form = WebHooksOptionsForm 51 | timeout = getattr(settings, 'SENTRY_WEBHOOK_TIMEOUT', 3) 52 | logger = logging.getLogger('sentry.plugins.webhooks') 53 | user_agent = 'sentry-webhooks/%s' % version 54 | 55 | def is_configured(self, project, **kwargs): 56 | return bool(self.get_option('urls', project)) 57 | 58 | def get_group_data(self, group, event): 59 | data = { 60 | 'id': str(group.id), 61 | 'checksum': group.checksum, 62 | 'project': group.project.slug, 63 | 'project_name': group.project.name, 64 | 'logger': group.logger, 65 | 'level': group.get_level_display(), 66 | 'culprit': group.culprit, 67 | 'message': event.message, 68 | 'url': group.get_absolute_url(), 69 | } 70 | data['event'] = dict(event.data or {}) 71 | data['event']['tags'] = event.get_tags() 72 | return data 73 | 74 | def get_webhook_urls(self, project): 75 | urls = self.get_option('urls', project) 76 | if not urls: 77 | return () 78 | return filter(bool, urls.strip().splitlines()) 79 | 80 | def send_webhook(self, url, data): 81 | return safe_urlopen( 82 | url=url, 83 | data=data, 84 | timeout=self.timeout, 85 | user_agent=self.user_agent, 86 | headers=(('Accept-Encoding', 'gzip'), ('Content-type', 'application/json')), 87 | ) 88 | 89 | def notify_users(self, group, event, fail_silently=False): 90 | data = simplejson.dumps(self.get_group_data(group, event)) 91 | for url in self.get_webhook_urls(group.project): 92 | safe_execute(self.send_webhook, url, data) 93 | --------------------------------------------------------------------------------