mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-03 21:43:21 +00:00 
			
		
		
		
	integrations: Add webhook code, API endpoint, and tests for Front.
This commit is contained in:
		
							
								
								
									
										1
									
								
								static/images/integrations/logos/front.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								static/images/integrations/logos/front.svg
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
<svg width="2500" height="2119" viewBox="0 0 256 217" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid"><path d="M219.172 0h-71.08c-.736 0-1.446.269-2 .754L130 12.181a3.035 3.035 0 0 1-4 0L109.91.754a3.032 3.032 0 0 0-2-.754H36.83c-.84 0-1.65.345-2.224.958C13.253 23.731.135 54.317.001 87.965-.273 157.81 56.328 215.492 126.168 216.47 197.699 217.469 256 159.784 256 88.482c0-33.854-13.145-64.634-34.607-87.524A3.043 3.043 0 0 0 219.172 0zm-27.587 32.946c12.98 14.844 20.86 34.265 20.86 55.536 0 48.156-40.312 86.895-89.026 84.321-43.439-2.295-78.304-37.851-79.81-81.324-.778-22.435 7.216-42.986 20.799-58.528 1.042-1.193.966-2.991-.151-4.111L38.119 2.618l86.078 61.768a6.071 6.071 0 0 0 7.607 0l86.02-61.71-26.08 26.161c-1.114 1.12-1.198 2.919-.159 4.11z" fill="#FF6B6B"/></svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 795 B  | 
@@ -294,6 +294,7 @@ WEBHOOK_INTEGRATIONS = [
 | 
			
		||||
    ),
 | 
			
		||||
    WebhookIntegration('dropbox', ['productivity'], display_name='Dropbox'),
 | 
			
		||||
    WebhookIntegration('freshdesk', ['customer-support']),
 | 
			
		||||
    WebhookIntegration('front', ['customer-support'], display_name='Front'),
 | 
			
		||||
    GithubIntegration(
 | 
			
		||||
        'github',
 | 
			
		||||
        ['version-control'],
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										0
									
								
								zerver/webhooks/front/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								zerver/webhooks/front/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										282
									
								
								zerver/webhooks/front/tests.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										282
									
								
								zerver/webhooks/front/tests.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,282 @@
 | 
			
		||||
from typing import Text
 | 
			
		||||
import ujson
 | 
			
		||||
 | 
			
		||||
from zerver.lib.test_classes import WebhookTestCase
 | 
			
		||||
 | 
			
		||||
class FrontHookTests(WebhookTestCase):
 | 
			
		||||
    STREAM_NAME = 'front'
 | 
			
		||||
    URL_TEMPLATE = "/api/v1/external/front?&api_key={api_key}"
 | 
			
		||||
    FIXTURE_DIR_NAME = 'front'
 | 
			
		||||
 | 
			
		||||
    def _test_no_message_data(self, fixture_name: Text) -> None:
 | 
			
		||||
        payload = self.get_body(fixture_name)
 | 
			
		||||
        payload_json = ujson.loads(payload)
 | 
			
		||||
        del payload_json['conversation']['subject']
 | 
			
		||||
        result = self.client_post(self.url, ujson.dumps(payload_json),
 | 
			
		||||
                                  content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
        self.assert_json_error(result, "Missing required data")
 | 
			
		||||
 | 
			
		||||
    def _test_no_source_name(self, fixture_name: Text) -> None:
 | 
			
		||||
        payload = self.get_body(fixture_name)
 | 
			
		||||
        payload_json = ujson.loads(payload)
 | 
			
		||||
        del payload_json['source']['data']['first_name']
 | 
			
		||||
        result = self.client_post(self.url, ujson.dumps(payload_json),
 | 
			
		||||
                                  content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
        self.assert_json_error(result, "Missing required data")
 | 
			
		||||
 | 
			
		||||
    def _test_no_target_name(self, fixture_name: Text) -> None:
 | 
			
		||||
        payload = self.get_body(fixture_name)
 | 
			
		||||
        payload_json = ujson.loads(payload)
 | 
			
		||||
        del payload_json['target']['data']['first_name']
 | 
			
		||||
        result = self.client_post(self.url, ujson.dumps(payload_json),
 | 
			
		||||
                                  content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
        self.assert_json_error(result, "Missing required data")
 | 
			
		||||
 | 
			
		||||
    def _test_no_comment(self, fixture_name: Text) -> None:
 | 
			
		||||
        payload = self.get_body(fixture_name)
 | 
			
		||||
        payload_json = ujson.loads(payload)
 | 
			
		||||
        del payload_json['target']['data']['body']
 | 
			
		||||
        result = self.client_post(self.url, ujson.dumps(payload_json),
 | 
			
		||||
                                  content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
        self.assert_json_error(result, "Missing required data")
 | 
			
		||||
 | 
			
		||||
    def _test_no_tag(self, fixture_name: Text) -> None:
 | 
			
		||||
        payload = self.get_body(fixture_name)
 | 
			
		||||
        payload_json = ujson.loads(payload)
 | 
			
		||||
        del payload_json['target']['data']['name']
 | 
			
		||||
        result = self.client_post(self.url, ujson.dumps(payload_json),
 | 
			
		||||
                                  content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
        self.assert_json_error(result, "Missing required data")
 | 
			
		||||
 | 
			
		||||
    def test_no_event_type(self) -> None:
 | 
			
		||||
        payload = self.get_body('1_conversation_assigned_outbound')
 | 
			
		||||
        payload_json = ujson.loads(payload)
 | 
			
		||||
        del payload_json['type']
 | 
			
		||||
        result = self.client_post(self.url, ujson.dumps(payload_json),
 | 
			
		||||
                                  content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
        self.assert_json_error(result, "Missing required data")
 | 
			
		||||
 | 
			
		||||
    def test_no_conversation_id(self) -> None:
 | 
			
		||||
        payload = self.get_body('1_conversation_assigned_outbound')
 | 
			
		||||
        payload_json = ujson.loads(payload)
 | 
			
		||||
        del payload_json['conversation']['id']
 | 
			
		||||
        result = self.client_post(self.url, ujson.dumps(payload_json),
 | 
			
		||||
                                  content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
        self.assert_json_error(result, "Missing required data")
 | 
			
		||||
 | 
			
		||||
    # Scenario 1: Conversation starts from an outbound message.
 | 
			
		||||
 | 
			
		||||
    # Conversation automatically assigned to a teammate who started it.
 | 
			
		||||
    def test_conversation_assigned_outbound(self) -> None:
 | 
			
		||||
        expected_subject = 'cnv_keo696'
 | 
			
		||||
        expected_message = "**Leela Turanga** assigned themselves."
 | 
			
		||||
 | 
			
		||||
        self.send_and_test_stream_message('1_conversation_assigned_outbound',
 | 
			
		||||
                                          expected_subject,
 | 
			
		||||
                                          expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_outbound_message(self) -> None:
 | 
			
		||||
        expected_subject = 'cnv_keo696'
 | 
			
		||||
        expected_message = "[Outbound message](https://app.frontapp.com/open/msg_1176ie2) " \
 | 
			
		||||
                           "from **support@planet-express.com** " \
 | 
			
		||||
                           "to **calculon@momsbot.com**.\n" \
 | 
			
		||||
                           "```quote\n*Subject*: Your next delivery is on Epsilon 96Z\n```"
 | 
			
		||||
 | 
			
		||||
        self.send_and_test_stream_message('2_outbound_message',
 | 
			
		||||
                                          expected_subject,
 | 
			
		||||
                                          expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_outbound_message_error(self) -> None:
 | 
			
		||||
        self._test_no_message_data('2_outbound_message')
 | 
			
		||||
 | 
			
		||||
    def test_conversation_archived(self) -> None:
 | 
			
		||||
        expected_subject = 'cnv_keo696'
 | 
			
		||||
        expected_message = "Archived by **Leela Turanga**."
 | 
			
		||||
 | 
			
		||||
        self.send_and_test_stream_message('3_conversation_archived',
 | 
			
		||||
                                          expected_subject,
 | 
			
		||||
                                          expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_conversation_archived_error(self) -> None:
 | 
			
		||||
        self._test_no_source_name('3_conversation_archived')
 | 
			
		||||
 | 
			
		||||
    def test_conversation_reopened(self) -> None:
 | 
			
		||||
        expected_subject = 'cnv_keo696'
 | 
			
		||||
        expected_message = "Reopened by **Leela Turanga**."
 | 
			
		||||
 | 
			
		||||
        self.send_and_test_stream_message('4_conversation_reopened',
 | 
			
		||||
                                          expected_subject,
 | 
			
		||||
                                          expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_conversation_reopened_error(self) -> None:
 | 
			
		||||
        self._test_no_source_name('4_conversation_reopened')
 | 
			
		||||
 | 
			
		||||
    def test_conversation_deleted(self) -> None:
 | 
			
		||||
        expected_subject = 'cnv_keo696'
 | 
			
		||||
        expected_message = "Deleted by **Leela Turanga**."
 | 
			
		||||
 | 
			
		||||
        self.send_and_test_stream_message('5_conversation_deleted',
 | 
			
		||||
                                          expected_subject,
 | 
			
		||||
                                          expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_conversation_deleted_error(self) -> None:
 | 
			
		||||
        self._test_no_source_name('5_conversation_deleted')
 | 
			
		||||
 | 
			
		||||
    def test_conversation_restored(self) -> None:
 | 
			
		||||
        expected_subject = 'cnv_keo696'
 | 
			
		||||
        expected_message = "Restored by **Leela Turanga**."
 | 
			
		||||
 | 
			
		||||
        self.send_and_test_stream_message('6_conversation_restored',
 | 
			
		||||
                                          expected_subject,
 | 
			
		||||
                                          expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_conversation_restored_error(self) -> None:
 | 
			
		||||
        self._test_no_source_name('6_conversation_restored')
 | 
			
		||||
 | 
			
		||||
    def test_conversation_unassigned(self) -> None:
 | 
			
		||||
        expected_subject = 'cnv_keo696'
 | 
			
		||||
        expected_message = "Unassined by **Leela Turanga**."
 | 
			
		||||
 | 
			
		||||
        self.send_and_test_stream_message('7_conversation_unassigned',
 | 
			
		||||
                                          expected_subject,
 | 
			
		||||
                                          expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_conversation_unassigned_error(self) -> None:
 | 
			
		||||
        self._test_no_source_name('7_conversation_unassigned')
 | 
			
		||||
 | 
			
		||||
    def test_mention_all(self) -> None:
 | 
			
		||||
        expected_subject = 'cnv_keo696'
 | 
			
		||||
        expected_message = "**Leela Turanga** left a comment:\n" \
 | 
			
		||||
                           "```quote\n@all Could someone else take this?\n```"
 | 
			
		||||
 | 
			
		||||
        self.send_and_test_stream_message('8_mention_all',
 | 
			
		||||
                                          expected_subject,
 | 
			
		||||
                                          expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    # Scenario 2: Conversation starts from an inbound message.
 | 
			
		||||
 | 
			
		||||
    def test_inbound_message(self) -> None:
 | 
			
		||||
        expected_subject = 'cnv_keocka'
 | 
			
		||||
        expected_message = "[Inbound message](https://app.frontapp.com/open/msg_1176r8y) " \
 | 
			
		||||
                           "from **calculon@momsbot.com** " \
 | 
			
		||||
                           "to **support@planet-express.com**.\n" \
 | 
			
		||||
                           "```quote\n*Subject*: Being a robot is great, but...\n```"
 | 
			
		||||
 | 
			
		||||
        self.send_and_test_stream_message('9_inbound_message',
 | 
			
		||||
                                          expected_subject,
 | 
			
		||||
                                          expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_inbound_message_error(self) -> None:
 | 
			
		||||
        self._test_no_message_data('9_inbound_message')
 | 
			
		||||
 | 
			
		||||
    def test_conversation_tagged(self) -> None:
 | 
			
		||||
        expected_subject = 'cnv_keocka'
 | 
			
		||||
        expected_message = "**Leela Turanga** added tag **Urgent**."
 | 
			
		||||
 | 
			
		||||
        self.send_and_test_stream_message('10_conversation_tagged',
 | 
			
		||||
                                          expected_subject,
 | 
			
		||||
                                          expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_conversation_tagged_error(self) -> None:
 | 
			
		||||
        self._test_no_tag('10_conversation_tagged')
 | 
			
		||||
 | 
			
		||||
    # Conversation automatically assigned to a teammate who replied to it.
 | 
			
		||||
    def test_conversation_assigned_reply(self) -> None:
 | 
			
		||||
        expected_subject = 'cnv_keocka'
 | 
			
		||||
        expected_message = "**Leela Turanga** assigned themselves."
 | 
			
		||||
 | 
			
		||||
        self.send_and_test_stream_message('11_conversation_assigned_reply',
 | 
			
		||||
                                          expected_subject,
 | 
			
		||||
                                          expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_outbound_reply(self) -> None:
 | 
			
		||||
        expected_subject = 'cnv_keocka'
 | 
			
		||||
        expected_message = "[Outbound reply](https://app.frontapp.com/open/msg_1176ryy) " \
 | 
			
		||||
                           "from **support@planet-express.com** " \
 | 
			
		||||
                           "to **calculon@momsbot.com**."
 | 
			
		||||
 | 
			
		||||
        self.send_and_test_stream_message('12_outbound_reply',
 | 
			
		||||
                                          expected_subject,
 | 
			
		||||
                                          expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_outbound_reply_error(self) -> None:
 | 
			
		||||
        self._test_no_message_data('12_outbound_reply')
 | 
			
		||||
 | 
			
		||||
    def test_conversation_untagged(self) -> None:
 | 
			
		||||
        expected_subject = 'cnv_keocka'
 | 
			
		||||
        expected_message = "**Leela Turanga** removed tag **Urgent**."
 | 
			
		||||
 | 
			
		||||
        self.send_and_test_stream_message('13_conversation_untagged',
 | 
			
		||||
                                          expected_subject,
 | 
			
		||||
                                          expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_conversation_untagged_error(self) -> None:
 | 
			
		||||
        self._test_no_tag('13_conversation_untagged')
 | 
			
		||||
 | 
			
		||||
    def test_mention(self) -> None:
 | 
			
		||||
        expected_subject = 'cnv_keocka'
 | 
			
		||||
        expected_message = "**Leela Turanga** left a comment:\n" \
 | 
			
		||||
                           "```quote\n@bender Could you take it from here?\n```"
 | 
			
		||||
 | 
			
		||||
        self.send_and_test_stream_message('14_mention',
 | 
			
		||||
                                          expected_subject,
 | 
			
		||||
                                          expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_comment(self) -> None:
 | 
			
		||||
        expected_subject = 'cnv_keocka'
 | 
			
		||||
        expected_message = "**Bender Rodriguez** left a comment:\n" \
 | 
			
		||||
                           "```quote\nSure.\n```"
 | 
			
		||||
 | 
			
		||||
        self.send_and_test_stream_message('15_comment',
 | 
			
		||||
                                          expected_subject,
 | 
			
		||||
                                          expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_comment_error(self) -> None:
 | 
			
		||||
        self._test_no_comment('15_comment')
 | 
			
		||||
 | 
			
		||||
    # Conversation manually assigned to another teammate.
 | 
			
		||||
    def test_conversation_assigned(self) -> None:
 | 
			
		||||
        expected_subject = 'cnv_keocka'
 | 
			
		||||
        expected_message = "**Leela Turanga** assigned **Bender Rodriguez**."
 | 
			
		||||
 | 
			
		||||
        self.send_and_test_stream_message('16_conversation_assigned',
 | 
			
		||||
                                          expected_subject,
 | 
			
		||||
                                          expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_conversation_assigned_error(self) -> None:
 | 
			
		||||
        self._test_no_target_name('16_conversation_assigned')
 | 
			
		||||
 | 
			
		||||
    def test_unknown_webhook_request(self) -> None:
 | 
			
		||||
        payload = self.get_body('16_conversation_assigned')
 | 
			
		||||
        payload_json = ujson.loads(payload)
 | 
			
		||||
        payload_json['type'] = 'qwerty'
 | 
			
		||||
        result = self.client_post(self.url, ujson.dumps(payload_json),
 | 
			
		||||
                                  content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
        self.assert_json_error(result, "Unknown webhook request")
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name: Text) -> Text:
 | 
			
		||||
        return self.fixture_data('front', fixture_name, file_type="json")
 | 
			
		||||
							
								
								
									
										188
									
								
								zerver/webhooks/front/view.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								zerver/webhooks/front/view.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,188 @@
 | 
			
		||||
from typing import Any, Dict, Optional, Text, Tuple
 | 
			
		||||
 | 
			
		||||
from django.http import HttpRequest, HttpResponse
 | 
			
		||||
from django.utils.translation import ugettext as _
 | 
			
		||||
 | 
			
		||||
from zerver.decorator import api_key_only_webhook_view
 | 
			
		||||
from zerver.lib.actions import check_send_stream_message
 | 
			
		||||
from zerver.lib.request import REQ, has_request_variables
 | 
			
		||||
from zerver.lib.response import json_error, json_success
 | 
			
		||||
from zerver.models import UserProfile
 | 
			
		||||
 | 
			
		||||
def get_message_data(payload: Dict[Text, Any]) -> Optional[Tuple[Text, Text, Text, Text]]:
 | 
			
		||||
    try:
 | 
			
		||||
        link = "https://app.frontapp.com/open/" + payload['target']['data']['id']
 | 
			
		||||
        outbox = payload['conversation']['recipient']['handle']
 | 
			
		||||
        inbox = payload['source']['data'][0]['address']
 | 
			
		||||
        subject = payload['conversation']['subject']
 | 
			
		||||
    except KeyError:
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    return link, outbox, inbox, subject
 | 
			
		||||
 | 
			
		||||
def get_source_name(payload: Dict[Text, Any]) -> Optional[Text]:
 | 
			
		||||
    try:
 | 
			
		||||
        first_name = payload['source']['data']['first_name']
 | 
			
		||||
        last_name = payload['source']['data']['last_name']
 | 
			
		||||
    except KeyError:
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    return "%s %s" % (first_name, last_name)
 | 
			
		||||
 | 
			
		||||
def get_target_name(payload: Dict[Text, Any]) -> Optional[Text]:
 | 
			
		||||
    try:
 | 
			
		||||
        first_name = payload['target']['data']['first_name']
 | 
			
		||||
        last_name = payload['target']['data']['last_name']
 | 
			
		||||
    except KeyError:
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    return "%s %s" % (first_name, last_name)
 | 
			
		||||
 | 
			
		||||
def get_comment(payload: Dict[Text, Any]) -> Optional[Text]:
 | 
			
		||||
    try:
 | 
			
		||||
        comment = payload['target']['data']['body']
 | 
			
		||||
    except KeyError:
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    return comment
 | 
			
		||||
 | 
			
		||||
def get_tag(payload: Dict[Text, Any]) -> Optional[Text]:
 | 
			
		||||
    try:
 | 
			
		||||
        tag = payload['target']['data']['name']
 | 
			
		||||
    except KeyError:
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    return tag
 | 
			
		||||
 | 
			
		||||
@api_key_only_webhook_view('Front')
 | 
			
		||||
@has_request_variables
 | 
			
		||||
def api_front_webhook(request: HttpRequest, user_profile: UserProfile,
 | 
			
		||||
                      payload: Dict[Text, Any]=REQ(argument_type='body'),
 | 
			
		||||
                      stream: Text=REQ(default='front'),
 | 
			
		||||
                      topic: Optional[Text]=REQ(default='cnv_id')) -> HttpResponse:
 | 
			
		||||
    try:
 | 
			
		||||
        event_type = payload['type']
 | 
			
		||||
        conversation_id = payload['conversation']['id']
 | 
			
		||||
    except KeyError:
 | 
			
		||||
        return json_error(_("Missing required data"))
 | 
			
		||||
 | 
			
		||||
    # Each topic corresponds to a separate conversation in Front.
 | 
			
		||||
    topic = conversation_id
 | 
			
		||||
 | 
			
		||||
    # Inbound message
 | 
			
		||||
    if event_type == 'inbound':
 | 
			
		||||
        message_data = get_message_data(payload)
 | 
			
		||||
        if not message_data:
 | 
			
		||||
            return json_error(_("Missing required data"))
 | 
			
		||||
 | 
			
		||||
        link, outbox, inbox, subject = message_data
 | 
			
		||||
        body = "[Inbound message]({link}) from **{outbox}** to **{inbox}**.\n" \
 | 
			
		||||
               "```quote\n*Subject*: {subject}\n```" \
 | 
			
		||||
            .format(link=link, outbox=outbox, inbox=inbox, subject=subject)
 | 
			
		||||
 | 
			
		||||
    # Outbound message
 | 
			
		||||
    elif event_type == 'outbound':
 | 
			
		||||
        message_data = get_message_data(payload)
 | 
			
		||||
        if not message_data:
 | 
			
		||||
            return json_error(_("Missing required data"))
 | 
			
		||||
 | 
			
		||||
        link, outbox, inbox, subject = message_data
 | 
			
		||||
        body = "[Outbound message]({link}) from **{inbox}** to **{outbox}**.\n" \
 | 
			
		||||
               "```quote\n*Subject*: {subject}\n```" \
 | 
			
		||||
            .format(link=link, inbox=inbox, outbox=outbox, subject=subject)
 | 
			
		||||
 | 
			
		||||
    # Outbound reply
 | 
			
		||||
    elif event_type == 'out_reply':
 | 
			
		||||
        message_data = get_message_data(payload)
 | 
			
		||||
        if not message_data:
 | 
			
		||||
            return json_error(_("Missing required data"))
 | 
			
		||||
 | 
			
		||||
        link, outbox, inbox, subject = message_data
 | 
			
		||||
        body = "[Outbound reply]({link}) from **{inbox}** to **{outbox}**." \
 | 
			
		||||
            .format(link=link, inbox=inbox, outbox=outbox)
 | 
			
		||||
 | 
			
		||||
    # Comment or mention
 | 
			
		||||
    elif event_type == 'comment' or event_type == 'mention':
 | 
			
		||||
        name, comment = get_source_name(payload), get_comment(payload)
 | 
			
		||||
        if not (name and comment):
 | 
			
		||||
            return json_error(_("Missing required data"))
 | 
			
		||||
 | 
			
		||||
        body = "**{name}** left a comment:\n```quote\n{comment}\n```" \
 | 
			
		||||
            .format(name=name, comment=comment)
 | 
			
		||||
 | 
			
		||||
    # Conversation assigned
 | 
			
		||||
    elif event_type == 'assign':
 | 
			
		||||
        source_name = get_source_name(payload)
 | 
			
		||||
        target_name = get_target_name(payload)
 | 
			
		||||
 | 
			
		||||
        if not (source_name and target_name):
 | 
			
		||||
            return json_error(_("Missing required data"))
 | 
			
		||||
 | 
			
		||||
        if source_name == target_name:
 | 
			
		||||
            body = "**{source_name}** assigned themselves." \
 | 
			
		||||
                .format(source_name=source_name)
 | 
			
		||||
        else:
 | 
			
		||||
            body = "**{source_name}** assigned **{target_name}**." \
 | 
			
		||||
                .format(source_name=source_name, target_name=target_name)
 | 
			
		||||
 | 
			
		||||
    # Conversation unassigned
 | 
			
		||||
    elif event_type == 'unassign':
 | 
			
		||||
        name = get_source_name(payload)
 | 
			
		||||
        if not name:
 | 
			
		||||
            return json_error(_("Missing required data"))
 | 
			
		||||
 | 
			
		||||
        body = "Unassined by **{name}**.".format(name=name)
 | 
			
		||||
 | 
			
		||||
    # Conversation archived
 | 
			
		||||
    elif event_type == 'archive':
 | 
			
		||||
        name = get_source_name(payload)
 | 
			
		||||
        if not name:
 | 
			
		||||
            return json_error(_("Missing required data"))
 | 
			
		||||
 | 
			
		||||
        body = "Archived by **{name}**.".format(name=name)
 | 
			
		||||
 | 
			
		||||
    # Conversation reopened
 | 
			
		||||
    elif event_type == 'reopen':
 | 
			
		||||
        name = get_source_name(payload)
 | 
			
		||||
        if not name:
 | 
			
		||||
            return json_error(_("Missing required data"))
 | 
			
		||||
 | 
			
		||||
        body = "Reopened by **{name}**.".format(name=name)
 | 
			
		||||
 | 
			
		||||
    # Conversation deleted
 | 
			
		||||
    elif event_type == 'trash':
 | 
			
		||||
        name = get_source_name(payload)
 | 
			
		||||
        if not name:
 | 
			
		||||
            return json_error(_("Missing required data"))
 | 
			
		||||
 | 
			
		||||
        body = "Deleted by **{name}**.".format(name=name)
 | 
			
		||||
 | 
			
		||||
    # Conversation restored
 | 
			
		||||
    elif event_type == 'restore':
 | 
			
		||||
        name = get_source_name(payload)
 | 
			
		||||
        if not name:
 | 
			
		||||
            return json_error(_("Missing required data"))
 | 
			
		||||
 | 
			
		||||
        body = "Restored by **{name}**.".format(name=name)
 | 
			
		||||
 | 
			
		||||
    # Conversation tagged
 | 
			
		||||
    elif event_type == 'tag':
 | 
			
		||||
        name, tag = get_source_name(payload), get_tag(payload)
 | 
			
		||||
        if not (name and tag):
 | 
			
		||||
            return json_error(_("Missing required data"))
 | 
			
		||||
 | 
			
		||||
        body = "**{name}** added tag **{tag}**.".format(name=name, tag=tag)
 | 
			
		||||
 | 
			
		||||
    # Conversation untagged
 | 
			
		||||
    elif event_type == 'untag':
 | 
			
		||||
        name, tag = get_source_name(payload), get_tag(payload)
 | 
			
		||||
        if not (name and tag):
 | 
			
		||||
            return json_error(_("Missing required data"))
 | 
			
		||||
 | 
			
		||||
        body = "**{name}** removed tag **{tag}**.".format(name=name, tag=tag)
 | 
			
		||||
    else:
 | 
			
		||||
        return json_error(_("Unknown webhook request"))
 | 
			
		||||
 | 
			
		||||
    check_send_stream_message(user_profile, request.client, stream, topic, body)
 | 
			
		||||
 | 
			
		||||
    return json_success()
 | 
			
		||||
		Reference in New Issue
	
	Block a user