diff --git a/integration_tests/webhook/test_async_webhook.py b/integration_tests/webhook/test_async_webhook.py index e46092bb2..09aa2e882 100644 --- a/integration_tests/webhook/test_async_webhook.py +++ b/integration_tests/webhook/test_async_webhook.py @@ -1,12 +1,13 @@ import os +from tests.helpers import async_test import unittest +import time from integration_tests.env_variable_names import ( SLACK_SDK_TEST_INCOMING_WEBHOOK_URL, SLACK_SDK_TEST_INCOMING_WEBHOOK_CHANNEL_NAME, SLACK_SDK_TEST_BOT_TOKEN, ) -from integration_tests.helpers import async_test from slack_sdk.web.async_client import AsyncWebClient from slack_sdk.webhook.async_client import AsyncWebhookClient from slack_sdk.models.attachments import Attachment, AttachmentField @@ -16,8 +17,22 @@ class TestAsyncWebhook(unittest.TestCase): - def setUp(self): - pass + @async_test + async def setUp(self): + if not hasattr(self, "channel_id"): + token = os.environ[SLACK_SDK_TEST_BOT_TOKEN] + channel_name = os.environ[ + SLACK_SDK_TEST_INCOMING_WEBHOOK_CHANNEL_NAME + ].replace("#", "") + client = AsyncWebClient(token=token) + self.channel_id = None + async for resp in await client.conversations_list(limit=10): + for c in resp["channels"]: + if c["name"] == channel_name: + self.channel_id = c["id"] + break + if self.channel_id is not None: + break def tearDown(self): pass @@ -31,24 +46,55 @@ async def test_webhook(self): self.assertEqual("ok", response.body) token = os.environ[SLACK_SDK_TEST_BOT_TOKEN] - channel_name = os.environ[SLACK_SDK_TEST_INCOMING_WEBHOOK_CHANNEL_NAME].replace( - "#", "" - ) client = AsyncWebClient(token=token) - channel_id = None - async for resp in await client.conversations_list(limit=10): - for c in resp["channels"]: - if c["name"] == channel_name: - channel_id = c["id"] - break - if channel_id is not None: - break - - history = await client.conversations_history(channel=channel_id, limit=1) + history = await client.conversations_history(channel=self.channel_id, limit=1) self.assertIsNotNone(history) actual_text = history["messages"][0]["text"] self.assertEqual("Hello!", actual_text) + @async_test + async def test_with_unfurls_off(self): + url = os.environ[SLACK_SDK_TEST_INCOMING_WEBHOOK_URL] + token = os.environ[SLACK_SDK_TEST_BOT_TOKEN] + webhook = AsyncWebhookClient(url) + client = AsyncWebClient(token=token) + # send message that does not unfurl + response = await webhook.send( + text="", + unfurl_links=False, + unfurl_media=False, + ) + self.assertEqual(200, response.status_code) + self.assertEqual("ok", response.body) + # wait to allow Slack API to edit message with attachments + time.sleep(2) + history = await client.conversations_history(channel=self.channel_id, limit=1) + self.assertIsNotNone(history) + self.assertTrue("attachments" not in history["messages"][0]) + + @async_test + async def test_with_unfurls_on(self): + # Slack API rate limits unfurls of unique links so test will + # fail when repeated. For testing, either use a different URL + # for text option or delete existing attachments in webhook channel. + url = os.environ[SLACK_SDK_TEST_INCOMING_WEBHOOK_URL] + token = os.environ[SLACK_SDK_TEST_BOT_TOKEN] + webhook = AsyncWebhookClient(url) + client = AsyncWebClient(token=token) + # send message that does unfurl + response = await webhook.send( + text="", + unfurl_links=True, + unfurl_media=True, + ) + self.assertEqual(200, response.status_code) + self.assertEqual("ok", response.body) + # wait to allow Slack API to edit message with attachments + time.sleep(2) + history = await client.conversations_history(channel=self.channel_id, limit=1) + self.assertIsNotNone(history) + self.assertTrue("attachments" in history["messages"][0]) + @async_test async def test_with_blocks(self): url = os.environ[SLACK_SDK_TEST_INCOMING_WEBHOOK_URL] diff --git a/integration_tests/webhook/test_webhook.py b/integration_tests/webhook/test_webhook.py index 12f60d022..2b4d0cbd4 100644 --- a/integration_tests/webhook/test_webhook.py +++ b/integration_tests/webhook/test_webhook.py @@ -1,5 +1,6 @@ import os import unittest +import time from integration_tests.env_variable_names import ( SLACK_SDK_TEST_INCOMING_WEBHOOK_URL, @@ -16,7 +17,20 @@ class TestWebhook(unittest.TestCase): def setUp(self): - pass + if not hasattr(self, "channel_id"): + token = os.environ[SLACK_SDK_TEST_BOT_TOKEN] + channel_name = os.environ[ + SLACK_SDK_TEST_INCOMING_WEBHOOK_CHANNEL_NAME + ].replace("#", "") + client = WebClient(token=token) + self.channel_id = None + for resp in client.conversations_list(limit=10): + for c in resp["channels"]: + if c["name"] == channel_name: + self.channel_id = c["id"] + break + if self.channel_id is not None: + break def tearDown(self): pass @@ -29,24 +43,53 @@ def test_webhook(self): self.assertEqual("ok", response.body) token = os.environ[SLACK_SDK_TEST_BOT_TOKEN] - channel_name = os.environ[SLACK_SDK_TEST_INCOMING_WEBHOOK_CHANNEL_NAME].replace( - "#", "" - ) client = WebClient(token=token) - channel_id = None - for resp in client.conversations_list(limit=10): - for c in resp["channels"]: - if c["name"] == channel_name: - channel_id = c["id"] - break - if channel_id is not None: - break - - history = client.conversations_history(channel=channel_id, limit=1) + history = client.conversations_history(channel=self.channel_id, limit=1) self.assertIsNotNone(history) actual_text = history["messages"][0]["text"] self.assertEqual("Hello!", actual_text) + def test_with_unfurls_off(self): + url = os.environ[SLACK_SDK_TEST_INCOMING_WEBHOOK_URL] + token = os.environ[SLACK_SDK_TEST_BOT_TOKEN] + webhook = WebhookClient(url) + client = WebClient(token=token) + # send message that does not unfurl + response = webhook.send( + text="", + unfurl_links=False, + unfurl_media=False, + ) + self.assertEqual(200, response.status_code) + self.assertEqual("ok", response.body) + # wait to allow Slack API to edit message with attachments + time.sleep(2) + history = client.conversations_history(channel=self.channel_id, limit=1) + self.assertIsNotNone(history) + self.assertTrue("attachments" not in history["messages"][0]) + + def test_with_unfurls_on(self): + # Slack API rate limits unfurls of unique links so test will + # fail when repeated. For testing, either use a different URL + # for text option or delete existing attachments in webhook channel. + url = os.environ[SLACK_SDK_TEST_INCOMING_WEBHOOK_URL] + token = os.environ[SLACK_SDK_TEST_BOT_TOKEN] + webhook = WebhookClient(url) + client = WebClient(token=token) + # send message that does unfurl + response = webhook.send( + text="", + unfurl_links=True, + unfurl_media=True, + ) + self.assertEqual(200, response.status_code) + self.assertEqual("ok", response.body) + # wait to allow Slack API to edit message with attachments + time.sleep(2) + history = client.conversations_history(channel=self.channel_id, limit=1) + self.assertIsNotNone(history) + self.assertTrue("attachments" in history["messages"][0]) + def test_with_blocks(self): url = os.environ[SLACK_SDK_TEST_INCOMING_WEBHOOK_URL] webhook = WebhookClient(url) diff --git a/slack_sdk/webhook/async_client.py b/slack_sdk/webhook/async_client.py index 38a6e3530..23edd728a 100644 --- a/slack_sdk/webhook/async_client.py +++ b/slack_sdk/webhook/async_client.py @@ -87,6 +87,8 @@ async def send( response_type: Optional[str] = None, replace_original: Optional[bool] = None, delete_original: Optional[bool] = None, + unfurl_links: Optional[bool] = None, + unfurl_media: Optional[bool] = None, headers: Optional[Dict[str, str]] = None, ) -> WebhookResponse: """Performs a Slack API request and returns the result. @@ -98,6 +100,8 @@ async def send( response_type: The type of message (either 'in_channel' or 'ephemeral') replace_original: True if you use this option for response_url requests delete_original: True if you use this option for response_url requests + unfurl_links: Option to indicate whether text url should unfurl + unfurl_media: Option to indicate whether media url should unfurl headers: Request headers to append only for this request Returns: @@ -113,6 +117,8 @@ async def send( "response_type": response_type, "replace_original": replace_original, "delete_original": delete_original, + "unfurl_links": unfurl_links, + "unfurl_media": unfurl_media, }, headers=headers, ) diff --git a/slack_sdk/webhook/client.py b/slack_sdk/webhook/client.py index d07aa6c53..1c0772be5 100644 --- a/slack_sdk/webhook/client.py +++ b/slack_sdk/webhook/client.py @@ -77,6 +77,8 @@ def send( response_type: Optional[str] = None, replace_original: Optional[bool] = None, delete_original: Optional[bool] = None, + unfurl_links: Optional[bool] = None, + unfurl_media: Optional[bool] = None, headers: Optional[Dict[str, str]] = None, ) -> WebhookResponse: """Performs a Slack API request and returns the result. @@ -89,6 +91,8 @@ def send( response_type: The type of message (either 'in_channel' or 'ephemeral') replace_original: True if you use this option for response_url requests delete_original: True if you use this option for response_url requests + unfurl_links: Option to indicate whether text url should unfurl + unfurl_media: Option to indicate whether media url should unfurl headers: Request headers to append only for this request Returns: @@ -104,6 +108,8 @@ def send( "response_type": response_type, "replace_original": replace_original, "delete_original": delete_original, + "unfurl_links": unfurl_links, + "unfurl_media": unfurl_media, }, headers=headers, ) diff --git a/slack_sdk/webhook/internal_utils.py b/slack_sdk/webhook/internal_utils.py index 37def28e7..1dd94deeb 100644 --- a/slack_sdk/webhook/internal_utils.py +++ b/slack_sdk/webhook/internal_utils.py @@ -4,7 +4,6 @@ from slack_sdk.web.internal_utils import ( _parse_web_class_objects, get_user_agent, - convert_bool_to_0_or_1, ) from .webhook_response import WebhookResponse @@ -12,7 +11,6 @@ def _build_body(original_body: Optional[Dict[str, Any]]) -> Optional[Dict[str, Any]]: if original_body: body = {k: v for k, v in original_body.items() if v is not None} - body = convert_bool_to_0_or_1(body) _parse_web_class_objects(body) return body return None diff --git a/tests/webhook/test_async_webhook.py b/tests/webhook/test_async_webhook.py index 4d1368d2b..9ec27f6f0 100644 --- a/tests/webhook/test_async_webhook.py +++ b/tests/webhook/test_async_webhook.py @@ -1,12 +1,9 @@ -import asyncio +from tests.helpers import async_test import unittest -import aiohttp - from slack.web.classes.attachments import Attachment, AttachmentField from slack.web.classes.blocks import SectionBlock, ImageBlock from slack.webhook import AsyncWebhookClient, WebhookResponse -from tests.helpers import async_test from tests.webhook.mock_web_api_server import ( cleanup_mock_web_api_server, setup_mock_web_api_server, @@ -31,6 +28,17 @@ async def test_send(self): resp = await client.send(text="hello!", response_type="in_channel") self.assertEqual("ok", resp.body) + @async_test + async def test_send_with_url_unfurl_opts_issue_1045(self): + client = AsyncWebhookClient("http://localhost:8888") + resp: WebhookResponse = await client.send( + text="", + unfurl_links=False, + unfurl_media=False, + ) + self.assertEqual(200, resp.status_code) + self.assertEqual("ok", resp.body) + @async_test async def test_send_blocks(self): client = AsyncWebhookClient("http://localhost:8888") diff --git a/tests/webhook/test_webhook.py b/tests/webhook/test_webhook.py index efcfa6fca..b54485904 100644 --- a/tests/webhook/test_webhook.py +++ b/tests/webhook/test_webhook.py @@ -28,6 +28,16 @@ def test_send(self): resp = client.send(text="hello!", response_type="in_channel") self.assertEqual("ok", resp.body) + def test_send_with_url_unfurl_opts_issue_1045(self): + client = WebhookClient("http://localhost:8888") + resp: WebhookResponse = client.send( + text="", + unfurl_links=False, + unfurl_media=False, + ) + self.assertEqual(200, resp.status_code) + self.assertEqual("ok", resp.body) + def test_send_blocks(self): client = WebhookClient("http://localhost:8888")