From 59436cb2549fb251c10804a6f0a45147f8164ab1 Mon Sep 17 00:00:00 2001 From: TiM Date: Wed, 1 Jul 2026 17:33:32 +1200 Subject: [PATCH] feat(slack): add unfurl_links/unfurl_media control to send_message - Callers can suppress Slack's link/media preview cards (e.g. a digest of many links that would otherwise unfurl into a wall of cards) while keeping the links themselves clickable. - SlackClient.send_message gains optional unfurl_links / unfurl_media params (default None). - Params are forwarded to chat_postMessage only when explicitly set, so every existing caller keeps Slack's default behavior. Backwards compatibility: additive optional params, default None. Existing callers (and their call assertions) are byte-for-byte unchanged; a second test locks in the omit-when-unset behavior. --- nui_shared_utils/slack_client.py | 19 ++++++++++--- tests/test_slack_client.py | 46 ++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/nui_shared_utils/slack_client.py b/nui_shared_utils/slack_client.py index cd265dc..f34c45e 100644 --- a/nui_shared_utils/slack_client.py +++ b/nui_shared_utils/slack_client.py @@ -388,7 +388,9 @@ def send_message( blocks: Optional[List[Dict]] = None, attachments: Optional[List[Dict]] = None, include_lambda_header: bool = True, - event_type: Optional[str] = None + event_type: Optional[str] = None, + unfurl_links: Optional[bool] = None, + unfurl_media: Optional[bool] = None, ) -> bool: """ Send message to Slack channel with standardized error handling. @@ -400,6 +402,10 @@ def send_message( attachments: Legacy attachment objects (supports color sidebars) include_lambda_header: Whether to include context header event_type: Optional event type label for header (e.g., "Scheduled", "API", "SQS") + unfurl_links: Override Slack link unfurling. None leaves Slack's default; + False suppresses link preview cards (links stay clickable) + unfurl_media: Override Slack media unfurling. None leaves Slack's default; + False suppresses media preview cards Returns: True if successful, False otherwise @@ -419,12 +425,19 @@ def _send_operation(): else: blocks_with_header = blocks - response = self._service_client.chat_postMessage( + kwargs = dict( channel=channel, text=text, blocks=blocks_with_header, - attachments=attachments + attachments=attachments, ) + # Forward unfurl controls only when explicitly set, so existing callers + # keep Slack's default behavior (and their call assertions stay unchanged). + if unfurl_links is not None: + kwargs["unfurl_links"] = unfurl_links + if unfurl_media is not None: + kwargs["unfurl_media"] = unfurl_media + response = self._service_client.chat_postMessage(**kwargs) if response["ok"]: log.info( diff --git a/tests/test_slack_client.py b/tests/test_slack_client.py index 4890662..35cfd09 100644 --- a/tests/test_slack_client.py +++ b/tests/test_slack_client.py @@ -111,6 +111,52 @@ def test_send_message_with_attachments(self, mock_webclient, mock_get_secret): channel="C123", text="Fallback", blocks=None, attachments=attachments ) + @patch("nui_shared_utils.base_client.get_secret") + @patch("nui_shared_utils.slack_client.WebClient") + def test_send_message_forwards_unfurl_flags(self, mock_webclient, mock_get_secret): + """unfurl_links / unfurl_media are forwarded to chat_postMessage when set.""" + mock_get_secret.return_value = {"bot_token": "xoxb-test-token"} + mock_client = Mock() + mock_webclient.return_value = mock_client + mock_client.chat_postMessage.return_value = {"ok": True, "ts": "1234567890.123456"} + + slack = SlackClient(secret_name="test-secret") + result = slack.send_message( + "C123", + "Test message", + include_lambda_header=False, + unfurl_links=False, + unfurl_media=False, + ) + + assert result is True + mock_client.chat_postMessage.assert_called_once_with( + channel="C123", + text="Test message", + blocks=None, + attachments=None, + unfurl_links=False, + unfurl_media=False, + ) + + @patch("nui_shared_utils.base_client.get_secret") + @patch("nui_shared_utils.slack_client.WebClient") + def test_send_message_omits_unfurl_when_unset(self, mock_webclient, mock_get_secret): + """When the unfurl flags are unset (default None), they are NOT forwarded, so + Slack's default behavior is preserved for every existing caller.""" + mock_get_secret.return_value = {"bot_token": "xoxb-test-token"} + mock_client = Mock() + mock_webclient.return_value = mock_client + mock_client.chat_postMessage.return_value = {"ok": True, "ts": "1234567890.123456"} + + slack = SlackClient(secret_name="test-secret") + result = slack.send_message("C123", "Test message", include_lambda_header=False) + + assert result is True + _, kwargs = mock_client.chat_postMessage.call_args + assert "unfurl_links" not in kwargs + assert "unfurl_media" not in kwargs + @patch("nui_shared_utils.base_client.get_secret") @patch("nui_shared_utils.slack_client.WebClient") def test_send_message_api_error(self, mock_webclient, mock_get_secret):