mirror of
https://github.com/zulip/zulip.git
synced 2025-11-03 13:33:24 +00:00
slack-integration: Update Slack integration to handle Events API.
This updates the Slack webhook integration to handle the Slack Events
API[1], while maintaining backwards compatibility with Slack's legacy
Outgoing Webhook service.
The Events API introduces the "challenge" handshake[2] to verify and
add a new webhook URL for them to call. This commit adds a handler for
the challenge handshake.
Additionally, this commit reformats incoming payloads using the Slack
text reformatter from `slack_message_conversion.py`. There is some
duplicative code here because of the difference in Slack export data
and Slack's webhook payload.
Part of #30465
[1]: https://api.slack.com/apis/events-api#using-events-api
[2]: https://api.slack.com/apis/events-api#handshake
(cherry picked from commit f29312ce03)
This commit is contained in:
@@ -806,7 +806,7 @@ DOC_SCREENSHOT_CONFIG: dict[str, list[BaseScreenshotConfig]] = {
|
||||
ScreenshotConfig("event_for_exception_python.json"),
|
||||
ScreenshotConfig("issue_assigned_to_team.json", "002.png"),
|
||||
],
|
||||
"slack": [ScreenshotConfig("message_info.txt")],
|
||||
"slack": [ScreenshotConfig("message_with_normal_text.json")],
|
||||
"sonarqube": [ScreenshotConfig("error.json")],
|
||||
"sonarr": [ScreenshotConfig("sonarr_episode_grabbed.json")],
|
||||
"splunk": [ScreenshotConfig("search_one_result.json")],
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"token": "Jhj5dZrVaK7ZwHHjRyZWjbDl",
|
||||
"challenge": "3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P",
|
||||
"type": "url_verification"
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"token": "CqCsBJmXoNSHaNCt3wGWYSEe",
|
||||
"team_id": "T06NRA6HM3P",
|
||||
"context_team_id": "T06NRA6HM3P",
|
||||
"context_enterprise_id": null,
|
||||
"api_app_id": "A0757RM31HN",
|
||||
"event": {
|
||||
"user": "U074G5E1ANR",
|
||||
"type": "message",
|
||||
"ts": "1718856814.111759",
|
||||
"bot_id": "B074K2M1NF4",
|
||||
"app_id": "A0757RM31HN",
|
||||
"text": "<Desdemona> koo",
|
||||
"team": "T06NRA6HM3P",
|
||||
"bot_profile": {
|
||||
"id": "B074K2M1NF4",
|
||||
"deleted": false,
|
||||
"name": "export-bot",
|
||||
"updated": 1716360210,
|
||||
"app_id": "A0757RM31HN",
|
||||
"icons": {
|
||||
"image_36": "https://a.slack-edge.com/80588/img/plugins/app/bot_36.png",
|
||||
"image_48": "https://a.slack-edge.com/80588/img/plugins/app/bot_48.png",
|
||||
"image_72": "https://a.slack-edge.com/80588/img/plugins/app/service_72.png"
|
||||
},
|
||||
"team_id": "T06NRA6HM3P"
|
||||
},
|
||||
"blocks": [
|
||||
{
|
||||
"type": "rich_text",
|
||||
"block_id": "a6Fvn",
|
||||
"elements": [
|
||||
{
|
||||
"type": "rich_text_section",
|
||||
"elements": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "<Desdemona> koo"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"channel": "C06NRA6JLER",
|
||||
"event_ts": "1718856814.111759",
|
||||
"channel_type": "channel"
|
||||
},
|
||||
"type": "event_callback",
|
||||
"event_id": "Ev078H33MYDV",
|
||||
"event_time": 1718856814,
|
||||
"authorizations": [
|
||||
{
|
||||
"enterprise_id": null,
|
||||
"team_id": "T06NRA6HM3P",
|
||||
"user_id": "U074G5E1ANR",
|
||||
"is_bot": true,
|
||||
"is_enterprise_install": false
|
||||
}
|
||||
],
|
||||
"is_ext_shared_channel": false,
|
||||
"event_context": "4-eyJldCI6Im1lc3NhZ2UiLCJ0aWQiOiJUMDZOUkE2SE0zUCIsImFpZCI6IkEwNzU3Uk0zMUhOIiwiY2lkIjoiQzA2TlJBNkpMRVIifQ"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
{
|
||||
"token": "x8GlZSSaBheFZWjZPbxPasEY",
|
||||
"team_id": "T06NRA6HM3P",
|
||||
"context_team_id": "T06NRA6HM3P",
|
||||
"context_enterprise_id": null,
|
||||
"api_app_id": "A07A20JQSAG",
|
||||
"event": {
|
||||
"subtype": "bot_message",
|
||||
"text": "*Task status changed* from `complete` to `to do`",
|
||||
"username": "ClickUp",
|
||||
"attachments": [
|
||||
{
|
||||
"id": 1,
|
||||
"blocks": [
|
||||
{
|
||||
"type": "section",
|
||||
"block_id": "bZ4XB",
|
||||
"text": {
|
||||
"type": "mrkdwn",
|
||||
"text": "*<https://app.clickup.com/t/25567147/86cw30wf2|asd>*",
|
||||
"verbatim": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "context",
|
||||
"block_id": "TEfLM",
|
||||
"elements": [
|
||||
{
|
||||
"type": "image",
|
||||
"image_url": "https://search.clickup-au.com/media/app-icons/clickup/logo_alpha.png",
|
||||
"alt_text": "Cookie / The Goodiest of Cstdddddsdd / dds"
|
||||
},
|
||||
{
|
||||
"type": "mrkdwn",
|
||||
"text": "in dds",
|
||||
"verbatim": false
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"image_url": "https://search.clickup-au.com/media/app-icons/clickup/status.png",
|
||||
"alt_text": "Status"
|
||||
},
|
||||
{
|
||||
"type": "mrkdwn",
|
||||
"text": "To Do",
|
||||
"verbatim": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "actions",
|
||||
"block_id": "cg8fg",
|
||||
"elements": [
|
||||
{
|
||||
"type": "button",
|
||||
"action_id": "NOOP",
|
||||
"text": {
|
||||
"type": "plain_text",
|
||||
"text": "View task",
|
||||
"emoji": true
|
||||
},
|
||||
"url": "https://app.clickup.com/t/25567147/86cw30wf2"
|
||||
},
|
||||
{
|
||||
"type": "button",
|
||||
"action_id": "EDIT_TASK_ACTION",
|
||||
"text": {
|
||||
"type": "plain_text",
|
||||
"text": "Edit task",
|
||||
"emoji": true
|
||||
},
|
||||
"value": "{\"taskID\":\"86cw30wf2\",\"teamID\":\"25567147\",\"taskUrl\":\"https://app.clickup.com/t/25567147/86cw30wf2\"}"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"color": "#656f7d",
|
||||
"fallback": "[no preview available]"
|
||||
}
|
||||
],
|
||||
"type": "message",
|
||||
"ts": "1723609070.703489",
|
||||
"bot_id": "B06NWMNUQ3W",
|
||||
"app_id": "A3G4A68V9",
|
||||
"blocks": [
|
||||
{
|
||||
"type": "section",
|
||||
"block_id": "X0xqC",
|
||||
"text": {
|
||||
"type": "mrkdwn",
|
||||
"text": "*Task status changed* from `complete` to `to do`",
|
||||
"verbatim": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"channel": "C06P6T3QGD7",
|
||||
"event_ts": "1723609070.703489",
|
||||
"channel_type": "channel"
|
||||
},
|
||||
"type": "event_callback",
|
||||
"event_id": "Ev07GMTJQFEJ",
|
||||
"event_time": 1723609070,
|
||||
"authorizations": [
|
||||
{
|
||||
"enterprise_id": null,
|
||||
"team_id": "T06NRA6HM3P",
|
||||
"user_id": "U07A1PEN4JW",
|
||||
"is_bot": true,
|
||||
"is_enterprise_install": false
|
||||
}
|
||||
],
|
||||
"is_ext_shared_channel": false,
|
||||
"event_context": "4-eyJldCI6Im1lc3NhZ2UiLCJ0aWQiOiJUMDZOUkE2SE0zUCIsImFpZCI6IkEwN0EyMEpRU0FHIiwiY2lkIjoiQzA2UDZUM1FHRDcifQ"
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"token": "CqCsBJmXoNSHaNCt3wGWYSEe",
|
||||
"team_id": "T06NRA6HM3P",
|
||||
"context_team_id": "T06NRA6HM3P",
|
||||
"context_enterprise_id": null,
|
||||
"api_app_id": "A0757RM31HN",
|
||||
"event": {
|
||||
"user": "U06NU4E26M9",
|
||||
"type": "message",
|
||||
"ts": "1719211714.999529",
|
||||
"client_msg_id": "8908e456-1821-4316-875c-810019adf321",
|
||||
"text": "\u2022 list three\n\u2022 list two",
|
||||
"team": "T06NRA6HM3P",
|
||||
"blocks": [
|
||||
{
|
||||
"type": "rich_text",
|
||||
"block_id": "Cua/K",
|
||||
"elements": [
|
||||
{
|
||||
"type": "rich_text_list",
|
||||
"elements": [
|
||||
{
|
||||
"type": "rich_text_section",
|
||||
"elements": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "list three"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "rich_text_section",
|
||||
"elements": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "list two"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"style": "bullet",
|
||||
"indent": 0,
|
||||
"border": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"channel": "C06NRA6JLER",
|
||||
"event_ts": "1719211714.999529",
|
||||
"channel_type": "channel"
|
||||
},
|
||||
"type": "event_callback",
|
||||
"event_id": "Ev07A31E3A2C",
|
||||
"event_time": 1719211714,
|
||||
"authorizations": [
|
||||
{
|
||||
"enterprise_id": null,
|
||||
"team_id": "T06NRA6HM3P",
|
||||
"user_id": "U074G5E1ANR",
|
||||
"is_bot": true,
|
||||
"is_enterprise_install": false
|
||||
}
|
||||
],
|
||||
"is_ext_shared_channel": false,
|
||||
"event_context": "4-eyJldCI6Im1lc3NhZ2UiLCJ0aWQiOiJUMDZOUkE2SE0zUCIsImFpZCI6IkEwNzU3Uk0zMUhOIiwiY2lkIjoiQzA2TlJBNkpMRVIifQ"
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"token": "CqCsBJmXoNSHaNCt3wGWYSEe",
|
||||
"team_id": "T06NRA6HM3P",
|
||||
"context_team_id": "T06NRA6HM3P",
|
||||
"context_enterprise_id": null,
|
||||
"api_app_id": "A0757RM31HN",
|
||||
"event": {
|
||||
"user": "U06NU4E26M9",
|
||||
"type": "message",
|
||||
"ts": "1719210664.234089",
|
||||
"client_msg_id": "3e66cc71-119b-4576-b9b5-ec6000cd7dc8",
|
||||
"text": "<@U074VRHQ11T> <#C06P6T3QGD7|general> message with both channel and user mentions",
|
||||
"team": "T06NRA6HM3P",
|
||||
"blocks": [
|
||||
{
|
||||
"type": "rich_text",
|
||||
"block_id": "jkPDh",
|
||||
"elements": [
|
||||
{
|
||||
"type": "rich_text_section",
|
||||
"elements": [
|
||||
{
|
||||
"type": "user",
|
||||
"user_id": "U074VRHQ11T"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": " "
|
||||
},
|
||||
{
|
||||
"type": "channel",
|
||||
"channel_id": "C06P6T3QGD7"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": " message with both channel and user mentions"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"channel": "C06NRA6JLER",
|
||||
"event_ts": "1719210664.234089",
|
||||
"channel_type": "channel"
|
||||
},
|
||||
"type": "event_callback",
|
||||
"event_id": "Ev079B8H59UM",
|
||||
"event_time": 1719210664,
|
||||
"authorizations": [
|
||||
{
|
||||
"enterprise_id": null,
|
||||
"team_id": "T06NRA6HM3P",
|
||||
"user_id": "U074G5E1ANR",
|
||||
"is_bot": true,
|
||||
"is_enterprise_install": false
|
||||
}
|
||||
],
|
||||
"is_ext_shared_channel": false,
|
||||
"event_context": "4-eyJldCI6Im1lc3NhZ2UiLCJ0aWQiOiJUMDZOUkE2SE0zUCIsImFpZCI6IkEwNzU3Uk0zMUhOIiwiY2lkIjoiQzA2TlJBNkpMRVIifQ"
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"token": "CqCsBJmXoNSHaNCt3wGWYSEe",
|
||||
"team_id": "T06NRA6HM3P",
|
||||
"context_team_id": "T06NRA6HM3P",
|
||||
"context_enterprise_id": null,
|
||||
"api_app_id": "A0757RM31HN",
|
||||
"event": {
|
||||
"user": "U06NU4E26M9",
|
||||
"type": "message",
|
||||
"ts": "1719210488.508969",
|
||||
"client_msg_id": "f90f528c-a4de-4ddf-8a6c-5315cc17c2d3",
|
||||
"text": "<#C06NRA6JLER|zulip-mirror> <#C06P6T3QGD7|general> message with channel mentions",
|
||||
"team": "T06NRA6HM3P",
|
||||
"blocks": [
|
||||
{
|
||||
"type": "rich_text",
|
||||
"block_id": "bVBDn",
|
||||
"elements": [
|
||||
{
|
||||
"type": "rich_text_section",
|
||||
"elements": [
|
||||
{
|
||||
"type": "channel",
|
||||
"channel_id": "C06NRA6JLER"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": " "
|
||||
},
|
||||
{
|
||||
"type": "channel",
|
||||
"channel_id": "C06P6T3QGD7"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": " message with channel mentions"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"channel": "C06NRA6JLER",
|
||||
"event_ts": "1719210488.508969",
|
||||
"channel_type": "channel"
|
||||
},
|
||||
"type": "event_callback",
|
||||
"event_id": "Ev079E1Z0DS7",
|
||||
"event_time": 1719210488,
|
||||
"authorizations": [
|
||||
{
|
||||
"enterprise_id": null,
|
||||
"team_id": "T06NRA6HM3P",
|
||||
"user_id": "U074G5E1ANR",
|
||||
"is_bot": true,
|
||||
"is_enterprise_install": false
|
||||
}
|
||||
],
|
||||
"is_ext_shared_channel": false,
|
||||
"event_context": "4-eyJldCI6Im1lc3NhZ2UiLCJ0aWQiOiJUMDZOUkE2SE0zUCIsImFpZCI6IkEwNzU3Uk0zMUhOIiwiY2lkIjoiQzA2TlJBNkpMRVIifQ"
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"token": "CqCsBJmXoNSHaNCt3wGWYSEe",
|
||||
"team_id": "T06NRA6HM3P",
|
||||
"context_team_id": "T06NRA6HM3P",
|
||||
"context_enterprise_id": null,
|
||||
"api_app_id": "A0757RM31HN",
|
||||
"event": {
|
||||
"user": "U06NU4E26M9",
|
||||
"type": "message",
|
||||
"ts": "1719210450.946059",
|
||||
"client_msg_id": "266a366d-5e6d-4293-8e9d-1d3ada3a3c77",
|
||||
"text": "*Bold text* _italic text_ ~strikethrough~",
|
||||
"team": "T06NRA6HM3P",
|
||||
"blocks": [
|
||||
{
|
||||
"type": "rich_text",
|
||||
"block_id": "pQxnY",
|
||||
"elements": [
|
||||
{
|
||||
"type": "rich_text_section",
|
||||
"elements": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Bold text ",
|
||||
"style": {
|
||||
"bold": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": "italic text ",
|
||||
"style": {
|
||||
"italic": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": "strikethrough",
|
||||
"style": {
|
||||
"strike": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"channel": "C06NRA6JLER",
|
||||
"event_ts": "1719210450.946059",
|
||||
"channel_type": "channel"
|
||||
},
|
||||
"type": "event_callback",
|
||||
"event_id": "Ev0797G216NS",
|
||||
"event_time": 1719210450,
|
||||
"authorizations": [
|
||||
{
|
||||
"enterprise_id": null,
|
||||
"team_id": "T06NRA6HM3P",
|
||||
"user_id": "U074G5E1ANR",
|
||||
"is_bot": true,
|
||||
"is_enterprise_install": false
|
||||
}
|
||||
],
|
||||
"is_ext_shared_channel": false,
|
||||
"event_context": "4-eyJldCI6Im1lc3NhZ2UiLCJ0aWQiOiJUMDZOUkE2SE0zUCIsImFpZCI6IkEwNzU3Uk0zMUhOIiwiY2lkIjoiQzA2TlJBNkpMRVIifQ"
|
||||
}
|
||||
179
zerver/webhooks/slack/fixtures/message_with_image_files.json
Normal file
179
zerver/webhooks/slack/fixtures/message_with_image_files.json
Normal file
@@ -0,0 +1,179 @@
|
||||
{
|
||||
"token": "CqCsBJmXoNSHaNCt3wGWYSEe",
|
||||
"team_id": "T06NRA6HM3P",
|
||||
"context_team_id": "T06NRA6HM3P",
|
||||
"context_enterprise_id": null,
|
||||
"api_app_id": "A0757RM31HN",
|
||||
"event": {
|
||||
"text": "",
|
||||
"files": [
|
||||
{
|
||||
"id": "F079E4173BL",
|
||||
"created": 1719209777,
|
||||
"timestamp": 1719209777,
|
||||
"name": "5e44bcbc-e43c-4a2e-85de-4be126f392f4.jpg",
|
||||
"title": "5e44bcbc-e43c-4a2e-85de-4be126f392f4.jpg",
|
||||
"mimetype": "image/jpeg",
|
||||
"filetype": "jpg",
|
||||
"pretty_type": "JPEG",
|
||||
"user": "U06NU4E26M9",
|
||||
"user_team": "T06NRA6HM3P",
|
||||
"editable": false,
|
||||
"size": 3036645,
|
||||
"mode": "hosted",
|
||||
"is_external": false,
|
||||
"external_type": "",
|
||||
"is_public": true,
|
||||
"public_url_shared": false,
|
||||
"display_as_bot": false,
|
||||
"username": "",
|
||||
"url_private": "https://files.slack.com/files-pri/T06NRA6HM3P-F079E4173BL/5e44bcbc-e43c-4a2e-85de-4be126f392f4.jpg",
|
||||
"url_private_download": "https://files.slack.com/files-pri/T06NRA6HM3P-F079E4173BL/download/5e44bcbc-e43c-4a2e-85de-4be126f392f4.jpg",
|
||||
"media_display_type": "unknown",
|
||||
"thumb_64": "https://files.slack.com/files-tmb/T06NRA6HM3P-F079E4173BL-6e091e0261/5e44bcbc-e43c-4a2e-85de-4be126f392f4_64.jpg",
|
||||
"thumb_80": "https://files.slack.com/files-tmb/T06NRA6HM3P-F079E4173BL-6e091e0261/5e44bcbc-e43c-4a2e-85de-4be126f392f4_80.jpg",
|
||||
"thumb_360": "https://files.slack.com/files-tmb/T06NRA6HM3P-F079E4173BL-6e091e0261/5e44bcbc-e43c-4a2e-85de-4be126f392f4_360.jpg",
|
||||
"thumb_360_w": 360,
|
||||
"thumb_360_h": 360,
|
||||
"thumb_480": "https://files.slack.com/files-tmb/T06NRA6HM3P-F079E4173BL-6e091e0261/5e44bcbc-e43c-4a2e-85de-4be126f392f4_480.jpg",
|
||||
"thumb_480_w": 480,
|
||||
"thumb_480_h": 480,
|
||||
"thumb_160": "https://files.slack.com/files-tmb/T06NRA6HM3P-F079E4173BL-6e091e0261/5e44bcbc-e43c-4a2e-85de-4be126f392f4_160.jpg",
|
||||
"thumb_720": "https://files.slack.com/files-tmb/T06NRA6HM3P-F079E4173BL-6e091e0261/5e44bcbc-e43c-4a2e-85de-4be126f392f4_720.jpg",
|
||||
"thumb_720_w": 720,
|
||||
"thumb_720_h": 720,
|
||||
"thumb_800": "https://files.slack.com/files-tmb/T06NRA6HM3P-F079E4173BL-6e091e0261/5e44bcbc-e43c-4a2e-85de-4be126f392f4_800.jpg",
|
||||
"thumb_800_w": 800,
|
||||
"thumb_800_h": 800,
|
||||
"thumb_960": "https://files.slack.com/files-tmb/T06NRA6HM3P-F079E4173BL-6e091e0261/5e44bcbc-e43c-4a2e-85de-4be126f392f4_960.jpg",
|
||||
"thumb_960_w": 960,
|
||||
"thumb_960_h": 960,
|
||||
"thumb_1024": "https://files.slack.com/files-tmb/T06NRA6HM3P-F079E4173BL-6e091e0261/5e44bcbc-e43c-4a2e-85de-4be126f392f4_1024.jpg",
|
||||
"thumb_1024_w": 1024,
|
||||
"thumb_1024_h": 1024,
|
||||
"original_w": 3468,
|
||||
"original_h": 3468,
|
||||
"thumb_tiny": "AwAwADCzSHgZxmnUhqRkYcYyccnHFOIqIMMkqMYqUHIoAYRTTTzTTQBPUcg6MM5HpT80132igBgyfYd+MUwEqcADj0pcqM54JHIppfI9e3WmBLkdqaaSMnacjFBpAS5ozTc0ZoARk3HO7FN2gDHUfyp2aQmgAz2700mgmmk0Af/Z",
|
||||
"permalink": "https://ds-py62195.slack.com/files/U06NU4E26M9/F079E4173BL/5e44bcbc-e43c-4a2e-85de-4be126f392f4.jpg",
|
||||
"permalink_public": "https://slack-files.com/T06NRA6HM3P-F079E4173BL-b1d2ae27be",
|
||||
"has_rich_preview": false,
|
||||
"file_access": "visible"
|
||||
},
|
||||
{
|
||||
"id": "F079GJ49X4L",
|
||||
"created": 1719209792,
|
||||
"timestamp": 1719209792,
|
||||
"name": "notif_bot.png",
|
||||
"title": "notif_bot.png",
|
||||
"mimetype": "image/png",
|
||||
"filetype": "png",
|
||||
"pretty_type": "PNG",
|
||||
"user": "U06NU4E26M9",
|
||||
"user_team": "T06NRA6HM3P",
|
||||
"editable": false,
|
||||
"size": 45054,
|
||||
"mode": "hosted",
|
||||
"is_external": false,
|
||||
"external_type": "",
|
||||
"is_public": true,
|
||||
"public_url_shared": false,
|
||||
"display_as_bot": false,
|
||||
"username": "",
|
||||
"url_private": "https://files.slack.com/files-pri/T06NRA6HM3P-F079GJ49X4L/notif_bot.png",
|
||||
"url_private_download": "https://files.slack.com/files-pri/T06NRA6HM3P-F079GJ49X4L/download/notif_bot.png",
|
||||
"media_display_type": "unknown",
|
||||
"thumb_64": "https://files.slack.com/files-tmb/T06NRA6HM3P-F079GJ49X4L-5f45c24098/notif_bot_64.png",
|
||||
"thumb_80": "https://files.slack.com/files-tmb/T06NRA6HM3P-F079GJ49X4L-5f45c24098/notif_bot_80.png",
|
||||
"thumb_360": "https://files.slack.com/files-tmb/T06NRA6HM3P-F079GJ49X4L-5f45c24098/notif_bot_360.png",
|
||||
"thumb_360_w": 360,
|
||||
"thumb_360_h": 360,
|
||||
"thumb_480": "https://files.slack.com/files-tmb/T06NRA6HM3P-F079GJ49X4L-5f45c24098/notif_bot_480.png",
|
||||
"thumb_480_w": 480,
|
||||
"thumb_480_h": 480,
|
||||
"thumb_160": "https://files.slack.com/files-tmb/T06NRA6HM3P-F079GJ49X4L-5f45c24098/notif_bot_160.png",
|
||||
"original_w": 500,
|
||||
"original_h": 500,
|
||||
"thumb_tiny": "AwAwADBbKzia3V5F3M3PParH2O3/AOeS0WX/AB5xfSnC4Q3DQYO4DPtSEN+x2/8AzyWj7Hb/APPJanqOd/KhdxjIHGfWgBn2O3/55LVe9s4lt2eNdrLzx3q3AZDEpmAD9wKZe/8AHnL9KAEtGCWMbMcALkmmSs8wDRQSb1OVcgD/ACKZ/wAwlT6AH9auTb/JcxffxxQMi3XLdIUX/ef/AApssVxIo8ySJFBB4BP86fbb8vnzNnG3zOvvS3ELSMjKqPtyNr9Oe9ADJBPChlMokC8lSgGRReHNlIR0K091EVkUkbgIQSfpUU2f7MO7g+WM/pQIW1UPYIjdGXBpwW5UBRLGQOASpzVeyvIlt1SRtrLxz3qx9st/+eq0AMk85MbrhyWOAsaDJpERXfa73IYjIDnGfyoe6gNxCwkXC7sn8KJLqA3MTCVcBWyfyoGSrawqwbZkjuxJpL3/AI85fpR9st/+eq1XvbyJrdkjbczccdqBH//Z",
|
||||
"permalink": "https://ds-py62195.slack.com/files/U06NU4E26M9/F079GJ49X4L/notif_bot.png",
|
||||
"permalink_public": "https://slack-files.com/T06NRA6HM3P-F079GJ49X4L-b7e63652ec",
|
||||
"has_rich_preview": false,
|
||||
"file_access": "visible"
|
||||
},
|
||||
{
|
||||
"id": "F07A2TA6PPS",
|
||||
"created": 1719209799,
|
||||
"timestamp": 1719209799,
|
||||
"name": "books.jpg",
|
||||
"title": "books.jpg",
|
||||
"mimetype": "image/jpeg",
|
||||
"filetype": "jpg",
|
||||
"pretty_type": "JPEG",
|
||||
"user": "U06NU4E26M9",
|
||||
"user_team": "T06NRA6HM3P",
|
||||
"editable": false,
|
||||
"size": 1406170,
|
||||
"mode": "hosted",
|
||||
"is_external": false,
|
||||
"external_type": "",
|
||||
"is_public": true,
|
||||
"public_url_shared": false,
|
||||
"display_as_bot": false,
|
||||
"username": "",
|
||||
"url_private": "https://files.slack.com/files-pri/T06NRA6HM3P-F07A2TA6PPS/books.jpg",
|
||||
"url_private_download": "https://files.slack.com/files-pri/T06NRA6HM3P-F07A2TA6PPS/download/books.jpg",
|
||||
"media_display_type": "unknown",
|
||||
"thumb_64": "https://files.slack.com/files-tmb/T06NRA6HM3P-F07A2TA6PPS-b8f86f8918/books_64.jpg",
|
||||
"thumb_80": "https://files.slack.com/files-tmb/T06NRA6HM3P-F07A2TA6PPS-b8f86f8918/books_80.jpg",
|
||||
"thumb_360": "https://files.slack.com/files-tmb/T06NRA6HM3P-F07A2TA6PPS-b8f86f8918/books_360.jpg",
|
||||
"thumb_360_w": 360,
|
||||
"thumb_360_h": 162,
|
||||
"thumb_480": "https://files.slack.com/files-tmb/T06NRA6HM3P-F07A2TA6PPS-b8f86f8918/books_480.jpg",
|
||||
"thumb_480_w": 480,
|
||||
"thumb_480_h": 216,
|
||||
"thumb_160": "https://files.slack.com/files-tmb/T06NRA6HM3P-F07A2TA6PPS-b8f86f8918/books_160.jpg",
|
||||
"thumb_720": "https://files.slack.com/files-tmb/T06NRA6HM3P-F07A2TA6PPS-b8f86f8918/books_720.jpg",
|
||||
"thumb_720_w": 720,
|
||||
"thumb_720_h": 324,
|
||||
"thumb_800": "https://files.slack.com/files-tmb/T06NRA6HM3P-F07A2TA6PPS-b8f86f8918/books_800.jpg",
|
||||
"thumb_800_w": 800,
|
||||
"thumb_800_h": 360,
|
||||
"thumb_960": "https://files.slack.com/files-tmb/T06NRA6HM3P-F07A2TA6PPS-b8f86f8918/books_960.jpg",
|
||||
"thumb_960_w": 960,
|
||||
"thumb_960_h": 432,
|
||||
"thumb_1024": "https://files.slack.com/files-tmb/T06NRA6HM3P-F07A2TA6PPS-b8f86f8918/books_1024.jpg",
|
||||
"thumb_1024_w": 1024,
|
||||
"thumb_1024_h": 461,
|
||||
"original_w": 4624,
|
||||
"original_h": 2080,
|
||||
"thumb_tiny": "AwAVADCikrg8HP1rVWBD16/WsuAFpkBHGa0w5z96kA2aCMnByMdCDzVaW2JJkeQuT0yKtSZZs55FROCsBBOeaBlRwwByRj2qvVmQnb9fSoIxk80IGhYpDE+4DNTi8P8Ac/WqtA60xFhrpy2RgUxriRlIJGD7VGetJQAu4+tKn3qbTk+9QB//2Q==",
|
||||
"permalink": "https://ds-py62195.slack.com/files/U06NU4E26M9/F07A2TA6PPS/books.jpg",
|
||||
"permalink_public": "https://slack-files.com/T06NRA6HM3P-F07A2TA6PPS-44b4516dd5",
|
||||
"has_rich_preview": false,
|
||||
"file_access": "visible"
|
||||
}
|
||||
],
|
||||
"upload": false,
|
||||
"user": "U06NU4E26M9",
|
||||
"type": "message",
|
||||
"ts": "1719209815.105159",
|
||||
"client_msg_id": "15117486-b4e1-42a9-a553-ca2886d94222",
|
||||
"channel": "C06NRA6JLER",
|
||||
"subtype": "file_share",
|
||||
"event_ts": "1719209815.105159",
|
||||
"channel_type": "channel"
|
||||
},
|
||||
"type": "event_callback",
|
||||
"event_id": "Ev079GJ5KG5A",
|
||||
"event_time": 1719209815,
|
||||
"authorizations": [
|
||||
{
|
||||
"enterprise_id": null,
|
||||
"team_id": "T06NRA6HM3P",
|
||||
"user_id": "U074G5E1ANR",
|
||||
"is_bot": true,
|
||||
"is_enterprise_install": false
|
||||
}
|
||||
],
|
||||
"is_ext_shared_channel": false,
|
||||
"event_context": "4-eyJldCI6Im1lc3NhZ2UiLCJ0aWQiOiJUMDZOUkE2SE0zUCIsImFpZCI6IkEwNzU3Uk0zMUhOIiwiY2lkIjoiQzA2TlJBNkpMRVIifQ"
|
||||
}
|
||||
52
zerver/webhooks/slack/fixtures/message_with_inline_code.json
Normal file
52
zerver/webhooks/slack/fixtures/message_with_inline_code.json
Normal file
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"token": "CqCsBJmXoNSHaNCt3wGWYSEe",
|
||||
"team_id": "T06NRA6HM3P",
|
||||
"context_team_id": "T06NRA6HM3P",
|
||||
"context_enterprise_id": null,
|
||||
"api_app_id": "A0757RM31HN",
|
||||
"event": {
|
||||
"user": "U06NU4E26M9",
|
||||
"type": "message",
|
||||
"ts": "1719211825.087659",
|
||||
"client_msg_id": "ad34b81a-017a-4468-9dfe-6ec77aeb936b",
|
||||
"text": "`asdasda this is a code block`",
|
||||
"team": "T06NRA6HM3P",
|
||||
"blocks": [
|
||||
{
|
||||
"type": "rich_text",
|
||||
"block_id": "DkA3r",
|
||||
"elements": [
|
||||
{
|
||||
"type": "rich_text_section",
|
||||
"elements": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "asdasda this is a code block",
|
||||
"style": {
|
||||
"code": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"channel": "C06NRA6JLER",
|
||||
"event_ts": "1719211825.087659",
|
||||
"channel_type": "channel"
|
||||
},
|
||||
"type": "event_callback",
|
||||
"event_id": "Ev078ZLS9GNT",
|
||||
"event_time": 1719211825,
|
||||
"authorizations": [
|
||||
{
|
||||
"enterprise_id": null,
|
||||
"team_id": "T06NRA6HM3P",
|
||||
"user_id": "U074G5E1ANR",
|
||||
"is_bot": true,
|
||||
"is_enterprise_install": false
|
||||
}
|
||||
],
|
||||
"is_ext_shared_channel": false,
|
||||
"event_context": "4-eyJldCI6Im1lc3NhZ2UiLCJ0aWQiOiJUMDZOUkE2SE0zUCIsImFpZCI6IkEwNzU3Uk0zMUhOIiwiY2lkIjoiQzA2TlJBNkpMRVIifQ"
|
||||
}
|
||||
49
zerver/webhooks/slack/fixtures/message_with_normal_text.json
Normal file
49
zerver/webhooks/slack/fixtures/message_with_normal_text.json
Normal file
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"token": "CqCsBJmXoNSHaNCt3wGWYSEe",
|
||||
"team_id": "T06NRA6HM3P",
|
||||
"context_team_id": "T06NRA6HM3P",
|
||||
"context_enterprise_id": null,
|
||||
"api_app_id": "A0757RM31HN",
|
||||
"event": {
|
||||
"user": "U06NU4E26M9",
|
||||
"type": "message",
|
||||
"ts": "1719209506.817929",
|
||||
"client_msg_id": "7cc3cc17-2fed-4356-b3c2-48106ab3a209",
|
||||
"text": "Hello, this is a normal text message",
|
||||
"team": "T06NRA6HM3P",
|
||||
"blocks": [
|
||||
{
|
||||
"type": "rich_text",
|
||||
"block_id": "Xh7lJ",
|
||||
"elements": [
|
||||
{
|
||||
"type": "rich_text_section",
|
||||
"elements": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Hello, this is a normal text message"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"channel": "C06NRA6JLER",
|
||||
"event_ts": "1719209506.817929",
|
||||
"channel_type": "channel"
|
||||
},
|
||||
"type": "event_callback",
|
||||
"event_id": "Ev079SP4TP1P",
|
||||
"event_time": 1719209506,
|
||||
"authorizations": [
|
||||
{
|
||||
"enterprise_id": null,
|
||||
"team_id": "T06NRA6HM3P",
|
||||
"user_id": "U074G5E1ANR",
|
||||
"is_bot": true,
|
||||
"is_enterprise_install": false
|
||||
}
|
||||
],
|
||||
"is_ext_shared_channel": false,
|
||||
"event_context": "4-eyJldCI6Im1lc3NhZ2UiLCJ0aWQiOiJUMDZOUkE2SE0zUCIsImFpZCI6IkEwNzU3Uk0zMUhOIiwiY2lkIjoiQzA2TlJBNkpMRVIifQ"
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
{
|
||||
"token": "CqCsBJmXoNSHaNCt3wGWYSEe",
|
||||
"team_id": "T06NRA6HM3P",
|
||||
"context_team_id": "T06NRA6HM3P",
|
||||
"context_enterprise_id": null,
|
||||
"api_app_id": "A0757RM31HN",
|
||||
"event": {
|
||||
"user": "U06NU4E26M9",
|
||||
"type": "message",
|
||||
"ts": "1719211791.356739",
|
||||
"client_msg_id": "a0d2e7ba-c057-45df-b59a-d4e38f4ac055",
|
||||
"text": "1. point one\n2. point two\n3. mix both\n4. pour water\n5. etc",
|
||||
"team": "T06NRA6HM3P",
|
||||
"blocks": [
|
||||
{
|
||||
"type": "rich_text",
|
||||
"block_id": "G+/T6",
|
||||
"elements": [
|
||||
{
|
||||
"type": "rich_text_list",
|
||||
"elements": [
|
||||
{
|
||||
"type": "rich_text_section",
|
||||
"elements": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "point one"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "rich_text_section",
|
||||
"elements": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "point two"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "rich_text_section",
|
||||
"elements": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "mix both"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "rich_text_section",
|
||||
"elements": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "pour water"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "rich_text_section",
|
||||
"elements": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "etc"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"style": "ordered",
|
||||
"indent": 0,
|
||||
"border": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"channel": "C06NRA6JLER",
|
||||
"event_ts": "1719211791.356739",
|
||||
"channel_type": "channel"
|
||||
},
|
||||
"type": "event_callback",
|
||||
"event_id": "Ev079STB112M",
|
||||
"event_time": 1719211791,
|
||||
"authorizations": [
|
||||
{
|
||||
"enterprise_id": null,
|
||||
"team_id": "T06NRA6HM3P",
|
||||
"user_id": "U074G5E1ANR",
|
||||
"is_bot": true,
|
||||
"is_enterprise_install": false
|
||||
}
|
||||
],
|
||||
"is_ext_shared_channel": false,
|
||||
"event_context": "4-eyJldCI6Im1lc3NhZ2UiLCJ0aWQiOiJUMDZOUkE2SE0zUCIsImFpZCI6IkEwNzU3Uk0zMUhOIiwiY2lkIjoiQzA2TlJBNkpMRVIifQ"
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"token": "CqCsBJmXoNSHaNCt3wGWYSEe",
|
||||
"team_id": "T06NRA6HM3P",
|
||||
"context_team_id": "T06NRA6HM3P",
|
||||
"context_enterprise_id": null,
|
||||
"api_app_id": "A0757RM31HN",
|
||||
"event": {
|
||||
"user": "U06NU4E26M9",
|
||||
"type": "message",
|
||||
"ts": "1719209649.631399",
|
||||
"client_msg_id": "ec2b7c8f-8530-43cc-9287-9f7617d5d335",
|
||||
"text": "<@U074VRHQ11T> <@U074G5E1ANR> <@U06NU4E26M9> hello, this is a message with mentions",
|
||||
"team": "T06NRA6HM3P",
|
||||
"blocks": [
|
||||
{
|
||||
"type": "rich_text",
|
||||
"block_id": "UST8x",
|
||||
"elements": [
|
||||
{
|
||||
"type": "rich_text_section",
|
||||
"elements": [
|
||||
{
|
||||
"type": "user",
|
||||
"user_id": "U074VRHQ11T"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": " "
|
||||
},
|
||||
{
|
||||
"type": "user",
|
||||
"user_id": "U074G5E1ANR"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": " "
|
||||
},
|
||||
{
|
||||
"type": "user",
|
||||
"user_id": "U06NU4E26M9"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": " hello, this is a message with mentions"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"channel": "C06NRA6JLER",
|
||||
"event_ts": "1719209649.631399",
|
||||
"channel_type": "channel"
|
||||
},
|
||||
"type": "event_callback",
|
||||
"event_id": "Ev079B6N3H53",
|
||||
"event_time": 1719209649,
|
||||
"authorizations": [
|
||||
{
|
||||
"enterprise_id": null,
|
||||
"team_id": "T06NRA6HM3P",
|
||||
"user_id": "U074G5E1ANR",
|
||||
"is_bot": true,
|
||||
"is_enterprise_install": false
|
||||
}
|
||||
],
|
||||
"is_ext_shared_channel": false,
|
||||
"event_context": "4-eyJldCI6Im1lc3NhZ2UiLCJ0aWQiOiJUMDZOUkE2SE0zUCIsImFpZCI6IkEwNzU3Uk0zMUhOIiwiY2lkIjoiQzA2TlJBNkpMRVIifQ"
|
||||
}
|
||||
222
zerver/webhooks/slack/fixtures/message_with_variety_files.json
Normal file
222
zerver/webhooks/slack/fixtures/message_with_variety_files.json
Normal file
@@ -0,0 +1,222 @@
|
||||
{
|
||||
"token": "CqCsBJmXoNSHaNCt3wGWYSEe",
|
||||
"team_id": "T06NRA6HM3P",
|
||||
"context_team_id": "T06NRA6HM3P",
|
||||
"context_enterprise_id": null,
|
||||
"api_app_id": "A0757RM31HN",
|
||||
"event": {
|
||||
"text": "Message with an assortment of file types",
|
||||
"files": [
|
||||
{
|
||||
"id": "F079E4CMY5Q",
|
||||
"created": 1719209974,
|
||||
"timestamp": 1719209974,
|
||||
"name": "postman-agent-0.4.25-linux-x64.tar.gz",
|
||||
"title": "postman-agent-0.4.25-linux-x64.tar.gz",
|
||||
"mimetype": "application/x-gzip",
|
||||
"filetype": "gzip",
|
||||
"pretty_type": "Gzip",
|
||||
"user": "U06NU4E26M9",
|
||||
"user_team": "T06NRA6HM3P",
|
||||
"editable": false,
|
||||
"size": 107287179,
|
||||
"mode": "hosted",
|
||||
"is_external": false,
|
||||
"external_type": "",
|
||||
"is_public": true,
|
||||
"public_url_shared": false,
|
||||
"display_as_bot": false,
|
||||
"username": "",
|
||||
"url_private": "https://files.slack.com/files-pri/T06NRA6HM3P-F079E4CMY5Q/postman-agent-0.4.25-linux-x64.tar.gz",
|
||||
"url_private_download": "https://files.slack.com/files-pri/T06NRA6HM3P-F079E4CMY5Q/download/postman-agent-0.4.25-linux-x64.tar.gz",
|
||||
"media_display_type": "unknown",
|
||||
"permalink": "https://ds-py62195.slack.com/files/U06NU4E26M9/F079E4CMY5Q/postman-agent-0.4.25-linux-x64.tar.gz",
|
||||
"permalink_public": "https://slack-files.com/T06NRA6HM3P-F079E4CMY5Q-22ef8b1a63",
|
||||
"has_rich_preview": false,
|
||||
"file_access": "visible"
|
||||
},
|
||||
{
|
||||
"id": "F079SQ33CBT",
|
||||
"created": 1719210050,
|
||||
"timestamp": 1719210050,
|
||||
"name": "discord-0.0.55.deb",
|
||||
"title": "discord-0.0.55.deb",
|
||||
"mimetype": "application/octet-stream",
|
||||
"filetype": "binary",
|
||||
"pretty_type": "Binary",
|
||||
"user": "U06NU4E26M9",
|
||||
"user_team": "T06NRA6HM3P",
|
||||
"editable": false,
|
||||
"size": 100957736,
|
||||
"mode": "hosted",
|
||||
"is_external": false,
|
||||
"external_type": "",
|
||||
"is_public": true,
|
||||
"public_url_shared": false,
|
||||
"display_as_bot": false,
|
||||
"username": "",
|
||||
"url_private": "https://files.slack.com/files-pri/T06NRA6HM3P-F079SQ33CBT/discord-0.0.55.deb",
|
||||
"url_private_download": "https://files.slack.com/files-pri/T06NRA6HM3P-F079SQ33CBT/download/discord-0.0.55.deb",
|
||||
"media_display_type": "unknown",
|
||||
"permalink": "https://ds-py62195.slack.com/files/U06NU4E26M9/F079SQ33CBT/discord-0.0.55.deb",
|
||||
"permalink_public": "https://slack-files.com/T06NRA6HM3P-F079SQ33CBT-2438647ea1",
|
||||
"has_rich_preview": false,
|
||||
"file_access": "visible"
|
||||
},
|
||||
{
|
||||
"id": "F079SQ721A5",
|
||||
"created": 1719210117,
|
||||
"timestamp": 1719210117,
|
||||
"name": "Slack-bot-scopes-List.xlsx",
|
||||
"title": "Slack-bot-scopes-List.xlsx",
|
||||
"mimetype": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
"filetype": "xlsx",
|
||||
"pretty_type": "Excel Spreadsheet",
|
||||
"user": "U06NU4E26M9",
|
||||
"user_team": "T06NRA6HM3P",
|
||||
"editable": false,
|
||||
"size": 75813,
|
||||
"mode": "hosted",
|
||||
"is_external": false,
|
||||
"external_type": "",
|
||||
"is_public": true,
|
||||
"public_url_shared": false,
|
||||
"display_as_bot": false,
|
||||
"username": "",
|
||||
"url_private": "https://files.slack.com/files-pri/T06NRA6HM3P-F079SQ721A5/slack-bot-scopes-list.xlsx",
|
||||
"url_private_download": "https://files.slack.com/files-pri/T06NRA6HM3P-F079SQ721A5/download/slack-bot-scopes-list.xlsx",
|
||||
"media_display_type": "unknown",
|
||||
"converted_pdf": "https://files.slack.com/files-tmb/T06NRA6HM3P-F079SQ721A5-315d1a255c/slack-bot-scopes-list_converted.pdf",
|
||||
"thumb_pdf": "https://files.slack.com/files-tmb/T06NRA6HM3P-F079SQ721A5-315d1a255c/slack-bot-scopes-list_thumb_pdf.png",
|
||||
"thumb_pdf_w": 1210,
|
||||
"thumb_pdf_h": 935,
|
||||
"permalink": "https://ds-py62195.slack.com/files/U06NU4E26M9/F079SQ721A5/slack-bot-scopes-list.xlsx",
|
||||
"permalink_public": "https://slack-files.com/T06NRA6HM3P-F079SQ721A5-77cdc07a4d",
|
||||
"has_rich_preview": false,
|
||||
"file_access": "visible"
|
||||
},
|
||||
{
|
||||
"id": "F079B7G7NUD",
|
||||
"created": 1719210121,
|
||||
"timestamp": 1719210121,
|
||||
"name": "wallpaper.jpg",
|
||||
"title": "wallpaper.jpg",
|
||||
"mimetype": "image/jpeg",
|
||||
"filetype": "jpg",
|
||||
"pretty_type": "JPEG",
|
||||
"user": "U06NU4E26M9",
|
||||
"user_team": "T06NRA6HM3P",
|
||||
"editable": false,
|
||||
"size": 1572520,
|
||||
"mode": "hosted",
|
||||
"is_external": false,
|
||||
"external_type": "",
|
||||
"is_public": true,
|
||||
"public_url_shared": false,
|
||||
"display_as_bot": false,
|
||||
"username": "",
|
||||
"url_private": "https://files.slack.com/files-pri/T06NRA6HM3P-F079B7G7NUD/wallpaper.jpg",
|
||||
"url_private_download": "https://files.slack.com/files-pri/T06NRA6HM3P-F079B7G7NUD/download/wallpaper.jpg",
|
||||
"media_display_type": "unknown",
|
||||
"thumb_64": "https://files.slack.com/files-tmb/T06NRA6HM3P-F079B7G7NUD-3b2ba7aee1/wallpaper_64.jpg",
|
||||
"thumb_80": "https://files.slack.com/files-tmb/T06NRA6HM3P-F079B7G7NUD-3b2ba7aee1/wallpaper_80.jpg",
|
||||
"thumb_360": "https://files.slack.com/files-tmb/T06NRA6HM3P-F079B7G7NUD-3b2ba7aee1/wallpaper_360.jpg",
|
||||
"thumb_360_w": 360,
|
||||
"thumb_360_h": 203,
|
||||
"thumb_480": "https://files.slack.com/files-tmb/T06NRA6HM3P-F079B7G7NUD-3b2ba7aee1/wallpaper_480.jpg",
|
||||
"thumb_480_w": 480,
|
||||
"thumb_480_h": 270,
|
||||
"thumb_160": "https://files.slack.com/files-tmb/T06NRA6HM3P-F079B7G7NUD-3b2ba7aee1/wallpaper_160.jpg",
|
||||
"thumb_720": "https://files.slack.com/files-tmb/T06NRA6HM3P-F079B7G7NUD-3b2ba7aee1/wallpaper_720.jpg",
|
||||
"thumb_720_w": 720,
|
||||
"thumb_720_h": 405,
|
||||
"thumb_800": "https://files.slack.com/files-tmb/T06NRA6HM3P-F079B7G7NUD-3b2ba7aee1/wallpaper_800.jpg",
|
||||
"thumb_800_w": 800,
|
||||
"thumb_800_h": 450,
|
||||
"thumb_960": "https://files.slack.com/files-tmb/T06NRA6HM3P-F079B7G7NUD-3b2ba7aee1/wallpaper_960.jpg",
|
||||
"thumb_960_w": 960,
|
||||
"thumb_960_h": 540,
|
||||
"thumb_1024": "https://files.slack.com/files-tmb/T06NRA6HM3P-F079B7G7NUD-3b2ba7aee1/wallpaper_1024.jpg",
|
||||
"thumb_1024_w": 1024,
|
||||
"thumb_1024_h": 576,
|
||||
"original_w": 3840,
|
||||
"original_h": 2160,
|
||||
"thumb_tiny": "AwAbADCUU8U0UyZyAFQZPemIl3qKFO7J7VVA7u3PoKl2sMKG4PpTESGmmiMNtbJzg8Gg0hirVadH356irIpJPu5oQFWD5nIP3hyPp6VZkYqq46ngVXT/AFwqyedmfU0xEka7U/CmSAA8U9+MVEaQz//Z",
|
||||
"permalink": "https://ds-py62195.slack.com/files/U06NU4E26M9/F079B7G7NUD/wallpaper.jpg",
|
||||
"permalink_public": "https://slack-files.com/T06NRA6HM3P-F079B7G7NUD-c461327fc3",
|
||||
"has_rich_preview": false,
|
||||
"file_access": "visible"
|
||||
},
|
||||
{
|
||||
"id": "F07A2TVKNQ0",
|
||||
"created": 1719210129,
|
||||
"timestamp": 1719210129,
|
||||
"name": "TestPDFfile.pdf",
|
||||
"title": "TestPDFfile.pdf",
|
||||
"mimetype": "application/pdf",
|
||||
"filetype": "pdf",
|
||||
"pretty_type": "PDF",
|
||||
"user": "U06NU4E26M9",
|
||||
"user_team": "T06NRA6HM3P",
|
||||
"editable": false,
|
||||
"size": 83186,
|
||||
"mode": "hosted",
|
||||
"is_external": false,
|
||||
"external_type": "",
|
||||
"is_public": true,
|
||||
"public_url_shared": false,
|
||||
"display_as_bot": false,
|
||||
"username": "",
|
||||
"url_private": "https://files.slack.com/files-pri/T06NRA6HM3P-F07A2TVKNQ0/testpdffile.pdf",
|
||||
"url_private_download": "https://files.slack.com/files-pri/T06NRA6HM3P-F07A2TVKNQ0/download/testpdffile.pdf",
|
||||
"media_display_type": "unknown",
|
||||
"thumb_pdf": "https://files.slack.com/files-tmb/T06NRA6HM3P-F07A2TVKNQ0-67f8edb672/testpdffile_thumb_pdf.png",
|
||||
"thumb_pdf_w": 935,
|
||||
"thumb_pdf_h": 1210,
|
||||
"permalink": "https://ds-py62195.slack.com/files/U06NU4E26M9/F07A2TVKNQ0/testpdffile.pdf",
|
||||
"permalink_public": "https://slack-files.com/T06NRA6HM3P-F07A2TVKNQ0-4507eb87a4",
|
||||
"has_rich_preview": false,
|
||||
"file_access": "visible"
|
||||
},
|
||||
{
|
||||
"id": "F07A2TVQ7C0",
|
||||
"created": 1719210132,
|
||||
"timestamp": 1719210132,
|
||||
"name": "channels.json",
|
||||
"title": "channels.json",
|
||||
"mimetype": "text/plain",
|
||||
"filetype": "json",
|
||||
"pretty_type": "JSON",
|
||||
"user": "U06NU4E26M9",
|
||||
"user_team": "T06NRA6HM3P",
|
||||
"editable": true,
|
||||
"size": 1563,
|
||||
"mode": "snippet",
|
||||
"is_external": false,
|
||||
"external_type": "",
|
||||
"is_public": false,
|
||||
"public_url_shared": false,
|
||||
"display_as_bot": false,
|
||||
"username": "",
|
||||
"url_private": "https://files.slack.com/files-pri/T06NRA6HM3P-F07A2TVQ7C0/channels.json",
|
||||
"url_private_download": "https://files.slack.com/files-pri/T06NRA6HM3P-F07A2TVQ7C0/download/channels.json",
|
||||
"permalink": "https://ds-py62195.slack.com/files/U06NU4E26M9/F07A2TVQ7C0/channels.json",
|
||||
"permalink_public": "https://slack-files.com/T06NRA6HM3P-F07A2TVQ7C0-5b1d277896",
|
||||
"edit_link": "https://ds-py62195.slack.com/files/U06NU4E26M9/F07A2TVQ7C0/channels.json/edit",
|
||||
"preview": "[\n{\n \"id\": \"C06NRA6JLER\",\n \"name\": \"random\",\n \"created\": 1710128735,",
|
||||
"preview_highlight": "<div class="
|
||||
}
|
||||
],
|
||||
"upload": false,
|
||||
"type": "message",
|
||||
"user": "U06NU4E26M9",
|
||||
"ts": "1719209815.105159",
|
||||
"client_msg_id": "15117486-b4e1-42a9-a553-ca2886d94222",
|
||||
"channel": "C06NRA6JLER",
|
||||
"subtype": "file_share",
|
||||
"event_ts": "1719209815.105159",
|
||||
"channel_type": "channel"
|
||||
},
|
||||
"type": "event_callback"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"token": "CqCsBJmXoNSHaNCt3wGWYSEe",
|
||||
"team_id": "T06NRA6HM3P",
|
||||
"context_team_id": "T06NRA6HM3P",
|
||||
"context_enterprise_id": null,
|
||||
"api_app_id": "A0757RM31HN",
|
||||
"event": {
|
||||
"user": "U06NU4E26M9",
|
||||
"type": "message",
|
||||
"ts": "1719211249.499799",
|
||||
"client_msg_id": "49d50b19-e55e-452b-a6c2-8c5e1d4b3ef9",
|
||||
"text": "<!channel> <!here> Sorry for mentioning. This is for the test fixtures for the Slack integration update PR I'm working on and can't be done in a private channel. :bow:",
|
||||
"team": "T06NRA6HM3P",
|
||||
"blocks": [
|
||||
{
|
||||
"type": "rich_text",
|
||||
"block_id": "298sd",
|
||||
"elements": [
|
||||
{
|
||||
"type": "rich_text_section",
|
||||
"elements": [
|
||||
{
|
||||
"type": "broadcast",
|
||||
"range": "channel"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": " "
|
||||
},
|
||||
{
|
||||
"type": "broadcast",
|
||||
"range": "here"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": " Sorry for mentioning. This is for the test fixtures for the Slack integration update PR I'm working on and can't be done in a private channel. "
|
||||
},
|
||||
{
|
||||
"type": "emoji",
|
||||
"name": "bow",
|
||||
"unicode": "1f647"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"channel": "C06NRA6JLER",
|
||||
"event_ts": "1719211249.499799",
|
||||
"channel_type": "channel"
|
||||
},
|
||||
"type": "event_callback",
|
||||
"event_id": "Ev079GLT13NY",
|
||||
"event_time": 1719211249,
|
||||
"authorizations": [
|
||||
{
|
||||
"enterprise_id": null,
|
||||
"team_id": "T06NRA6HM3P",
|
||||
"user_id": "U074G5E1ANR",
|
||||
"is_bot": true,
|
||||
"is_enterprise_install": false
|
||||
}
|
||||
],
|
||||
"is_ext_shared_channel": false,
|
||||
"event_context": "4-eyJldCI6Im1lc3NhZ2UiLCJ0aWQiOiJUMDZOUkE2SE0zUCIsImFpZCI6IkEwNzU3Uk0zMUhOIiwiY2lkIjoiQzA2TlJBNkpMRVIifQ"
|
||||
}
|
||||
@@ -3,7 +3,14 @@ from typing_extensions import override
|
||||
from zerver.lib.test_classes import WebhookTestCase
|
||||
|
||||
EXPECTED_TOPIC = "Message from Slack"
|
||||
EXPECTED_MESSAGE = "**slack_user**: test"
|
||||
|
||||
MESSAGE_WITH_NORMAL_TEXT = "Hello, this is a normal text message"
|
||||
USER = "U06NU4E26M9"
|
||||
CHANNEL = "C06NRA6JLER"
|
||||
EXPECTED_MESSAGE = "**{user}**: {message}"
|
||||
TOPIC_WITH_CHANNEL = "channel: {channel}"
|
||||
|
||||
LEGACY_USER = "slack_user"
|
||||
|
||||
|
||||
class SlackWebhookTests(WebhookTestCase):
|
||||
@@ -12,50 +19,270 @@ class SlackWebhookTests(WebhookTestCase):
|
||||
WEBHOOK_DIR_NAME = "slack"
|
||||
|
||||
def test_slack_only_stream_parameter(self) -> None:
|
||||
expected_message = EXPECTED_MESSAGE.format(user=USER, message=MESSAGE_WITH_NORMAL_TEXT)
|
||||
self.check_webhook(
|
||||
"message_with_normal_text",
|
||||
EXPECTED_TOPIC,
|
||||
expected_message,
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
def test_slack_with_user_specified_topic(self) -> None:
|
||||
expected_topic_name = "test"
|
||||
self.url = self.build_webhook_url(topic=expected_topic_name)
|
||||
expected_message = EXPECTED_MESSAGE.format(user=USER, message=MESSAGE_WITH_NORMAL_TEXT)
|
||||
self.check_webhook(
|
||||
"message_with_normal_text",
|
||||
expected_topic_name,
|
||||
expected_message,
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
def test_slack_channels_map_to_topics_true(self) -> None:
|
||||
self.url = self.build_webhook_url(channels_map_to_topics="1")
|
||||
expected_message = EXPECTED_MESSAGE.format(user=USER, message=MESSAGE_WITH_NORMAL_TEXT)
|
||||
expected_topic_name = TOPIC_WITH_CHANNEL.format(channel=CHANNEL)
|
||||
self.check_webhook(
|
||||
"message_with_normal_text",
|
||||
expected_topic_name,
|
||||
expected_message,
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
def test_slack_channels_map_to_topics_true_and_user_specified_topic(self) -> None:
|
||||
expected_topic_name = "test"
|
||||
self.url = self.build_webhook_url(topic=expected_topic_name, channels_map_to_topics="1")
|
||||
expected_message = EXPECTED_MESSAGE.format(user=USER, message=MESSAGE_WITH_NORMAL_TEXT)
|
||||
self.check_webhook(
|
||||
"message_with_normal_text",
|
||||
expected_topic_name,
|
||||
expected_message,
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
def test_slack_channels_map_to_topics_false(self) -> None:
|
||||
self.CHANNEL_NAME = CHANNEL
|
||||
self.url = self.build_webhook_url(channels_map_to_topics="0")
|
||||
expected_message = EXPECTED_MESSAGE.format(user=USER, message=MESSAGE_WITH_NORMAL_TEXT)
|
||||
self.check_webhook(
|
||||
"message_with_normal_text",
|
||||
EXPECTED_TOPIC,
|
||||
expected_message,
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
def test_slack_channels_map_to_topics_false_and_user_specified_topic(self) -> None:
|
||||
self.CHANNEL_NAME = CHANNEL
|
||||
expected_topic_name = "test"
|
||||
self.url = self.build_webhook_url(topic=expected_topic_name, channels_map_to_topics="0")
|
||||
expected_message = EXPECTED_MESSAGE.format(user=USER, message=MESSAGE_WITH_NORMAL_TEXT)
|
||||
self.check_webhook(
|
||||
"message_with_normal_text",
|
||||
expected_topic_name,
|
||||
expected_message,
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
def test_invalid_channels_map_to_topics(self) -> None:
|
||||
payload = self.get_body("message_with_normal_text")
|
||||
url = self.build_webhook_url(channels_map_to_topics="abc")
|
||||
result = self.client_post(url, payload, content_type="application/json")
|
||||
self.assert_json_error(result, "Error: channels_map_to_topics parameter other than 0 or 1")
|
||||
|
||||
def test_challenge_handshake_payload(self) -> None:
|
||||
url = self.build_webhook_url(channels_map_to_topics="1")
|
||||
payload = self.get_body("challenge_handshake_payload")
|
||||
result = self.client_post(url, payload, content_type="application/json")
|
||||
expected_challenge_response = {
|
||||
"msg": "",
|
||||
"result": "success",
|
||||
"challenge": "3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P",
|
||||
}
|
||||
|
||||
self.assertJSONEqual(result.content.decode("utf-8"), expected_challenge_response)
|
||||
|
||||
def test_block_message_from_slack_bridge_bot(self) -> None:
|
||||
self.check_webhook(
|
||||
"message_from_slack_bridge_bot",
|
||||
"",
|
||||
"",
|
||||
content_type="application/json",
|
||||
expect_noop=True,
|
||||
)
|
||||
|
||||
def test_message_with_bullet_points(self) -> None:
|
||||
message_body = "• list three\n• list two"
|
||||
expected_message = EXPECTED_MESSAGE.format(user=USER, message=message_body)
|
||||
self.check_webhook(
|
||||
"message_with_bullet_points",
|
||||
EXPECTED_TOPIC,
|
||||
expected_message,
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
def test_message_with_channel_and_user_mentions(self) -> None:
|
||||
# TODO
|
||||
pass
|
||||
|
||||
def test_message_with_channel_mentions(self) -> None:
|
||||
message_body = "**#zulip-mirror** **#general** message with channel mentions"
|
||||
expected_message = EXPECTED_MESSAGE.format(user=USER, message=message_body)
|
||||
self.check_webhook(
|
||||
"message_with_channel_mentions",
|
||||
EXPECTED_TOPIC,
|
||||
expected_message,
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
def test_message_with_formatted_texts(self) -> None:
|
||||
message_body = "**Bold text** *italic text* ~~strikethrough~~"
|
||||
expected_message = EXPECTED_MESSAGE.format(user=USER, message=message_body)
|
||||
self.check_webhook(
|
||||
"message_with_formatted_texts",
|
||||
EXPECTED_TOPIC,
|
||||
expected_message,
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
def test_message_with_image_files(self) -> None:
|
||||
message_body = """
|
||||
*[5e44bcbc-e43c-4a2e-85de-4be126f392f4.jpg](https://ds-py62195.slack.com/files/U06NU4E26M9/F079E4173BL/5e44bcbc-e43c-4a2e-85de-4be126f392f4.jpg)*
|
||||
*[notif_bot.png](https://ds-py62195.slack.com/files/U06NU4E26M9/F079GJ49X4L/notif_bot.png)*
|
||||
*[books.jpg](https://ds-py62195.slack.com/files/U06NU4E26M9/F07A2TA6PPS/books.jpg)*"""
|
||||
expected_message = EXPECTED_MESSAGE.format(user=USER, message=message_body)
|
||||
self.check_webhook(
|
||||
"message_with_image_files",
|
||||
EXPECTED_TOPIC,
|
||||
expected_message,
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
def test_message_with_inline_code(self) -> None:
|
||||
message_body = "`asdasda this is a code block`"
|
||||
expected_message = EXPECTED_MESSAGE.format(user=USER, message=message_body)
|
||||
self.check_webhook(
|
||||
"message_with_inline_code",
|
||||
EXPECTED_TOPIC,
|
||||
expected_message,
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
def test_message_with_ordered_list(self) -> None:
|
||||
message_body = "1. point one\n2. point two\n3. mix both\n4. pour water\n5. etc"
|
||||
expected_message = EXPECTED_MESSAGE.format(user=USER, message=message_body)
|
||||
self.check_webhook(
|
||||
"message_with_ordered_list",
|
||||
EXPECTED_TOPIC,
|
||||
expected_message,
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
def test_message_with_user_mentions(self) -> None:
|
||||
# TODO
|
||||
message_body = (
|
||||
"<@U074VRHQ11T> <@U074G5E1ANR> <@U06NU4E26M9> hello, this is a message with mentions"
|
||||
)
|
||||
expected_message = EXPECTED_MESSAGE.format(user=USER, message=message_body)
|
||||
self.check_webhook(
|
||||
"message_with_user_mentions",
|
||||
EXPECTED_TOPIC,
|
||||
expected_message,
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
def test_message_with_variety_files(self) -> None:
|
||||
message_body = """Message with an assortment of file types
|
||||
*[postman-agent-0.4.25-linux-x64.tar.gz](https://ds-py62195.slack.com/files/U06NU4E26M9/F079E4CMY5Q/postman-agent-0.4.25-linux-x64.tar.gz)*
|
||||
*[discord-0.0.55.deb](https://ds-py62195.slack.com/files/U06NU4E26M9/F079SQ33CBT/discord-0.0.55.deb)*
|
||||
*[Slack-bot-scopes-List.xlsx](https://ds-py62195.slack.com/files/U06NU4E26M9/F079SQ721A5/slack-bot-scopes-list.xlsx)*
|
||||
*[wallpaper.jpg](https://ds-py62195.slack.com/files/U06NU4E26M9/F079B7G7NUD/wallpaper.jpg)*
|
||||
*[TestPDFfile.pdf](https://ds-py62195.slack.com/files/U06NU4E26M9/F07A2TVKNQ0/testpdffile.pdf)*
|
||||
*[channels.json](https://ds-py62195.slack.com/files/U06NU4E26M9/F07A2TVQ7C0/channels.json)*"""
|
||||
expected_message = EXPECTED_MESSAGE.format(user=USER, message=message_body)
|
||||
self.check_webhook(
|
||||
"message_with_variety_files",
|
||||
EXPECTED_TOPIC,
|
||||
expected_message,
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
def test_message_with_workspace_mentions(self) -> None:
|
||||
message_body = "@**all** @**all** Sorry for mentioning. This is for the test fixtures for the Slack integration update PR I'm working on and can't be done in a private channel. :bow:"
|
||||
expected_message = EXPECTED_MESSAGE.format(user=USER, message=message_body)
|
||||
self.check_webhook(
|
||||
"message_with_workspace_mentions",
|
||||
EXPECTED_TOPIC,
|
||||
expected_message,
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
def test_message_from_slack_integration_bot(self) -> None:
|
||||
self.check_webhook(
|
||||
"message_from_slack_integration_bot",
|
||||
"",
|
||||
"",
|
||||
content_type="application/json",
|
||||
expect_noop=True,
|
||||
)
|
||||
|
||||
|
||||
class SlackLegacyWebhookTests(WebhookTestCase):
|
||||
CHANNEL_NAME = "slack"
|
||||
URL_TEMPLATE = "/api/v1/external/slack?stream={stream}&api_key={api_key}"
|
||||
WEBHOOK_DIR_NAME = "slack"
|
||||
|
||||
def test_slack_only_stream_parameter(self) -> None:
|
||||
expected_topic_name = "Message from Slack"
|
||||
expected_message = EXPECTED_MESSAGE.format(user=LEGACY_USER, message="test")
|
||||
self.check_webhook(
|
||||
"message_info",
|
||||
EXPECTED_TOPIC,
|
||||
EXPECTED_MESSAGE,
|
||||
expected_topic_name,
|
||||
expected_message,
|
||||
content_type="application/x-www-form-urlencoded",
|
||||
)
|
||||
|
||||
def test_slack_with_user_specified_topic(self) -> None:
|
||||
self.url = self.build_webhook_url(topic="test")
|
||||
expected_topic_name = "test"
|
||||
expected_message = EXPECTED_MESSAGE.format(user=LEGACY_USER, message="test")
|
||||
self.check_webhook(
|
||||
"message_info",
|
||||
expected_topic_name,
|
||||
EXPECTED_MESSAGE,
|
||||
expected_message,
|
||||
content_type="application/x-www-form-urlencoded",
|
||||
)
|
||||
|
||||
def test_slack_channels_map_to_topics_true(self) -> None:
|
||||
self.url = self.build_webhook_url(channels_map_to_topics="1")
|
||||
expected_topic_name = "channel: general"
|
||||
expected_message = EXPECTED_MESSAGE.format(user=LEGACY_USER, message="test")
|
||||
self.check_webhook(
|
||||
"message_info",
|
||||
expected_topic_name,
|
||||
EXPECTED_MESSAGE,
|
||||
expected_message,
|
||||
content_type="application/x-www-form-urlencoded",
|
||||
)
|
||||
|
||||
def test_slack_channels_map_to_topics_true_and_user_specified_topic(self) -> None:
|
||||
self.url = self.build_webhook_url(topic="test", channels_map_to_topics="1")
|
||||
expected_topic_name = "test"
|
||||
expected_message = EXPECTED_MESSAGE.format(user=LEGACY_USER, message="test")
|
||||
self.check_webhook(
|
||||
"message_info",
|
||||
expected_topic_name,
|
||||
EXPECTED_MESSAGE,
|
||||
expected_message,
|
||||
content_type="application/x-www-form-urlencoded",
|
||||
)
|
||||
|
||||
def test_slack_channels_map_to_topics_false(self) -> None:
|
||||
self.CHANNEL_NAME = "general"
|
||||
self.url = self.build_webhook_url(channels_map_to_topics="0")
|
||||
expected_topic_name = "Message from Slack"
|
||||
expected_message = EXPECTED_MESSAGE.format(user=LEGACY_USER, message="test")
|
||||
self.check_webhook(
|
||||
"message_info",
|
||||
EXPECTED_TOPIC,
|
||||
EXPECTED_MESSAGE,
|
||||
expected_topic_name,
|
||||
expected_message,
|
||||
content_type="application/x-www-form-urlencoded",
|
||||
)
|
||||
|
||||
@@ -63,10 +290,11 @@ class SlackWebhookTests(WebhookTestCase):
|
||||
self.CHANNEL_NAME = "general"
|
||||
self.url = self.build_webhook_url(topic="test", channels_map_to_topics="0")
|
||||
expected_topic_name = "test"
|
||||
expected_message = EXPECTED_MESSAGE.format(user=LEGACY_USER, message="test")
|
||||
self.check_webhook(
|
||||
"message_info",
|
||||
expected_topic_name,
|
||||
EXPECTED_MESSAGE,
|
||||
expected_message,
|
||||
content_type="application/x-www-form-urlencoded",
|
||||
)
|
||||
|
||||
@@ -88,12 +316,6 @@ class SlackWebhookTests(WebhookTestCase):
|
||||
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) -> None:
|
||||
payload = self.get_body("message_info")
|
||||
url = self.build_webhook_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")
|
||||
|
||||
@override
|
||||
def get_body(self, fixture_name: str) -> str:
|
||||
return self.webhook_fixture_data("slack", fixture_name, file_type="txt")
|
||||
|
||||
@@ -1,17 +1,148 @@
|
||||
import re
|
||||
from typing import TypeAlias
|
||||
|
||||
from django.http import HttpRequest
|
||||
from django.http.response import HttpResponse
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from zerver.data_import.slack_message_conversion import (
|
||||
SLACK_BOLD_REGEX,
|
||||
SLACK_ITALIC_REGEX,
|
||||
SLACK_STRIKETHROUGH_REGEX,
|
||||
SLACK_USERMENTION_REGEX,
|
||||
convert_link_format,
|
||||
convert_mailto_format,
|
||||
convert_markdown_syntax,
|
||||
)
|
||||
from zerver.decorator import webhook_view
|
||||
from zerver.lib.exceptions import JsonableError
|
||||
from zerver.lib.exceptions import JsonableError, UnsupportedWebhookEventTypeError
|
||||
from zerver.lib.request import RequestVariableMissingError
|
||||
from zerver.lib.response import json_success
|
||||
from zerver.lib.typed_endpoint import typed_endpoint
|
||||
from zerver.lib.validator import WildValue, check_none_or, check_string, to_wild_value
|
||||
from zerver.lib.webhooks.common import check_send_webhook_message
|
||||
from zerver.models import UserProfile
|
||||
|
||||
ZULIP_MESSAGE_TEMPLATE = "**{message_sender}**: {text}"
|
||||
FILE_LINK_TEMPLATE = "\n*[{file_name}]({file_link})*"
|
||||
ZULIP_MESSAGE_TEMPLATE = "**{sender}**: {text}"
|
||||
VALID_OPTIONS = {"SHOULD_NOT_BE_MAPPED": "0", "SHOULD_BE_MAPPED": "1"}
|
||||
|
||||
SlackFileListT: TypeAlias = list[dict[str, str]]
|
||||
|
||||
SLACK_CHANNELMENTION_REGEX = r"(?<=<#)(.*)(?=>)"
|
||||
|
||||
|
||||
def is_zulip_slack_bridge_bot_message(payload: WildValue) -> bool:
|
||||
app_api_id = payload.get("api_app_id").tame(check_none_or(check_string))
|
||||
bot_app_id = (
|
||||
payload.get("event", {})
|
||||
.get("bot_profile", {})
|
||||
.get("app_id")
|
||||
.tame(check_none_or(check_string))
|
||||
)
|
||||
return bot_app_id is not None and app_api_id == bot_app_id
|
||||
|
||||
|
||||
def convert_slack_user_and_channel_mentions(text: str) -> str:
|
||||
tokens = text.split(" ")
|
||||
for iterator in range(len(tokens)):
|
||||
slack_usermention_match = re.search(SLACK_USERMENTION_REGEX, tokens[iterator], re.VERBOSE)
|
||||
slack_channelmention_match = re.search(
|
||||
SLACK_CHANNELMENTION_REGEX, tokens[iterator], re.MULTILINE
|
||||
)
|
||||
if slack_usermention_match:
|
||||
# TODO: Callback to Slack API
|
||||
pass
|
||||
elif slack_channelmention_match:
|
||||
# Convert Slack channel mentions to a mention-like syntax so that
|
||||
# a mention isn't triggered for a Zulip channel with the same name.
|
||||
channel_info: list[str] = slack_channelmention_match.group(0).split("|")
|
||||
channel_name = channel_info[1]
|
||||
tokens[iterator] = f"**#{channel_name}**" if channel_name else "**#[private channel]**"
|
||||
text = " ".join(tokens)
|
||||
return text
|
||||
|
||||
|
||||
# This is a modified version of `convert_to_zulip_markdown` in
|
||||
# `slack_message_conversion.py`, which cannot be used directly
|
||||
# due to differences in the Slack import data and Slack webhook
|
||||
# payloads.
|
||||
def convert_to_zulip_markdown(text: str) -> str:
|
||||
text = convert_slack_formatting(text)
|
||||
text = convert_slack_workspace_mentions(text)
|
||||
text = convert_slack_user_and_channel_mentions(text)
|
||||
return text
|
||||
|
||||
|
||||
def convert_slack_formatting(text: str) -> str:
|
||||
text = convert_markdown_syntax(text, SLACK_BOLD_REGEX, "**")
|
||||
text = convert_markdown_syntax(text, SLACK_STRIKETHROUGH_REGEX, "~~")
|
||||
text = convert_markdown_syntax(text, SLACK_ITALIC_REGEX, "*")
|
||||
return text
|
||||
|
||||
|
||||
def convert_slack_workspace_mentions(text: str) -> str:
|
||||
# Map Slack's <!everyone>, <!channel> and <!here> mentions to @**all**.
|
||||
# No regex for this as it can be present anywhere in the sentence.
|
||||
text = text.replace("<!everyone>", "@**all**")
|
||||
text = text.replace("<!channel>", "@**all**")
|
||||
text = text.replace("<!here>", "@**all**")
|
||||
return text
|
||||
|
||||
|
||||
def replace_links(text: str) -> str:
|
||||
text, _ = convert_link_format(text)
|
||||
text, _ = convert_mailto_format(text)
|
||||
return text
|
||||
|
||||
|
||||
def convert_raw_file_data(file_dict: WildValue) -> SlackFileListT:
|
||||
files = [
|
||||
{
|
||||
"file_link": file.get("permalink").tame(check_string),
|
||||
"file_name": file.get("title").tame(check_string),
|
||||
}
|
||||
for file in file_dict
|
||||
]
|
||||
return files
|
||||
|
||||
|
||||
def get_message_body(text: str, sender: str, files: SlackFileListT) -> str:
|
||||
# TODO: Reformat mentions in messages, currently its <@USER_ID>.
|
||||
body = ZULIP_MESSAGE_TEMPLATE.format(sender=sender, text=text)
|
||||
for file in files:
|
||||
body += FILE_LINK_TEMPLATE.format(**file)
|
||||
return body
|
||||
|
||||
|
||||
def is_challenge_handshake(payload: WildValue) -> bool:
|
||||
return payload.get("type").tame(check_string) == "url_verification"
|
||||
|
||||
|
||||
def handle_slack_webhook_message(
|
||||
request: HttpRequest,
|
||||
user_profile: UserProfile,
|
||||
content: str,
|
||||
channel: str | None,
|
||||
channels_map_to_topics: str | None,
|
||||
) -> None:
|
||||
topic_name = "Message from Slack"
|
||||
if channels_map_to_topics is None:
|
||||
check_send_webhook_message(request, user_profile, topic_name, content)
|
||||
elif channels_map_to_topics == VALID_OPTIONS["SHOULD_BE_MAPPED"]:
|
||||
topic_name = f"channel: {channel}"
|
||||
check_send_webhook_message(request, user_profile, topic_name, content)
|
||||
elif channels_map_to_topics == VALID_OPTIONS["SHOULD_NOT_BE_MAPPED"]:
|
||||
check_send_webhook_message(
|
||||
request,
|
||||
user_profile,
|
||||
topic_name,
|
||||
content,
|
||||
stream=channel,
|
||||
)
|
||||
else:
|
||||
raise JsonableError(_("Error: channels_map_to_topics parameter other than 0 or 1"))
|
||||
|
||||
|
||||
@webhook_view("Slack", notify_bot_owner_on_invalid_json=False)
|
||||
@typed_endpoint
|
||||
@@ -19,42 +150,77 @@ def api_slack_webhook(
|
||||
request: HttpRequest,
|
||||
user_profile: UserProfile,
|
||||
*,
|
||||
user_name: str,
|
||||
text: str,
|
||||
channel_name: str,
|
||||
channels_map_to_topics: str | None = None,
|
||||
) -> HttpResponse:
|
||||
content = ZULIP_MESSAGE_TEMPLATE.format(message_sender=user_name, text=text)
|
||||
topic_name = "Message from Slack"
|
||||
|
||||
if channels_map_to_topics is None:
|
||||
check_send_webhook_message(
|
||||
request,
|
||||
user_profile,
|
||||
topic_name,
|
||||
content,
|
||||
)
|
||||
elif channels_map_to_topics == VALID_OPTIONS["SHOULD_BE_MAPPED"]:
|
||||
# If the webhook URL has a user_specified_topic,
|
||||
# then this topic-channel mapping will not be used.
|
||||
topic_name = f"channel: {channel_name}"
|
||||
check_send_webhook_message(
|
||||
request,
|
||||
user_profile,
|
||||
topic_name,
|
||||
content,
|
||||
)
|
||||
elif channels_map_to_topics == VALID_OPTIONS["SHOULD_NOT_BE_MAPPED"]:
|
||||
# This channel-channel mapping will be used even if
|
||||
# there is a channel specified in the webhook URL.
|
||||
check_send_webhook_message(
|
||||
request,
|
||||
user_profile,
|
||||
topic_name,
|
||||
content,
|
||||
stream=channel_name,
|
||||
)
|
||||
if request.content_type != "application/json":
|
||||
# Handle Slack's legacy Outgoing Webhook Service payload.
|
||||
expected_legacy_variable = ["user_name", "text", "channel_name"]
|
||||
legacy_payload = {}
|
||||
for variable in expected_legacy_variable:
|
||||
if variable in request.POST:
|
||||
legacy_payload[variable] = request.POST[variable]
|
||||
elif variable in request.GET: # nocoverage
|
||||
legacy_payload[variable] = request.GET[variable]
|
||||
else:
|
||||
raise JsonableError(_("Error: channels_map_to_topics parameter other than 0 or 1"))
|
||||
raise RequestVariableMissingError(variable)
|
||||
|
||||
text = convert_slack_formatting(legacy_payload["text"])
|
||||
text = replace_links(text)
|
||||
text = get_message_body(text, legacy_payload["user_name"], [])
|
||||
handle_slack_webhook_message(
|
||||
request,
|
||||
user_profile,
|
||||
text,
|
||||
legacy_payload["channel_name"],
|
||||
channels_map_to_topics,
|
||||
)
|
||||
return json_success(request)
|
||||
|
||||
try:
|
||||
val = request.body.decode(request.encoding or "utf-8")
|
||||
except UnicodeDecodeError: # nocoverage
|
||||
raise JsonableError(_("Malformed payload"))
|
||||
payload = to_wild_value("payload", val)
|
||||
|
||||
# Handle initial URL verification handshake for Slack Events API.
|
||||
if is_challenge_handshake(payload):
|
||||
challenge = payload.get("challenge").tame(check_string)
|
||||
check_send_webhook_message(
|
||||
request,
|
||||
user_profile,
|
||||
"Integration events",
|
||||
"Successfully verified webhook URL with Slack!",
|
||||
)
|
||||
return json_success(request=request, data={"challenge": challenge})
|
||||
|
||||
# Prevent any Zulip messages sent through the Slack Bridge from looping
|
||||
# back here.
|
||||
if is_zulip_slack_bridge_bot_message(payload):
|
||||
return json_success(request)
|
||||
|
||||
event_dict = payload.get("event", {})
|
||||
event_type = event_dict.get("type").tame(check_string)
|
||||
|
||||
if event_type != "message":
|
||||
raise UnsupportedWebhookEventTypeError(event_type)
|
||||
|
||||
raw_files = event_dict.get("files")
|
||||
files = convert_raw_file_data(raw_files) if raw_files else []
|
||||
raw_text = event_dict.get("text", "").tame(check_string)
|
||||
text = convert_to_zulip_markdown(raw_text)
|
||||
sender = event_dict.get("user").tame(check_none_or(check_string))
|
||||
if sender is None:
|
||||
# This is likely a Slack integration bot message. The sender of these
|
||||
# messages doesn't have a user profile and would require additional
|
||||
# formatting to handle. Refer to the Slack Incoming Webhook integration
|
||||
# for how to add support for this type of payload.
|
||||
raise UnsupportedWebhookEventTypeError(
|
||||
"integration bot message"
|
||||
if event_dict["subtype"] == "bot_message"
|
||||
else "unknown Slack event"
|
||||
)
|
||||
content = get_message_body(text, sender, files)
|
||||
channel = event_dict.get("channel").tame(check_string) if channels_map_to_topics else None
|
||||
|
||||
handle_slack_webhook_message(request, user_profile, content, channel, channels_map_to_topics)
|
||||
return json_success(request)
|
||||
|
||||
Reference in New Issue
Block a user