From 792b7b1b0553bea7f4b56df3673b26bf53a5c26a Mon Sep 17 00:00:00 2001 From: jorrick Date: Thu, 25 Sep 2025 15:27:09 +0200 Subject: [PATCH 1/2] =?UTF-8?q?=E2=9C=A8=20Add=20function=20to=20show=20me?= =?UTF-8?q?ssage=20in=20slacks=20Block=20kit=20builder?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- blockkit/core.py | 8 ++++++++ docs/quickstart.md | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/blockkit/core.py b/blockkit/core.py index 99b2dda..75ab7e3 100644 --- a/blockkit/core.py +++ b/blockkit/core.py @@ -4,6 +4,7 @@ from abc import ABC, abstractmethod from datetime import date, datetime, time from typing import Any, Final, Literal, Self, TypeAlias, get_args +from urllib import parse from zoneinfo import ZoneInfo from blockkit.utils import is_md @@ -3135,6 +3136,13 @@ def thread_ts(self, thread_ts: str | int | float | None) -> Self: def mrkdwn(self, mrkdwn: bool | None = True) -> Self: return self._add_field("mrkdwn", mrkdwn, validators=[Typed(bool)]) + def get_block_kit_explorer_url(self) -> str: + """This function returns a link visualising what the message would look like in Slack.""" + compact_json = json.dumps(self.build(), separators=(",", ":")) + encoded_json = parse.quote(compact_json) + base_url = "https://app.slack.com/block-kit-builder#" + return base_url + encoded_json + ModalBlock: TypeAlias = ( Actions | Context | Divider | Header | Image | Input | RichText | Section | Video diff --git a/docs/quickstart.md b/docs/quickstart.md index 90db5d1..0f9e95e 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -28,6 +28,11 @@ message = ( That's it. Real Slack BlockKit JSON. Ready to send. +Wondering what it looks like now in Slack? You can check your exact message in the Slack block kit builder before sending it; +```python +message.get_block_kit_explorer_url() +``` + ## Send it ```python From b5b4348afc1fcec5236d69d754ddb2b4c66202c0 Mon Sep 17 00:00:00 2001 From: Dima Date: Sat, 27 Sep 2025 18:59:43 +0200 Subject: [PATCH 2/2] let's have `builder_url()` for all surfaces --- blockkit/core.py | 45 +++++++++++++++++++++++++++++++++++---------- tests/test_core.py | 1 + 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/blockkit/core.py b/blockkit/core.py index 75ab7e3..09d98f5 100644 --- a/blockkit/core.py +++ b/blockkit/core.py @@ -732,6 +732,28 @@ def external_id(self, external_id: str | None) -> Self: ) +class BuilderUrlMixin: + def builder_url(self) -> str: + """ + Returns a URL to preview this message in Slack's Block Kit Builder. + """ + + surface = self.build() + filter_fields = { + Message: ("blocks",), + Modal: ("type", "title", "submit", "close", "blocks"), + Home: ("type", "blocks"), + }.get(type(self), None) + + if filter_fields: + surface = {k: v for k, v in surface.items() if k in filter_fields} + + encoded_json = parse.quote( + json.dumps(surface, ensure_ascii=False, separators=(",", ":")), safe="" + ) + return f"https://app.slack.com/block-kit-builder#{encoded_json}" + + """ Composition objects """ @@ -3093,7 +3115,7 @@ def author_name(self, author_name: str | None) -> Self: ) -class Message(Component): +class Message(Component, BuilderUrlMixin): """ Message surface @@ -3136,13 +3158,6 @@ def thread_ts(self, thread_ts: str | int | float | None) -> Self: def mrkdwn(self, mrkdwn: bool | None = True) -> Self: return self._add_field("mrkdwn", mrkdwn, validators=[Typed(bool)]) - def get_block_kit_explorer_url(self) -> str: - """This function returns a link visualising what the message would look like in Slack.""" - compact_json = json.dumps(self.build(), separators=(",", ":")) - encoded_json = parse.quote(compact_json) - base_url = "https://app.slack.com/block-kit-builder#" - return base_url + encoded_json - ModalBlock: TypeAlias = ( Actions | Context | Divider | Header | Image | Input | RichText | Section | Video @@ -3150,7 +3165,12 @@ def get_block_kit_explorer_url(self) -> str: class Modal( - Component, BlocksMixin, PrivateMetadataMixin, CallbackIdMixin, ExternalIdMixin + Component, + BlocksMixin, + PrivateMetadataMixin, + CallbackIdMixin, + ExternalIdMixin, + BuilderUrlMixin, ): """ Modal surface @@ -3228,7 +3248,12 @@ def submit_disabled(self, submit_disabled: bool | None = True) -> Self: class Home( - Component, BlocksMixin, PrivateMetadataMixin, CallbackIdMixin, ExternalIdMixin + Component, + BlocksMixin, + PrivateMetadataMixin, + CallbackIdMixin, + ExternalIdMixin, + BuilderUrlMixin, ): """ App Home surface diff --git a/tests/test_core.py b/tests/test_core.py index 6277bb1..00bbe95 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -3544,3 +3544,4 @@ def test_builds(self): .external_id("alice_intro") .build() ) + assert got == want