mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-03 21:43:21 +00:00 
			
		
		
		
	Add Slack webhook.
Adds a new webhook integration for Slack to receive messages from one's Slack team's public channels. Contains negative tests for broken, missing or invalid data. Allows two different option for integration: 1. Receive notification on a single stream with different topics for each of Slack's public channels. 2. Receive notification on different streams for each of Slack's public channels. Steps to choose between the two options is described in the documentation. Fixes #3569.
This commit is contained in:
		
							
								
								
									
										
											BIN
										
									
								
								static/images/integrations/logos/slack.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								static/images/integrations/logos/slack.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 25 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/images/integrations/slack/001.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								static/images/integrations/slack/001.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 86 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/images/integrations/slack/002.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								static/images/integrations/slack/002.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 131 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/images/integrations/slack/003.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								static/images/integrations/slack/003.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 106 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/images/integrations/slack/004.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								static/images/integrations/slack/004.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 49 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								static/images/integrations/slack/005.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								static/images/integrations/slack/005.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 97 KiB  | 
@@ -27,6 +27,7 @@ IGNORED_PHRASES = [
 | 
			
		||||
    r"MiB",
 | 
			
		||||
    r"Pivotal",
 | 
			
		||||
    r'REMOTE_USER',
 | 
			
		||||
    r'Slack',
 | 
			
		||||
    r"SSO",
 | 
			
		||||
    r'Terms of Service',
 | 
			
		||||
    r"URL",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								zerver/fixtures/slack/slack_message_info.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								zerver/fixtures/slack/slack_message_info.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
token=aotJImaVOVEVWssawxmegDWt&team_id=T3W8CTX0F&team_domain=gsoc2017&service_id=133126759712&channel_id=C3W8CTZFZ&channel_name=general×tamp=1485947263.000010&user_id=U3W8CTX2T&user_name=slack_user&text=test
 | 
			
		||||
@@ -0,0 +1 @@
 | 
			
		||||
token=aotJImaVOVEVWssawxmegDWt&team_id=T3W8CTX0F&team_domain=gsoc2017&service_id=133126759712&channel_id=C3W8CTZFZ×tamp=1485947263.000010&user_id=U3W8CTX2T&user_name=slack_user&text=test
 | 
			
		||||
@@ -0,0 +1 @@
 | 
			
		||||
token=aotJImaVOVEVWssawxmegDWt&team_id=T3W8CTX0F&team_domain=gsoc2017&service_id=133126759712&channel_id=C3W8CTZFZ&channel_name=general×tamp=1485947263.000010&user_name=slack_user&user_id=U3W8CTX2T
 | 
			
		||||
@@ -0,0 +1 @@
 | 
			
		||||
token=aotJImaVOVEVWssawxmegDWt&team_id=T3W8CTX0F&team_domain=gsoc2017&service_id=133126759712&channel_id=C3W8CTZFZ&channel_name=general×tamp=1485947263.000010&user_id=U3W8CTX2T&text=test
 | 
			
		||||
@@ -151,6 +151,7 @@ WEBHOOK_INTEGRATIONS = [
 | 
			
		||||
    WebhookIntegration('pivotal', display_name='Pivotal Tracker'),
 | 
			
		||||
    WebhookIntegration('semaphore'),
 | 
			
		||||
    WebhookIntegration('sentry'),
 | 
			
		||||
    WebhookIntegration('slack'),
 | 
			
		||||
    WebhookIntegration('solano', display_name='Solano Labs'),
 | 
			
		||||
    WebhookIntegration('splunk', display_name='Splunk'),
 | 
			
		||||
    WebhookIntegration('stash'),
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										0
									
								
								zerver/webhooks/slack/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								zerver/webhooks/slack/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										41
									
								
								zerver/webhooks/slack/doc.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								zerver/webhooks/slack/doc.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
			
		||||
<p>Get notifications from Slack for messages on your team's public channels</p>
 | 
			
		||||
 | 
			
		||||
<p>
 | 
			
		||||
    There are two ways in which you may want to receive a notification:
 | 
			
		||||
    <ul type="disc">
 | 
			
		||||
        <li>Slack Channel to a single Zulip stream with different topics</li>
 | 
			
		||||
        <li>Multiple Zulip streams for multiple Slack channels</li>
 | 
			
		||||
    </ul>
 | 
			
		||||
</p>
 | 
			
		||||
 | 
			
		||||
<p>
 | 
			
		||||
    If you want a single Zulip stream to receive notifications from Slack then first create your desired stream.
 | 
			
		||||
</p>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<p>
 | 
			
		||||
    Go to the following URL: <code>https://api.slack.com</code><br/>
 | 
			
		||||
    Next, under the category of App features click on <b>Legacy custom integrations</b>.<br/>
 | 
			
		||||
    <img class="screenshot" src="/static/images/integrations/slack/001.png"/><br/><br/>
 | 
			
		||||
    Now, click on <b>Outgoing Webhooks</b> under <b>Custom Integrations</b>.<br/>
 | 
			
		||||
    <img class="screenshot" src="/static/images/integrations/slack/002.png"/><br/><br/>
 | 
			
		||||
    Then click the hyperlink <b>outgoing webhook integration</b> which can be found in the page.
 | 
			
		||||
    <img class="screenshot" src="/static/images/integrations/slack/003.png"/><br/><br/>
 | 
			
		||||
    Next, click the <b>Add Outgoing Webhook integration</b> button.
 | 
			
		||||
    <img class="screenshot" src="/static/images/integrations/slack/004.png"/><br/><br/>
 | 
			
		||||
</p><br/>
 | 
			
		||||
<p>
 | 
			
		||||
    Now, under <code> Integration Settings, </code> fill in the channel you'd like to get your notifications from.<br/><br/>
 | 
			
		||||
    Then fill in <code>{{ external_api_uri_subdomain }}/v1/external/slack?api_key=abcdefgh&stream=slack&channels_map_topics=1</code> as your URL.<br/><br/>
 | 
			
		||||
    <img class="screenshot" src="/static/images/integrations/slack/005.png"/><br/><br/>
 | 
			
		||||
    Finally, save your settings.<br/><br/>
 | 
			
		||||
</p>
 | 
			
		||||
<p>
 | 
			
		||||
    In case you want your Slack channels mapped to multiple Zulip streams, you have to
 | 
			
		||||
    change the parameter <code>channels_map_to_topics=1</code> to <code>channels_map_to_topics=0</code> in your URL.
 | 
			
		||||
    The resulting URL will be
 | 
			
		||||
    <code>{{ external_api_uri_subdomain }}/v1/external/slack?api_key=abcdefgh&stream=slack&channels_map_topics=0</code>
 | 
			
		||||
</p>
 | 
			
		||||
<p><b>Congratulations, you're done!</b></p>
 | 
			
		||||
<p><b>This integration is not created by, affiliated with, or supported by Slack Technologies, Inc.</b></p>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										64
									
								
								zerver/webhooks/slack/tests.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								zerver/webhooks/slack/tests.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,64 @@
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
from zerver.lib.test_classes import WebhookTestCase
 | 
			
		||||
from six import text_type
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SlackWebhookTests(WebhookTestCase):
 | 
			
		||||
    STREAM_NAME = 'slack'
 | 
			
		||||
    URL_TEMPLATE = "/api/v1/external/slack?stream={stream}&api_key={api_key}"
 | 
			
		||||
    FIXTURE_DIR_NAME = 'slack'
 | 
			
		||||
 | 
			
		||||
    def test_slack_channel_to_topic(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
 | 
			
		||||
        expected_subject = u"channel: general"
 | 
			
		||||
        expected_message = u"**slack_user**: `test\n`"
 | 
			
		||||
        self.send_and_test_stream_message('message_info', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_slack_channel_to_stream(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
 | 
			
		||||
        self.STREAM_NAME = 'general'
 | 
			
		||||
        self.url = "{}{}".format(self.url, "&channels_map_to_topics=0")
 | 
			
		||||
        expected_subject = u"Message from Slack"
 | 
			
		||||
        expected_message = u"**slack_user**: `test\n`"
 | 
			
		||||
        self.send_and_test_stream_message('message_info', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_missing_data_user_name(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
 | 
			
		||||
        payload = self.get_body('message_info_missing_user_name')
 | 
			
		||||
        url = self.build_webhook_url()
 | 
			
		||||
        result = self.client_post(url, payload, content_type="application/x-www-form-urlencoded")
 | 
			
		||||
        self.assert_json_error(result, "Missing 'user_name' argument")
 | 
			
		||||
 | 
			
		||||
    def test_missing_data_channel_name(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
 | 
			
		||||
        payload = self.get_body('message_info_missing_channel_name')
 | 
			
		||||
        url = self.build_webhook_url()
 | 
			
		||||
        result = self.client_post(url, payload, content_type="application/x-www-form-urlencoded")
 | 
			
		||||
        self.assert_json_error(result, "Missing 'channel_name' argument")
 | 
			
		||||
 | 
			
		||||
    def test_missing_data_text(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
 | 
			
		||||
        payload = self.get_body('message_info_missing_text')
 | 
			
		||||
        url = self.build_webhook_url()
 | 
			
		||||
        result = self.client_post(url, payload, content_type="application/x-www-form-urlencoded")
 | 
			
		||||
        self.assert_json_error(result, "Missing 'text' argument")
 | 
			
		||||
 | 
			
		||||
    def test_invalid_channels_map_to_topics(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
 | 
			
		||||
        payload = self.get_body('message_info')
 | 
			
		||||
        url = "{}{}".format(self.url, "&channels_map_to_topics=abc")
 | 
			
		||||
        result = self.client_post(url, payload, content_type="application/x-www-form-urlencoded")
 | 
			
		||||
        self.assert_json_error(result, 'Error: channels_map_to_topics parameter other than 0 or 1')
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name):
 | 
			
		||||
        # type: (text_type) -> text_type
 | 
			
		||||
 | 
			
		||||
        return self.fixture_data("slack", fixture_name, file_type="txt")
 | 
			
		||||
							
								
								
									
										35
									
								
								zerver/webhooks/slack/view.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								zerver/webhooks/slack/view.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
from __future__ import absolute_import
 | 
			
		||||
from django.utils.translation import ugettext as _
 | 
			
		||||
from django.http import HttpRequest, HttpResponse
 | 
			
		||||
from django.utils.translation import ugettext as _
 | 
			
		||||
from zerver.lib.actions import check_send_message, create_stream_if_needed
 | 
			
		||||
from zerver.lib.response import json_success, json_error
 | 
			
		||||
from zerver.lib.validator import check_string, check_int
 | 
			
		||||
from zerver.decorator import REQ, has_request_variables, api_key_only_webhook_view
 | 
			
		||||
from zerver.models import UserProfile, Client
 | 
			
		||||
 | 
			
		||||
ZULIP_MESSAGE_TEMPLATE = "**{message_sender}**: `{text}`"
 | 
			
		||||
VALID_OPTIONS = {'SHOULD_NOT_BE_MAPPED': '0', 'SHOULD_BE_MAPPED': '1'}
 | 
			
		||||
 | 
			
		||||
@api_key_only_webhook_view('Slack')
 | 
			
		||||
@has_request_variables
 | 
			
		||||
def api_slack_webhook(request, user_profile, client,
 | 
			
		||||
                      user_name=REQ(),
 | 
			
		||||
                      text=REQ(),
 | 
			
		||||
                      channel_name=REQ(),
 | 
			
		||||
                      stream=REQ(default='slack'),
 | 
			
		||||
                      channels_map_to_topics=REQ(default='1')):
 | 
			
		||||
    # type: (HttpRequest, UserProfile, Client, str, str, str, str, str) -> HttpResponse
 | 
			
		||||
 | 
			
		||||
    if channels_map_to_topics not in list(VALID_OPTIONS.values()):
 | 
			
		||||
        return json_error(_('Error: channels_map_to_topics parameter other than 0 or 1'))
 | 
			
		||||
 | 
			
		||||
    if channels_map_to_topics == VALID_OPTIONS['SHOULD_BE_MAPPED']:
 | 
			
		||||
        subject = "channel: {}".format(channel_name)
 | 
			
		||||
    else:
 | 
			
		||||
        stream = channel_name
 | 
			
		||||
        subject = _("Message from Slack")
 | 
			
		||||
 | 
			
		||||
    content = ZULIP_MESSAGE_TEMPLATE.format(message_sender=user_name, text=text)
 | 
			
		||||
    check_send_message(user_profile, client, "stream", [stream], subject, content)
 | 
			
		||||
    return json_success()
 | 
			
		||||
		Reference in New Issue
	
	Block a user