# Slack Platform Documentation - Python (Bolt for Python + Python Slack SDK) > Documentation for the Python (Bolt for Python + Python Slack SDK) stack only. Pair with llms-full-platform.txt for platform concepts, auth, and API reference. > Generated from the built site. Each page is also available individually at its URL + `.md`. > Page index: https://docs.slack.dev/llms-sitemap.md Source: https://docs.slack.dev/tools/bolt-python # Bolt for Python Bolt for Python is a Python framework to build Slack apps with the latest Slack platform features. Read the [Getting Started Guide](/tools/bolt-python/getting-started) to set up and run your first Bolt app. Then, explore the rest of the pages within the Guides section. The documentation there will help you build a Bolt app for whatever use case you may have. ## Getting help {#getting-help} These docs have lots of information on Bolt for Python. There's also an in-depth Reference section. Please explore! If you otherwise get stuck, we're here to help. The following are the best ways to get assistance working through your issue: * [Issue Tracker](http://github.com/slackapi/bolt-python/issues) for questions, bug reports, feature requests, and general discussion related to Bolt for Python. Try searching for an existing issue before creating a new one. * [Email](mailto:support@slack.com) our developer support team: `support@slack.com`. ## Release notes {#release-notes} Check out the [Bolt for Python release notes](https://github.com/slackapi/bolt-python/releases) for all the latest happenings. ## Contributing {#contributing} These docs live within the [Bolt-Python](https://github.com/slackapi/bolt-python/) repository and are open source. We welcome contributions from everyone! Please check out our [Contributor's Guide](https://github.com/slackapi/bolt-python/blob/main/.github/contributing.md) for how to contribute in a helpful and collaborative way. --- Source: https://docs.slack.dev/tools/bolt-python/concepts/acknowledge # Acknowledging requests Actions, commands, shortcuts, options requests, and view submissions must **always** be acknowledged using the `ack()` function. This lets Slack know that the request was received so that it may update the Slack user interface accordingly. Depending on the type of request, your acknowledgement may be different. For example, when acknowledging a menu selection associated with an external data source, you would call `ack()` with a list of relevant [options](/reference/block-kit/composition-objects/option-object/). When acknowledging a view submission, you may supply a `response_action` as part of your acknowledgement to [update the view](/tools/bolt-python/concepts/view-submissions). We recommend calling `ack()` right away before initiating any time-consuming processes such as fetching information from your database or sending a new message, since you only have 3 seconds to respond before Slack registers a timeout error. When working in a FaaS / serverless environment, our guidelines for when to `ack()` are different. See the section on [Lazy listeners (FaaS)](/tools/bolt-python/concepts/lazy-listeners) for more detail on this. Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. ## Example {#example} ``` # Example of responding to an external_select options request@app.options("menu_selection")def show_menu_options(ack): options = [ { "text": {"type": "plain_text", "text": "Option 1"}, "value": "1-1", }, { "text": {"type": "plain_text", "text": "Option 2"}, "value": "1-2", }, ] ack(options=options) ``` --- Source: https://docs.slack.dev/tools/bolt-python/concepts/actions # Listening & responding to actions Your app can listen and respond to user actions, like button clicks, and menu selects, using the `action` method. ## Listening to actions {#listening-to-actions} Actions can be filtered on an `action_id` parameter of type `str` or `re.Pattern`. The `action_id` parameter acts as a unique identifier for interactive components on the Slack platform. You'll notice in all `action()` examples, `ack()` is used. It is required to call the `ack()` function within an action listener to acknowledge that the request was received from Slack. This is discussed in the [acknowledging requests guide](/tools/bolt-python/concepts/acknowledge). Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. ``` # Your listener will be called every time a block element with the action_id "approve_button" is triggered@app.action("approve_button")def update_message(ack): ack() # Update the message to reflect the action ``` ### Listening to actions using a constraint object {#listening-to-actions-using-a-constraint-object} You can use a constraints object to listen to `block_id`s and `action_id`s (or any combination of them). Constraints in the object can be of type `str` or `re.Pattern`. ``` # Your function will only be called when the action_id matches 'select_user' AND the block_id matches 'assign_ticket'@app.action({ "block_id": "assign_ticket", "action_id": "select_user"})def update_message(ack, body, client): ack() if "container" in body and "message_ts" in body["container"]: client.reactions_add( name="white_check_mark", channel=body["channel"]["id"], timestamp=body["container"]["message_ts"], ) ``` ## Responding to actions {#responding-to-actions} There are two main ways to respond to actions. The first (and most common) way is to use `say()`, which sends a message back to the conversation where the incoming request took place. The second way to respond to actions is using `respond()`, which is a utility to use the `response_url` associated with the action. Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. ``` # Your listener will be called every time an interactive component with the action_id “approve_button” is triggered@app.action("approve_button")def approve_request(ack, say): # Acknowledge action request ack() say("Request approved 👍") ``` ### Using respond() method {#using-respond-method} Since `respond()` is a utility for calling the `response_url`, it behaves in the same way. You can pass [all the message payload properties](/messaging/#payloads) as keyword arguments along with optional properties like `response_type` (which has a value of `"in_channel"` or `"ephemeral"`), `replace_original`, `delete_original`, `unfurl_links`, and `unfurl_media`. With that, your app can send a new message payload that will be published back to the source of the original interaction. ``` # Listens to actions triggered with action_id of “user_select”@app.action("user_select")def select_user(ack, action, respond): ack() respond(f"You selected <@{action['selected_user']}>") ``` --- Source: https://docs.slack.dev/tools/bolt-python/concepts/adapters # Adapters Adapters are responsible for handling and parsing incoming requests from Slack to conform to [`BoltRequest`](https://github.com/slackapi/bolt-python/blob/main/slack_bolt/request/request.py), then dispatching those requests to your Bolt app. By default, Bolt will use the built-in [`HTTPServer`](https://docs.python.org/3/library/http.server.html) adapter. While this is okay for local development, **it is not recommended for production**. Bolt for Python includes a collection of built-in adapters that can be imported and used with your app. The built-in adapters support a variety of popular Python frameworks including Flask, Django, and Starlette among others. Adapters support the use of any production-ready web server of your choice. To use an adapter, you'll create an app with the framework of your choosing and import its corresponding adapter. Then you'll initialize the adapter instance and call its function that handles and parses incoming requests. The full list adapters, as well as configuration and sample usage, can be found within the repository's [`examples`](https://github.com/slackapi/bolt-python/tree/main/examples) ## Example {#example} ``` from slack_bolt import Appapp = App( signing_secret=os.environ.get("SLACK_SIGNING_SECRET"), token=os.environ.get("SLACK_BOT_TOKEN"))# There is nothing specific to Flask here!# App is completely framework/runtime agnostic@app.command("/hello-bolt")def hello(body, ack): ack(f"Hi <@{body['user_id']}>!")# Initialize Flask appfrom flask import Flask, requestflask_app = Flask(__name__)# SlackRequestHandler translates WSGI requests to Bolt's interface# and builds WSGI response from Bolt's response.from slack_bolt.adapter.flask import SlackRequestHandlerhandler = SlackRequestHandler(app)# Register routes to Flask app@flask_app.route("/slack/events", methods=["POST"])def slack_events(): # handler runs App's dispatch method return handler.handle(request) ``` --- Source: https://docs.slack.dev/tools/bolt-python/concepts/adding-agent-features # Adding agent features with Bolt for Python Check out the Support Agent sample app The code snippets throughout this guide are from our [Support Agent sample app](https://github.com/slack-samples/bolt-python-support-agent), Casey, which supports integration with Pydantic, Anthropic, and OpenAI. View our [agent quickstart](/ai/agent-quickstart) to get up and running with Casey. Otherwise, read on for exploration and explanation of agent-focused Bolt features found within Casey. Your agent can utilize features applicable to messages throughout Slack, like [chat streaming](#text-streaming) and [feedback buttons](#adding-and-handling-feedback). They can also [utilize the `Assistant` class](/tools/bolt-python/concepts/using-the-assistant-class) for a side-panel view designed with AI in mind. If you're unfamiliar with using these feature within Slack, you may want to read the [API docs on the subject](/ai/). Then come back here to implement them with Bolt! * * * ## Slack MCP Server {#slack-mcp-server} Casey can harness the [Slack MCP Server](https://docs.slack.dev/ai/slack-mcp-server/developing) when deployed via an HTTP Server with OAuth. To enable the Slack MCP Server: 1. Install [ngrok](https://ngrok.com/download) and start a tunnel: ``` ngrok http 3000 ``` 2. Copy the `https://*.ngrok-free.app` URL from the ngrok output. 3. Update `manifest.json` for HTTP mode: * Set `socket_mode_enabled` to `false` * Replace `ngrok-free.app` with your ngrok domain (e.g. `YOUR_NGROK_SUBDOMAIN.ngrok-free.app`) 4. Create a new local dev app: ``` slack install -E local ``` 5. Enable MCP for your app: * Run `slack app settings` to open your app's settings * Navigate to **Agents & AI Apps** in the left-side navigation * Toggle **Model Context Protocol** on 6. Update your `.env` OAuth environment variables: * Run `slack app settings` to open App Settings * Copy **Client ID**, **Client Secret**, and **Signing Secret** * Update `SLACK_REDIRECT_URI` in `.env` with your ngrok domain ``` SLACK_CLIENT_ID=YOUR_CLIENT_IDSLACK_CLIENT_SECRET=YOUR_CLIENT_SECRETSLACK_REDIRECT_URI=https://YOUR_NGROK_SUBDOMAIN.ngrok-free.app/slack/oauth_redirectSLACK_SIGNING_SECRET=YOUR_SIGNING_SECRET ``` 7. Start the app: ``` slack run app_oauth.py ``` 8. Click the install URL printed in the terminal to install the app to your workspace via OAuth. Your agent can now access the Slack MCP server! * * * ## Listening for user invocation {#listening-for-user-invocation} Agents can be invoked throughout Slack, such as via @mentions in channels, messaging the agent, and using the assistant side panel. * App mention * Message * Assistant thread ``` import refrom logging import Loggerfrom agents import Runnerfrom slack_bolt import BoltContext, Say, SayStream, SetStatusfrom slack_sdk import WebClientfrom agent import CaseyDeps, casey_agentfrom thread_context import conversation_storefrom listeners.views.feedback_builder import build_feedback_blocksdef handle_app_mentioned( client: WebClient, context: BoltContext, event: dict, logger: Logger, say: Say, say_stream: SayStream, set_status: SetStatus,): """Handle @Casey mentions in channels.""" try: channel_id = context.channel_id text = event.get("text", "") thread_ts = event.get("thread_ts") or event["ts"] user_id = context.user_id # Strip the bot mention from the text cleaned_text = re.sub(r"<@[A-Z0-9]+>", "", text).strip() if not cleaned_text: say( text="Hey there! How can I help you? Describe your IT issue and I'll do my best to assist.", thread_ts=thread_ts, ) return # Add eyes reaction only to the first message (not threaded replies) if not event.get("thread_ts"): client.reactions_add( channel=channel_id, timestamp=event["ts"], name="eyes", ) ... ``` ``` from logging import Loggerfrom slack_bolt.context import BoltContextfrom slack_bolt.context.say import Sayfrom slack_bolt.context.say_stream import SayStreamfrom slack_bolt.context.set_status import SetStatusfrom slack_sdk import WebClientfrom agent import CaseyDeps, run_casey_agentfrom thread_context import session_storefrom listeners.views.feedback_builder import build_feedback_blocksdef handle_message( client: WebClient, context: BoltContext, event: dict, logger: Logger, say: Say, say_stream: SayStream, set_status: SetStatus,): """Handle messages sent to Casey via DM or in threads the bot is part of.""" # Issue submissions are posted by the bot with metadata so the message # handler can run the agent on behalf of the original user. is_issue_submission = ( event.get("metadata", {}).get("event_type") == "issue_submission" ) # Skip message subtypes (edits, deletes, etc.) and bot messages that # are not issue submissions. if event.get("subtype"): return if event.get("bot_id") and not is_issue_submission: return is_dm = event.get("channel_type") == "im" is_thread_reply = event.get("thread_ts") is not None if is_dm: pass elif is_thread_reply: # Channel thread replies are handled only if the bot is already engaged session = session_store.get_session(context.channel_id, event["thread_ts"]) if session is None: return else: # Top-level channel messages are handled by app_mentioned return try: channel_id = context.channel_id text = event.get("text", "") thread_ts = event.get("thread_ts") or event["ts"] # Get session ID for conversation context existing_session_id = session_store.get_session(channel_id, thread_ts) # Add eyes reaction only to the first message (DMs only — channel # threads already have the reaction from the initial app_mention) if is_dm and not existing_session_id: client.reactions_add( channel=channel_id, timestamp=event["ts"], name="eyes", ) ... ``` Using the Assistant side panel The Assistant side panel requires additional setup. See the [Assistant class guide](/tools/bolt-python/concepts/using-the-assistant-class). ``` from logging import Loggerfrom slack_bolt.context.set_suggested_prompts import SetSuggestedPromptsSUGGESTED_PROMPTS = [ {"title": "Reset Password", "message": "I need to reset my password"}, {"title": "Request Access", "message": "I need access to a system or tool"}, {"title": "Network Issues", "message": "I'm having network connectivity issues"},]def handle_assistant_thread_started( set_suggested_prompts: SetSuggestedPrompts, logger: Logger): """Handle assistant thread started events by setting suggested prompts.""" try: set_suggested_prompts( prompts=SUGGESTED_PROMPTS, title="How can I help you today?", ) except Exception as e: logger.exception(f"Failed to handle assistant thread started: {e}") ``` * * * ## Setting status {#setting-assistant-status} Your app can show actions are happening behind the scenes by setting its thread status. ``` def handle_app_mentioned( set_status: SetStatus, ...): set_status( status="Thinking...", loading_messages=[ "Teaching the hamsters to type faster…", "Untangling the internet cables…", "Consulting the office goldfish…", "Polishing up the response just for you…", "Convincing the AI to stop overthinking…", ], ) ``` * * * ## Streaming messages {#text-streaming} You can have your app's messages stream in to replicate conventional agent behavior. Bolt for Python provides a `say_stream` utility as a listener argument available for `app.event` and `app.message` listeners. The `say_stream` utility streamlines calling the Python Slack SDK's [`WebClient.chat_stream`](https://docs.slack.dev/tools/python-slack-sdk/reference/web/client.html#slack_sdk.web.client.WebClient.chat_stream) helper utility by sourcing parameter values from the relevant event payload. Parameter Value `channel_id` Sourced from the event payload. `thread_ts` Sourced from the event payload. Falls back to the `ts` value if available. `recipient_team_id` Sourced from the event `team_id` (`enterprise_id` if the app is installed on an org). `recipient_user_id` Sourced from the `user_id` of the event. If either `channel_id` or `thread_ts` cannot be sourced, the utility will be `None`. ``` streamer = say_stream()streamer.append(markdown_text="Here's my response...")streamer.append(markdown_text="And here's more...")streamer.stop() ``` * * * ## Adding and handling feedback {#adding-and-handling-feedback} You can use the [feedback buttons block element](/reference/block-kit/block-elements/feedback-buttons-element/) to allow users to immediately provide feedback regarding the app's responses. Here's what the feedback buttons look like from the Support Agent sample app: .../listeners/views/feedback\_builder.py ``` from slack_sdk.models.blocks import ( Block, ContextActionsBlock, FeedbackButtonObject, FeedbackButtonsElement,)def build_feedback_blocks() -> list[Block]: """Build feedback blocks with thumbs up/down buttons.""" return [ ContextActionsBlock( elements=[ FeedbackButtonsElement( action_id="feedback", positive_button=FeedbackButtonObject( text="Good Response", accessibility_label="Submit positive feedback on this response", value="good-feedback", ), negative_button=FeedbackButtonObject( text="Bad Response", accessibility_label="Submit negative feedback on this response", value="bad-feedback", ), ) ] ) ] ``` That feedback block is then rendered at the bottom of your app's message via the `say_stream` utility. ``` ... # Stream response in thread with feedback buttons streamer = say_stream() streamer.append(markdown_text=result.output) feedback_blocks = build_feedback_blocks() streamer.stop(blocks=feedback_blocks)... ``` You can also add a response for when the user provides feedback. ...listeners/actions/feedback\_button.py ``` from logging import Loggerfrom slack_bolt import Ack, BoltContextfrom slack_sdk import WebClientdef handle_feedback_button( ack: Ack, body: dict, client: WebClient, context: BoltContext, logger: Logger): """Handle thumbs up/down feedback on Casey's responses.""" ack() try: channel_id = context.channel_id user_id = context.user_id message_ts = body["message"]["ts"] feedback_value = body["actions"][0]["value"] if feedback_value == "good-feedback": client.chat_postEphemeral( channel=channel_id, user=user_id, thread_ts=message_ts, text="Glad that was helpful! :tada:", ) else: client.chat_postEphemeral( channel=channel_id, user=user_id, thread_ts=message_ts, text="Sorry that wasn't helpful. :slightly_frowning_face: Try rephrasing your question or I can create a support ticket for you.", ) logger.debug( f"Feedback received: value={feedback_value}, message_ts={message_ts}" ) except Exception as e: logger.exception(f"Failed to handle feedback: {e}") ``` * * * ## Full example {#full-example} Putting all those concepts together results in a dynamic agent ready to helpfully respond. Full example * Pydantic * Anthropic * OpenAI app\_mentioned.py ``` import refrom logging import Loggerfrom slack_bolt import BoltContext, Say, SayStream, SetStatusfrom slack_sdk import WebClientfrom agent import CaseyDeps, casey_agent, get_modelfrom thread_context import conversation_storefrom listeners.views.feedback_builder import build_feedback_blocksdef handle_app_mentioned( client: WebClient, context: BoltContext, event: dict, logger: Logger, say: Say, say_stream: SayStream, set_status: SetStatus,): """Handle @Casey mentions in channels.""" try: channel_id = context.channel_id text = event.get("text", "") thread_ts = event.get("thread_ts") or event["ts"] user_id = context.user_id # Strip the bot mention from the text cleaned_text = re.sub(r"<@[A-Z0-9]+>", "", text).strip() if not cleaned_text: say( text="Hey there! How can I help you? Describe your IT issue and I'll do my best to assist.", thread_ts=thread_ts, ) return # Add eyes reaction only to the first message (not threaded replies) if not event.get("thread_ts"): client.reactions_add( channel=channel_id, timestamp=event["ts"], name="eyes", ) # Set assistant thread status with loading messages set_status( status="Thinking...", loading_messages=[ "Teaching the hamsters to type faster…", "Untangling the internet cables…", "Consulting the office goldfish…", "Polishing up the response just for you…", "Convincing the AI to stop overthinking…", ], ) # Get conversation history history = conversation_store.get_history(channel_id, thread_ts) # Run the agent deps = CaseyDeps( client=client, user_id=user_id, channel_id=channel_id, thread_ts=thread_ts, message_ts=event["ts"], ) result = casey_agent.run_sync( cleaned_text, model=get_model(), deps=deps, message_history=history, ) # Stream response in thread with feedback buttons streamer = say_stream() streamer.append(markdown_text=result.output) feedback_blocks = build_feedback_blocks() streamer.stop(blocks=feedback_blocks) # Store conversation history conversation_store.set_history(channel_id, thread_ts, result.all_messages()) except Exception as e: logger.exception(f"Failed to handle app mention: {e}") say( text=f":warning: Something went wrong! ({e})", thread_ts=event.get("thread_ts") or event["ts"], ) ``` app\_mentioned.py ``` import refrom logging import Loggerfrom slack_bolt.context import BoltContextfrom slack_bolt.context.say import Sayfrom slack_bolt.context.say_stream import SayStreamfrom slack_bolt.context.set_status import SetStatusfrom slack_sdk import WebClientfrom agent import CaseyDeps, run_casey_agentfrom thread_context import session_storefrom listeners.views.feedback_builder import build_feedback_blocksdef handle_app_mentioned( client: WebClient, context: BoltContext, event: dict, logger: Logger, say: Say, say_stream: SayStream, set_status: SetStatus,): """Handle @Casey mentions in channels.""" try: channel_id = context.channel_id text = event.get("text", "") thread_ts = event.get("thread_ts") or event["ts"] # Strip the bot mention from the text cleaned_text = re.sub(r"<@[A-Z0-9]+>", "", text).strip() if not cleaned_text: say( text="Hey there! How can I help you? Describe your IT issue and I'll do my best to assist.", thread_ts=thread_ts, ) return # Add eyes reaction only to the first message (not threaded replies) if not event.get("thread_ts"): client.reactions_add( channel=channel_id, timestamp=event["ts"], name="eyes", ) # Set assistant thread status with loading messages set_status( status="Thinking...", loading_messages=[ "Teaching the hamsters to type faster…", "Untangling the internet cables…", "Consulting the office goldfish…", "Polishing up the response just for you…", "Convincing the AI to stop overthinking…", ], ) # Get session ID for conversation context existing_session_id = session_store.get_session(channel_id, thread_ts) # Run the agent with deps for tool access deps = CaseyDeps( client=client, user_id=context.user_id, channel_id=channel_id, thread_ts=thread_ts, message_ts=event["ts"], ) response_text, new_session_id = run_casey_agent( cleaned_text, session_id=existing_session_id, deps=deps ) # Stream response in thread with feedback buttons streamer = say_stream() streamer.append(markdown_text=response_text) feedback_blocks = build_feedback_blocks() streamer.stop(blocks=feedback_blocks) # Store session ID for future context if new_session_id: session_store.set_session(channel_id, thread_ts, new_session_id) except Exception as e: logger.exception(f"Failed to handle app mention: {e}") say( text=f":warning: Something went wrong! ({e})", thread_ts=event.get("thread_ts") or event["ts"], ) ``` app\_mentioned.py ``` import refrom logging import Loggerfrom agents import Runnerfrom slack_bolt import BoltContext, Say, SayStream, SetStatusfrom slack_sdk import WebClientfrom agent import CaseyDeps, casey_agentfrom thread_context import conversation_storefrom listeners.views.feedback_builder import build_feedback_blocksdef handle_app_mentioned( client: WebClient, context: BoltContext, event: dict, logger: Logger, say: Say, say_stream: SayStream, set_status: SetStatus,): """Handle @Casey mentions in channels.""" try: channel_id = context.channel_id text = event.get("text", "") thread_ts = event.get("thread_ts") or event["ts"] user_id = context.user_id # Strip the bot mention from the text cleaned_text = re.sub(r"<@[A-Z0-9]+>", "", text).strip() if not cleaned_text: say( text="Hey there! How can I help you? Describe your IT issue and I'll do my best to assist.", thread_ts=thread_ts, ) return # Add eyes reaction only to the first message (not threaded replies) if not event.get("thread_ts"): client.reactions_add( channel=channel_id, timestamp=event["ts"], name="eyes", ) # Set assistant thread status with loading messages set_status( status="Thinking...", loading_messages=[ "Teaching the hamsters to type faster…", "Untangling the internet cables…", "Consulting the office goldfish…", "Polishing up the response just for you…", "Convincing the AI to stop overthinking…", ], ) # Get conversation history history = conversation_store.get_history(channel_id, thread_ts) # Build input for the agent if history: input_items = history + [{"role": "user", "content": cleaned_text}] else: input_items = cleaned_text # Run the agent deps = CaseyDeps( client=client, user_id=user_id, channel_id=channel_id, thread_ts=thread_ts, message_ts=event["ts"], ) result = Runner.run_sync(casey_agent, input=input_items, context=deps) # Stream response in thread with feedback buttons streamer = say_stream() streamer.append(markdown_text=result.final_output) feedback_blocks = build_feedback_blocks() streamer.stop(blocks=feedback_blocks) # Store conversation history conversation_store.set_history(channel_id, thread_ts, result.to_input_list()) except Exception as e: logger.exception(f"Failed to handle app mention: {e}") say( text=f":warning: Something went wrong! ({e})", thread_ts=event.get("thread_ts") or event["ts"], ) ``` * * * ## Onward: adding custom tools {#onward-adding-custom-tools} Casey comes with test tools and simulated systems. You can extend it with custom tools to make it a fully functioning Slack agent. In this example, we'll add a tool that makes live calls to check the GitHub status. 1. Create `agent/tools/{tool-name}.py` and define the tool with the `@tool` decorator: agent/tools/check\_github\_status.py ``` from claude_agent_sdk import toolimport httpx@tool( name="check_github_status", description="Check GitHub's current operational status", input_schema={},)async def check_github_status_tool(args): """Check if GitHub is operational.""" async with httpx.AsyncClient() as client: response = await client.get("https://www.githubstatus.com/api/v2/status.json") data = response.json() status = data["status"]["indicator"] description = data["status"]["description"] return { "content": [ { "type": "text", "text": f"**GitHub Status** — {status}\n{description}", } ] } ``` 2. Import the tool in `agent/casey.py`: agent/casey.py ``` from agent.tools import check_github_status_tool ``` 3. Register in `casey_tools_server`: agent/casey.py ``` casey_tools_server = create_sdk_mcp_server( name="casey-tools", version="1.0.0", tools=[ check_github_status_tool, # Add here # ... other tools ],) ``` 4. Add to `CASEY_TOOLS`: agent/casey.py ``` CASEY_TOOLS = [ "check_github_status", # Add here # ... other tools] ``` Use this example as a jumping off point for building out an agent with the capabilities you need! --- Source: https://docs.slack.dev/tools/bolt-python/concepts/app-home # Publishing views to App Home [Home tabs](/surfaces/app-home) are customizable surfaces accessible via the sidebar and search that allow apps to display views on a per-user basis. After enabling App Home within your app configuration, home tabs can be published and updated by passing a `user_id` and [view payload](/reference/interaction-payloads/view-interactions-payload/#view_submission) to the [`views.publish`](/reference/methods/views.publish) method. You can subscribe to the [`app_home_opened`](/reference/events/app_home_opened) event to listen for when users open your App Home. Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. ## Example {#example} ``` @app.event("app_home_opened")def update_home_tab(client, event, logger): try: # Call views.publish with the built-in client client.views_publish( # Use the user ID associated with the event user_id=event["user"], # Home tabs must be enabled in your app configuration view={ "type": "home", "blocks": [ { "type": "section", "text": { "type": "mrkdwn", "text": "*Welcome home, <@" + event["user"] + "> :house:*" } }, { "type": "section", "text": { "type": "mrkdwn", "text": "Learn how home tabs can be more useful and interactive ." } } ] } ) except Exception as e: logger.error(f"Error publishing home tab: {e}") ``` --- Source: https://docs.slack.dev/tools/bolt-python/concepts/async # Using async (asyncio) To use the async version of Bolt, you can import and initialize an `AsyncApp` instance (rather than `App`). `AsyncApp` relies on [AIOHTTP](https://docs.aiohttp.org) to make API requests, which means you'll need to install `aiohttp` (by adding to `requirements.txt` or running `pip install aiohttp`). Sample async projects can be found within the repository's [examples](https://github.com/slackapi/bolt-python/tree/main/examples) folder. ``` # Requirement: install aiohttpfrom slack_bolt.async_app import AsyncAppapp = AsyncApp()@app.event("app_mention")async def handle_mentions(event, client, say): # async function api_response = await client.reactions_add( channel=event["channel"], timestamp=event["ts"], name="eyes", ) await say("What's up?")if __name__ == "__main__": app.start(3000) ``` ## Using other frameworks {#using-other-frameworks} Internally `AsyncApp#start()` implements a [`AIOHTTP`](https://docs.aiohttp.org/) web server. If you prefer, you can use a framework other than `AIOHTTP` to handle incoming requests. This example uses [Sanic](https://sanicframework.org/), but the full list of adapters are in the [`adapter` folder](https://github.com/slackapi/bolt-python/tree/main/slack_bolt/adapter). The following commands install the necessary requirements and starts the Sanic server on port 3000. ``` # Install requirementspip install slack_bolt sanic uvicorn# Save the source as async_app.pyuvicorn async_app:api --reload --port 3000 --log-level debug ``` ``` from slack_bolt.async_app import AsyncAppapp = AsyncApp()# There is nothing specific to Sanic here!# AsyncApp is completely framework/runtime agnostic@app.event("app_mention")async def handle_app_mentions(say): await say("What's up?")import osfrom sanic import Sanicfrom sanic.request import Requestfrom slack_bolt.adapter.sanic import AsyncSlackRequestHandler# Create an adapter for Sanic with the App instanceapp_handler = AsyncSlackRequestHandler(app)# Create a Sanic appapi = Sanic(name="awesome-slack-app")@api.post("/slack/events")async def endpoint(req: Request): # app_handler internally runs the App's dispatch method return await app_handler.handle(req)if __name__ == "__main__": api.run(host="0.0.0.0", port=int(os.environ.get("PORT", 3000))) ``` --- Source: https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth # Authenticating with OAuth Slack apps installed on multiple workspaces will need to implement OAuth, then store installation information (like access tokens) securely. By providing `client_id`, `client_secret`, `scopes`, `installation_store`, and `state_store` when initializing App, Bolt for Python will handle the work of setting up OAuth routes and verifying state. If you're implementing a custom adapter, you can make use of our [OAuth library](/tools/python-slack-sdk/oauth/), which is what Bolt for Python uses under the hood. Bolt for Python will create a **Redirect URL** `slack/oauth_redirect`, which Slack uses to redirect users after they complete your app's installation flow. You will need to add this **Redirect URL** in your app configuration settings under **OAuth and Permissions**. This path can be configured in the `OAuthSettings` argument described below. Bolt for Python will also create a `slack/install` route, where you can find an **Add to Slack** button for your app to perform direct installs of your app. If you need any additional authorizations (user tokens) from users inside a team when your app is already installed or a reason to dynamically generate an install URL, you can pass your own custom URL generator to `oauth_settings` as `authorize_url_generator`. Bolt for Python automatically includes support for [org wide installations](/enterprise) in version `1.1.0+`. Org wide installations can be enabled in your app configuration settings under **Org Level Apps**. To learn more about the OAuth installation flow with Slack, [read the API documentation](/authentication/installing-with-oauth). ``` import osfrom slack_bolt import Appfrom slack_bolt.oauth.oauth_settings import OAuthSettingsfrom slack_sdk.oauth.installation_store import FileInstallationStorefrom slack_sdk.oauth.state_store import FileOAuthStateStoreoauth_settings = OAuthSettings( client_id=os.environ["SLACK_CLIENT_ID"], client_secret=os.environ["SLACK_CLIENT_SECRET"], scopes=["channels:read", "groups:read", "chat:write"], installation_store=FileInstallationStore(base_dir="./data/installations"), state_store=FileOAuthStateStore(expiration_seconds=600, base_dir="./data/states"))app = App( signing_secret=os.environ["SLACK_SIGNING_SECRET"], oauth_settings=oauth_settings) ``` ## Customizing OAuth defaults {#customizing-oauth-defaults} You can override the default OAuth using `oauth_settings`, which can be passed in during the initialization of App. You can override the following: * `install_path`: Override default path for "Add to Slack" button * `redirect_uri`: Override default redirect url path * `callback_options`: Provide custom success and failure pages at the end of the OAuth flow * `state_store`: Provide a custom state store instead of using the built in `FileOAuthStateStore` * `installation_store`: Provide a custom installation store instead of the built-in `FileInstallationStore` ``` from slack_bolt.oauth.callback_options import CallbackOptions, SuccessArgs, FailureArgsfrom slack_bolt.response import BoltResponsedef success(args: SuccessArgs) -> BoltResponse: assert args.request is not None return BoltResponse( status=200, # you can redirect users too body="Your own response to end-users here" )def failure(args: FailureArgs) -> BoltResponse: assert args.request is not None assert args.reason is not None return BoltResponse( status=args.suggested_status_code, body="Your own response to end-users here" )callback_options = CallbackOptions(success=success, failure=failure)import osfrom slack_bolt import Appfrom slack_bolt.oauth.oauth_settings import OAuthSettingsfrom slack_sdk.oauth.installation_store import FileInstallationStorefrom slack_sdk.oauth.state_store import FileOAuthStateStoreapp = App( signing_secret=os.environ.get("SLACK_SIGNING_SECRET"), installation_store=FileInstallationStore(base_dir="./data/installations"), oauth_settings=OAuthSettings( client_id=os.environ.get("SLACK_CLIENT_ID"), client_secret=os.environ.get("SLACK_CLIENT_SECRET"), scopes=["app_mentions:read", "channels:history", "im:history", "chat:write"], user_scopes=[], redirect_uri=None, install_path="/slack/install", redirect_uri_path="/slack/oauth_redirect", state_store=FileOAuthStateStore(expiration_seconds=600, base_dir="./data/states"), callback_options=callback_options, ),) ``` --- Source: https://docs.slack.dev/tools/bolt-python/concepts/authorization # Authorization Authorization is the process of determining which Slack credentials should be available while processing an incoming Slack request. Apps installed on a single workspace can simply pass their bot token into the `App` constructor using the `token` parameter. However, if your app will be installed on multiple workspaces, you have two options. The easier option is to use the built-in OAuth support. This will handle setting up OAuth routes and verifying state. Read the section on [authenticating with OAuth](/tools/bolt-python/concepts/authenticating-oauth) for details. For a more custom solution, you can set the `authorize` parameter to a function upon `App` instantiation. The `authorize` function should return [an instance of `AuthorizeResult`](https://github.com/slackapi/bolt-python/blob/main/slack_bolt/authorization/authorize_result.py), which contains information about who and where the request is coming from. `AuthorizeResult` should have a few specific properties, all of type `str`: * Either **`bot_token`** (xoxb) _or_ **`user_token`** (xoxp) are **required**. Most apps will use `bot_token` by default. Passing a token allows built-in functions (like `say()`) to work. * **`bot_user_id`** and **`bot_id`**, if using a `bot_token`. * **`enterprise_id`** and **`team_id`**, which can be found in requests sent to your app. * **`user_id`** only when using `user_token`. ## Example {#example} ``` import osfrom slack_bolt import App# Import the AuthorizeResult classfrom slack_bolt.authorization import AuthorizeResult# This is just an example (assumes there are no user tokens)# You should store authorizations in a secure DBinstallations = [ { "enterprise_id": "E1234A12AB", "team_id": "T12345", "bot_token": "xoxb-123abc", "bot_id": "B1251", "bot_user_id": "U12385" }, { "team_id": "T77712", "bot_token": "xoxb-102anc", "bot_id": "B5910", "bot_user_id": "U1239", "enterprise_id": "E1234A12AB" }]def authorize(enterprise_id, team_id, logger): # You can implement your own logic to fetch token here for team in installations: # enterprise_id doesn't exist for some teams is_valid_enterprise = "enterprise_id" not in team or enterprise_id == team["enterprise_id"] if is_valid_enterprise and team["team_id"] == team_id: # Return an instance of AuthorizeResult # If you don't store bot_id and bot_user_id, could also call `from_auth_test_response` with your bot_token to automatically fetch them return AuthorizeResult( enterprise_id=enterprise_id, team_id=team_id, bot_token=team["bot_token"], bot_id=team["bot_id"], bot_user_id=team["bot_user_id"] ) logger.error("No authorization information was found")app = App( signing_secret=os.environ["SLACK_SIGNING_SECRET"], authorize=authorize) ``` --- Source: https://docs.slack.dev/tools/bolt-python/concepts/commands # Listening & responding to commands Your app can use the `command()` method to listen to incoming slash command requests. The method requires a `command_name` of type `str`. Commands must be acknowledged with `ack()` to inform Slack your app has received the request. There are two ways to respond to slash commands. The first way is to use `say()`, which accepts a string or JSON payload. The second is `respond()` which is a utility for the `response_url`. These are explained in more depth in the [responding to actions](/tools/bolt-python/concepts/actions) section. When setting up commands within your app configuration, you'll append `/slack/events` to your request URL. Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. ## Example {#example} ``` # The echo command simply echoes on command@app.command("/echo")def repeat_text(ack, respond, command): # Acknowledge command request ack() respond(f"{command['text']}") ``` --- Source: https://docs.slack.dev/tools/bolt-python/concepts/context # Adding context All listeners have access to a `context` dictionary, which can be used to enrich requests with additional information. Bolt automatically attaches information that is included in the incoming request, like `user_id`, `team_id`, `channel_id`, and `enterprise_id`. `context` is just a dictionary, so you can directly modify it. ## Example {#example} ``` # Listener middleware to fetch tasks from external system using user IDdef fetch_tasks(context, event, next): user = event["user"] try: # Assume get_tasks fetchs list of tasks from DB corresponding to user ID user_tasks = db.get_tasks(user) tasks = user_tasks except Exception: # get_tasks() raises exception because no tasks are found tasks = [] finally: # Put user's tasks in context context["tasks"] = tasks next()# Listener middleware to create a list of section blocksdef create_sections(context, next): task_blocks = [] # Loops through tasks added to context in previous middleware for task in context["tasks"]: task_blocks.append( { "type": "section", "text": { "type": "mrkdwn", "text": f"*{task['title']}*\n{task['body']}" }, "accessory": { "type": "button", "text": { "type": "plain_text", "text": "See task" }, "url": task["url"], } } ) # Put list of blocks in context context["blocks"] = task_blocks next()# Listen for user opening app home# Include fetch_tasks middleware@app.event( event = "app_home_opened", middleware = [fetch_tasks, create_sections])def show_tasks(event, client, context): # Publish view to user's home tab client.views_publish( user_id=event["user"], view={ "type": "home", "blocks": context["blocks"] } ) ``` --- Source: https://docs.slack.dev/tools/bolt-python/concepts/custom-adapters # Custom adapters [Adapters](/tools/bolt-python/concepts/adapters) are flexible and can be adjusted based on the framework you prefer. There are two necessary components of adapters: * `__init__(app: App)`: Constructor that accepts and stores an instance of the Bolt `App`. * `handle(req: Request)`: Function (typically named `handle()`) that receives incoming Slack requests, parses them to conform to an instance of [`BoltRequest`](https://github.com/slackapi/bolt-python/blob/main/slack_bolt/request/request.py), then dispatches them to the stored Bolt app. `BoltRequest` instantiation accepts four parameters: Parameter Description Required? `body: str` The raw request body **Yes** `query: any` The query string data No `headers: Dict[str, Union[str, List[str]]]` Request headers No `context: BoltContext` Any context for the request No Your adapter will return [an instance of `BoltResponse`](https://github.com/slackapi/bolt-python/blob/main/slack_bolt/response/response.py) from the Bolt app. For more in-depth examples of custom adapters, look at the implementations of the [built-in adapters](https://github.com/slackapi/bolt-python/tree/main/slack_bolt/adapter). ## Example {#example} ``` # Necessary imports for Flaskfrom flask import Request, Response, make_responsefrom slack_bolt.app import Appfrom slack_bolt.request import BoltRequestfrom slack_bolt.response import BoltResponse# This example is a simplified version of the Flask adapter# For a more detailed, complete example, look in the adapter folder# github.com/slackapi/bolt-python/blob/main/slack_bolt/adapter/flask/handler.py# Takes in an HTTP request and converts it to a standard BoltRequestdef to_bolt_request(req: Request) -> BoltRequest: return BoltRequest( body=req.get_data(as_text=True), query=req.query_string.decode("utf-8"), headers=req.headers, )# Takes in a BoltResponse and converts it to a standard Flask responsedef to_flask_response(bolt_resp: BoltResponse) -> Response: resp: Response = make_response(bolt_resp.body, bolt_resp.status) for k, values in bolt_resp.headers.items(): for v in values: resp.headers.add_header(k, v) return resp# Instantiated from your app# Accepts a Flask appclass SlackRequestHandler: def __init__(self, app: App): self.app = app # handle() will be called from your Flask app # when you receive a request from Slack def handle(self, req: Request) -> Response: # This example does not cover OAuth if req.method == "POST": # Dispatch the request for Bolt to handle and route bolt_resp: BoltResponse = self.app.dispatch(to_bolt_request(req)) return to_flask_response(bolt_resp) return make_response("Not Found", 404) ``` --- Source: https://docs.slack.dev/tools/bolt-python/concepts/custom-steps-dynamic-options # Custom Steps dynamic options for Workflow Builder ## Background {#background} [Legacy steps from apps](/changelog/2023-08-workflow-steps-from-apps-step-back) previously enabled Slack apps to create and process custom workflow steps, which could then be shared and used by anyone in Workflow Builder. To support your transition away from them, custom steps used as dynamic options are available. These allow you to use data defined when referencing the step in Workflow Builder as inputs to the step. ## Example use case {#use-case} Let's say a builder wants to add a custom step in Workflow Builder that creates an issue in an external issue-tracking system. First, they'll need to specify a project. Once a project is selected, a project-specific list of fields can be presented to them to choose from when creating the issue. As a developer, dynamic options allow you to supply data to input parameters of custom steps so that you can provide builders with varying sets of fields based on the builders' selections. In this example, the primary step would invoke a separate project selection step that retrieves the list of available projects. The builder-selected item from the retrieved list would then be used as the input to the secondary issue creation step. There are two parts necessary for Slack apps to support dynamic options: custom step definitions, and handling custom step dynamic options. We'll take a look at both in the following sections. ## Custom step definitions {#custom-step-definitions} When defining an input to a custom step intended to be dynamic (rather than explicitly defining a set of input parameters up front), you'll define a `dynamic_options` property that points to another custom step designed to return the set of dynamic elements once this step is added to a workflow from Workflow Builder. An input parameter for a custom step can reference a different custom step that defines what data is available for it to return. One Slack app could even use another Slack app’s custom step to define dynamic options for one of its inputs. The following code snippet from our issue creation example discussed above shows a `create-issue` custom step that will be used as a workflow step. Another custom step, the `get-projects` step, will dynamically populate the project input parameter to be configured by a builder. This `get-projects` step provides an `array` containing projects fetched dynamically from the external issue-tracking system. ``` "functions": { "create-issue": { "title": "Create Issue", "description": "", "input_parameters": { "support_channel": { "type": "slack#/types/channel_id", "title": "Support Channel", "description": "", "name": "support_channel" }, "project": { "type": "string", "title": "Project", "description": "A project from the issue tracking system", "is_required": true, "dynamic_options": { "function": "#/functions/get-projects", "inputs": {} } }, }, "output_parameters": {} }, "get-projects": { "title": "Get Projects", "description": "Get the available project from the issue tracking system", "input_parameters": {}, "output_parameters": { "options": { "type": "slack#/types/options_select", "title": "Project Options", } } } }, ``` ### Defining the function and inputs attributes {#define-attributes} Defining the `function` and `inputs` attributes of the `dynamic_options` property would look as follows: ``` "dynamic_options": { "function": "#/functions/get-projects", "inputs": {}} ``` The `function` attribute specifies the step reference used to resolve the options of the input parameter. For example: `"#/functions/get-projects"`. The `inputs` attribute defines the parameters to be passed as inputs to the step referenced by the `function` attribute. For example: ``` "inputs": { "selected_user_id": { "value": "{{input_parameters.user_id}}" }, "query": { "value": "{{client.query}}" }} ``` The following format can be used to reference any input parameter defined by the step: `{{input_parameters.}}`. In addition, the `{{client.query}}` parameter can be used as a placeholder for an input value. The `{{client.builder_context}}` parameter will inject the [`slack#/types/user_context`](/tools/deno-slack-sdk/reference/slack-types/#usercontext) of the user building the workflow as the value to the input parameter. ### Types of dynamic options UIs {#dynamic-option-UIs} The above example demonstrates one possible UI to be rendered for builders: a single-select drop-down menu of dynamic options. However, dynamic options in Workflow Builder can be rendered in one of two ways: as a drop-down menu (single-select or multi-select), or as a set of fields. The type is dictated by the output parameter of the custom step used as a dynamic option. In order to use a custom step in a dynamic option context, its output must adhere to a defined interface, that is, it must have an `options` parameter of type [`options_select`](/tools/deno-slack-sdk/reference/slack-types#options_select) or [`options_field`](/tools/deno-slack-sdk/reference/slack-types#options_field), as shown in the following code snippet. ``` "output_parameters": { "options": { "type": "slack#/types/options_select" or "slack#/types/options_field", "title": "Custom Options", "description": "Options to be used in a dynamic context", } ...} ``` #### Drop-down menus {#drop-down} Your dynamic input parameter can be rendered as a drop-down menu, which will use the options obtained from a custom step with an `options` output parameter of the type [`options_select`](/tools/deno-slack-sdk/reference/slack-types#options_select). The drop-down menu UI component can be rendered in two ways: single-select, or multi-select. To render the dynamic input as a single-select menu, the input parameter defining the dynamic option must be of the type [`string`](/tools/deno-slack-sdk/reference/slack-types#string). ``` "step-with-dynamic-input": { "title": "Step that uses a dynamic input", "description": "This step uses a dynamic input rendered as a single-select menu", "input_parameters": { "dynamic_single_select": { "type": "string", // this must be of type string for single-select "title": "dynamic single select drop-down menu", "description": "A dynamically-populated single-select drop-down menu", "is_required": true, "dynamic_options": { "function": "#/functions/get-options", "inputs": {}, }, } }, "output_parameters": {}} ``` To render the dynamic input as a multi-select menu, the input parameter defining the dynamic option must be of the type [`array`](/tools/deno-slack-sdk/reference/slack-types#array), and its `items` must be of type [`string`](/tools/deno-slack-sdk/reference/slack-types#string). ``` "step-with-dynamic-input": { "title": "Step that uses a dynamic input", "description": "This step uses a dynamic input rendered as a multi-select menu", "input_parameters": { "dynamic_multi_select": { "type": "array", // this must be of type array for multi-select "items": { "type": "string" }, "title": "dynamic single select drop-down menu", "description": "A dynamically-populated multi-select drop-down menu", "dynamic_options": { "function": "#/functions/get-options", "inputs": {}, }, } }, "output_parameters": {}} ``` #### Fields {#fields} In the code snippet below, the input parameter is rendered as a set of fields with keys and values. The option fields are obtained from a custom step with an `options` output parameter of type [`options_field`](/tools/deno-slack-sdk/reference/slack-types#options_field). The input parameter that defines the dynamic option must be of type [`object`](/tools/deno-slack-sdk/reference/slack-types#object), as the completed set of fields in Workflow Builder will be passed to the custom step as an [untyped object](/tools/deno-slack-sdk/reference/slack-types#untyped-object) during workflow execution. ``` "test-field-dynamic-options": { "title": "Test dynamic field options", "description": "", "input_parameters": { "dynamic_fields": { "type": "object", "title": "Dynamic custom field options", "description": "A dynamically-populated section of input fields", "dynamic_options": { "function": "#/functions/get-field-options", "inputs": {} "selection_type": "key-value", } } }, "output_parameters": {}} ``` ### Dynamic option types {#dynamic-option-types} As mentioned earlier, in order to use a custom step as a dynamic option, its output must adhere to a defined interface: it must have an `options` output parameter of the type either [`options_select`](/tools/deno-slack-sdk/reference/slack-types#options_select) or [`options_field`](/tools/deno-slack-sdk/reference/slack-types#options_field). To take a look at these in more detail, refer to our [Options Slack type](/tools/deno-slack-sdk/reference/slack-types#options) documentation. ## Dynamic options handler {#dynamic-option-handler} Each custom step defined in the manifest needs a corresponding handler in your Slack app. Although implemented similarly to existing function execution event handlers, there are two key differences between regular custom step invocations and those used for dynamic options: * The custom step must have an `options` output parameter that is of type [`options_select`](/tools/deno-slack-sdk/reference/slack-types#options_select) or [`options_field`](/tools/deno-slack-sdk/reference/slack-types#options_field). * The [`function_executed`](/reference/events/function_executed) event must be handled synchronously. This optimizes the response time of returned dynamic options and provides a crisp builder experience. ### Asynchronous event handling {#async} By default, the Bolt family of frameworks handles `function_executed` events asynchronously. For example, the various modal-related API methods provide two ways to update a view: synchronously using a `response_action` HTTP response, or asynchronously using a separate HTTP API call. Using the asynchronous approach allows developers to handle events free of timeouts, but this isn't desired for dynamic options as it introduces delays and violates our stated goal of providing a crisp builder experience. ### Synchronous event handling {#sync} Dynamic options support synchronous handling of `function_executed` events. By ensuring that the function execution’s state is complete with output parameters provided before responding to the `function_executed` event, Slack can quickly provide Workflow Builder with the requisite dynamic options. ### Implementation {#implementation} To optimize the response time of dynamic options, you must acknowledge the incoming event after calling the [`function.completeSuccess`](/reference/methods/functions.completeSuccess) or [`function.completeError`](/reference/methods/functions.completeError) API methods, minimizing asynchronous latency. The `function.completeSuccess` and `function.completeError` API methods are invoked in the complete and fail helper functions. ([For example](https://github.com/slackapi/bolt-python?tab=readme-ov-file#making-things-happen)). A new `auto_acknowledge` flag allows you more granular control over whether specific event handlers should operate in synchronous or asynchronous response modes in order to enable a smooth dynamic options experience. #### Example {#bolt-py} In [Bolt for Python](https://docs.slack.dev/tools/bolt-python/), you can set `auto_acknowledge=False` on a specific function decorator. This allows you to manually control when the `ack()` event acknowledgement helper function is executed. It flips Bolt to synchronous `function_executed` event handling mode for the specific handler. ``` @app.function("get-projects", auto_acknowledge=False)def handle_get_projects(ack: Ack, complete: Complete): try: complete( outputs={ "options": [ { "text": { "type": "plain_text", "text": "Secret Squirrel Project", }, "value": "p1", }, { "text": { "type": "plain_text", "text": "Public Kangaroo Project", }, "value": "p2", }, ] } ) finally: ack() ``` ✨ **To learn more about the Bolt family of frameworks and tools**, check out our [Slack Developer Tools](/tools). --- Source: https://docs.slack.dev/tools/bolt-python/concepts/custom-steps # Listening and responding to custom steps Your app can use the `function()` method to listen to incoming [custom step requests](/workflows/workflow-steps). Custom steps are used in Workflow Builder to build workflows. The method requires a step `callback_id` of type `str`. This `callback_id` must also be defined in your [Function](/reference/app-manifest#functions) definition. Custom steps must be finalized using the `complete()` or `fail()` listener arguments to notify Slack that your app has processed the request. * `complete()` requires **one** argument: `outputs` of type `dict`. It ends your custom step **successfully** and provides a dictionary containing the outputs of your custom step as per its definition. * `fail()` requires **one** argument: `error` of type `str`. It ends your custom step **unsuccessfully** and provides a message containing information regarding why your custom step failed. You can reference your custom step's inputs using the `inputs` listener argument of type `dict`. Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn about the available listener arguments. ``` # This sample custom step formats an input and outputs it@app.function("sample_custom_step")def sample_step_callback(inputs: dict, fail: Fail, complete: Complete): try: message = inputs["message"] complete( outputs={ "message": f":wave: You submitted the following message: \n\n>{message}" } ) except Exception as e: fail(f"Failed to handle a custom step request (error: {e})") raise e ``` Example app manifest definition ``` ..."functions": { "sample_custom_step": { "title": "Sample custom step", "description": "Run a sample custom step", "input_parameters": { "message": { "type": "string", "title": "Message", "description": "A message to be formatted by the custom step", "is_required": true, } }, "output_parameters": { "message": { "type": "string", "title": "Messge", "description": "A formatted message", "is_required": true, } } }} ``` * * * ### Listening to custom step interactivity events {#listening-to-custom-step-interactivity-events} Your app's custom steps may create interactivity points for users, for example: Post a message with a button. If such interaction points originate from a custom step execution, the events sent to your app representing the end-user interaction with these points are considered to be _function-scoped interactivity events_. These interactivity events can be handled by your app using the same concepts we covered earlier, such as [Listening to actions](/tools/bolt-python/concepts/actions). _function-scoped interactivity events_ will contain data related to the custom step (`function_executed` event) they were spawned from, such as custom step `inputs` and access to `complete()` and `fail()` listener arguments. Your app can skip calling `complete()` or `fail()` in the `function()` handler method if the custom step creates an interaction point that requires user interaction before the step can end. However, in the relevant interactivity handler method, your app must invoke `complete()` or `fail()` to notify Slack that the custom step has been processed. You’ll notice in all interactivity handler examples, `ack()` is used. It is required to call the `ack()` function within an interactivity listener to acknowledge that the request was received from Slack. This is discussed in the [acknowledging requests section](/tools/bolt-python/concepts/acknowledge). ``` # This sample custom step posts a message with a button@app.function("custom_step_button")def sample_step_callback(inputs, say, fail): try: say( channel=inputs["user_id"], # sending a DM to this user text="Click the button to signal the step completion", blocks=[ { "type": "section", "text": {"type": "mrkdwn", "text": "Click the button to signal step completion"}, "accessory": { "type": "button", "text": {"type": "plain_text", "text": "Complete step"}, "action_id": "sample_click", }, } ], ) except Exception as e: fail(f"Failed to handle a function request (error: {e})")# Your listener will be called every time a block element with the action_id "sample_click" is triggered@app.action("sample_click")def handle_sample_click(ack, body, context, client, complete, fail): ack() try: # Since the button no longer works, we should remove it client.chat_update( channel=context.channel_id, ts=body["message"]["ts"], text="Congrats! You clicked the button", ) # Signal that the custom step completed successfully complete({"user_id": context.actor_user_id}) except Exception as e: fail(f"Failed to handle a function request (error: {e})") ``` Example app manifest definition ``` ..."functions": { "custom_step_button": { "title": "Custom step with a button", "description": "Custom step that waits for a button click", "input_parameters": { "user_id": { "type": "slack#/types/user_id", "title": "User", "description": "The recipient of a message with a button", "is_required": true, } }, "output_parameters": { "user_id": { "type": "slack#/types/user_id", "title": "User", "description": "The user that completed the function", "is_required": true } } }} ``` Learn more about responding to interactivity, see the [Slack API documentation](/interactivity/handling-user-interaction). --- Source: https://docs.slack.dev/tools/bolt-python/concepts/errors # Handling errors If an error occurs in a listener, you can handle it directly using a try/except block. Errors associated with your app will be of type `BoltError`. Errors associated with calling Slack APIs will be of type `SlackApiError`. By default, the global error handler will log all non-handled exceptions to the console. To handle global errors yourself, you can attach a global error handler to your app using the `app.error(fn)` function. ## Example {#example} ``` @app.errordef custom_error_handler(error, body, logger): logger.exception(f"Error: {error}") logger.info(f"Request body: {body}") ``` --- Source: https://docs.slack.dev/tools/bolt-python/concepts/event-listening # Listening to events You can listen to [any Events API event](/reference/events) using the `event()` method after subscribing to it in your app configuration. This allows your app to take action when something happens in a workspace where it's installed, like a user reacting to a message or joining a channel. The `event()` method requires an `eventType` of type `str`. Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. ``` # When a user joins the workspace, send a message in a predefined channel asking them to introduce themselves@app.event("team_join")def ask_for_introduction(event, say): welcome_channel_id = "C12345" user_id = event["user"] text = f"Welcome to the team, <@{user_id}>! 🎉 You can introduce yourself in this channel." say(text=text, channel=welcome_channel_id) ``` ## Filtering on message subtypes {#filtering-on-message-subtypes} The `message()` listener is equivalent to `event("message")`. You can filter on subtypes of events by passing in the additional key `subtype`. Common message subtypes like `bot_message` and `message_replied` can be found [on the message event page](/reference/events/message#subtypes). You can explicitly filter for events without a subtype by explicitly setting `None`. ``` # Matches all modified messages@app.event({ "type": "message", "subtype": "message_changed"})def log_message_change(logger, event): user, text = event["user"], event["text"] logger.info(f"The user {user} changed the message to {text}") ``` --- Source: https://docs.slack.dev/tools/bolt-python/concepts/global-middleware # Global middleware Global middleware is run for all incoming requests, before any listener middleware. You can add any number of global middleware to your app by passing middleware functions to `app.use()`. Middleware functions are called with the same arguments as listeners, with an additional `next()` function. Both global and listener middleware must call `next()` to pass control of the execution chain to the next middleware. Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. ## Example {#example} ``` @app.usedef auth_acme(client, context, logger, payload, next): slack_user_id = payload["user"] help_channel_id = "C12345" try: # Look up user in external system using their Slack user ID user = acme.lookup_by_id(slack_user_id) # Add that to context context["user"] = user except Exception: client.chat_postEphemeral( channel=payload["channel"], user=slack_user_id, text=f"Sorry <@{slack_user_id}>, you aren't registered in Acme or there was an error with authentication. Please post in <#{help_channel_id}> for assistance" ) # Pass control to the next middleware next() ``` --- Source: https://docs.slack.dev/tools/bolt-python/concepts/lazy-listeners # Lazy listeners (FaaS) Lazy Listeners are a feature which make it easier to deploy Slack apps to FaaS (Function-as-a-Service) environments. Please note that this feature is only available in Bolt for Python, and we are not planning to add the same to other Bolt frameworks. Typically when handling actions, commands, shortcuts, options and view submissions, you must acknowledge the request from Slack by calling `ack()` within 3 seconds. Calling `ack()` results in sending an HTTP 200 OK response to Slack, letting Slack know that you're handling the response. We normally encourage you to do this as the very first step in your handler function. However, when running your app on FaaS or similar runtimes which **do not allow you to run threads or processes after returning an HTTP response**, we cannot follow the typical pattern of acknowledgement first, processing later. To work with these runtimes, set the `process_before_response` flag to `True`. When this flag is true, the Bolt framework holds off sending an HTTP response until all the things in a listener function are done. You need to complete your processing within 3 seconds or you will run into errors with Slack timeouts. Note that in the case of events, while the listener doesn't need to explicitly call the `ack()` method, it still needs to complete its function within 3 seconds as well. To allow you to still run more time-consuming processes as part of your handler, we've added a lazy listener function mechanism. Rather than acting as a decorator, a lazy listener accepts two keyword args: * `ack: Callable`: Responsible for calling `ack()` within 3 seconds * `lazy: List[Callable]`: Responsible for handling time-consuming processes related to the request. The lazy function does not have access to `ack()`. ``` def respond_to_slack_within_3_seconds(body, ack): text = body.get("text") if text is None or len(text) == 0: ack(f":x: Usage: /start-process (description here)") else: ack(f"Accepted! (task: {body['text']})")import timedef run_long_process(respond, body): time.sleep(5) # longer than 3 seconds respond(f"Completed! (task: {body['text']})")app.command("/start-process")( # ack() is still called within 3 seconds ack=respond_to_slack_within_3_seconds, # Lazy function is responsible for processing the event lazy=[run_long_process]) ``` ## Example with AWS Lambda {#example-with-aws-lambda} This example deploys the code to [AWS Lambda](https://aws.amazon.com/lambda/). There are more examples within the [`examples`](https://github.com/slackapi/bolt-python/tree/main/examples/aws_lambda) folder. ``` pip install slack_bolt# Save the source code as main.py# and refer handler as `handler: main.handler` in config.yaml# https://pypi.org/project/python-lambda/pip install python-lambda# Configure config.yml properly# lambda:InvokeFunction & lambda:GetFunction are required for running lazy listenersexport SLACK_SIGNING_SECRET=***export SLACK_BOT_TOKEN=xoxb-***echo 'slack_bolt' > requirements.txtlambda deploy --config-file config.yaml --requirements requirements.txt ``` ``` from slack_bolt import Appfrom slack_bolt.adapter.aws_lambda import SlackRequestHandler# process_before_response must be True when running on FaaSapp = App(process_before_response=True)def respond_to_slack_within_3_seconds(body, ack): text = body.get("text") if text is None or len(text) == 0: ack(":x: Usage: /start-process (description here)") else: ack(f"Accepted! (task: {body['text']})")import timedef run_long_process(respond, body): time.sleep(5) # longer than 3 seconds respond(f"Completed! (task: {body['text']})")app.command("/start-process")( ack=respond_to_slack_within_3_seconds, # responsible for calling `ack()` lazy=[run_long_process] # unable to call `ack()` / can have multiple functions)def handler(event, context): slack_handler = SlackRequestHandler(app=app) return slack_handler.handle(event, context) ``` Please note that the following IAM permissions would be required for running this example app. ``` { "Version": "2012-10-17", "Statement": [ { "Sid": "VisualEditor0", "Effect": "Allow", "Action": [ "lambda:InvokeFunction", "lambda:GetFunction" ], "Resource": "*" } ]} ``` --- Source: https://docs.slack.dev/tools/bolt-python/concepts/listener-middleware # Listener middleware Listener middleware is only run for the listener in which it's passed. You can pass any number of middleware functions to the listener using the `middleware` parameter, which must be a list that contains one to many middleware functions. If your listener middleware is a quite simple one, you can use a listener matcher, which returns `bool` value (`True` for proceeding) instead of requiring `next()` method call. Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. ## Example {#example} ``` # Listener middleware which filters out messages from a botdef no_bot_messages(message, next): if "bot_id" not in message: next()# This listener only receives messages from humans@app.event(event="message", middleware=[no_bot_messages])def log_message(logger, event): logger.info(f"(MSG) User: {event['user']}\nMessage: {event['text']}")# Listener matchers: simplified version of listener middlewaredef no_bot_messages(message) -> bool: return "bot_id" not in message@app.event( event="message", matchers=[no_bot_messages] # or matchers=[lambda message: message.get("subtype") != "bot_message"])def log_message(logger, event): logger.info(f"(MSG) User: {event['user']}\nMessage: {event['text']}") ``` --- Source: https://docs.slack.dev/tools/bolt-python/concepts/logging # Logging By default, Bolt will log information from your app to the output destination. After you've imported the `logging` module, you can customize the root log level by passing the `level` parameter to `basicConfig()`. The available log levels in order of least to most severe are `debug`, `info`, `warning`, `error`, and `critical`. Outside of a global context, you can also log a single message corresponding to a specific level. Because Bolt uses Python’s [standard logging module](https://docs.python.org/3/library/logging.html), you can use any its features. ## Example {#example} ``` import logging# logger in a global context# requires importing logginglogging.basicConfig(level=logging.DEBUG)@app.event("app_mention")def handle_mention(body, say, logger): user = body["event"]["user"] # single logger call # global logger is passed to listener logger.debug(body) say(f"{user} mentioned your app") ``` --- Source: https://docs.slack.dev/tools/bolt-python/concepts/message-listening # Listening to messages To listen to messages that [your app has access to receive](/messaging/retrieving-messages), you can use the `message()` method which filters out events that aren't of type `message`. `message()` accepts an argument of type `str` or `re.Pattern` object that filters out any messages that don't match the pattern. Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. ``` # This will match any message that contains 👋@app.message(":wave:")def say_hello(message, say): user = message['user'] say(f"Hi there, <@{user}>!") ``` ## Using a regular expression pattern {#using-a-regular-expression-pattern} The `re.compile()` method can be used instead of a string for more granular matching. ``` import re@app.message(re.compile("(hi|hello|hey)"))def say_hello_regex(say, context): # regular expression matches are inside of context.matches greeting = context['matches'][0] say(f"{greeting}, how are you?") ``` --- Source: https://docs.slack.dev/tools/bolt-python/concepts/message-sending # Sending messages Within your listener function, `say()` is available whenever there is an associated conversation (for example, a conversation where the event or action which triggered the listener occurred). `say()` accepts a string to post simple messages and JSON payloads to send more complex messages. The message payload you pass in will be sent to the associated conversation. In the case that you'd like to send a message outside of a listener or you want to do something more advanced (like handle specific errors), you can call `client.chat_postMessage` [using the client attached to your Bolt instance](/tools/bolt-python/concepts/web-api). Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. ``` # Listens for messages containing "knock knock" and responds with an italicized "who's there?"@app.message("knock knock")def ask_who(message, say): say("_Who's there?_") ``` ## Sending a message with blocks {#sending-a-message-with-blocks} `say()` accepts more complex message payloads to make it easy to add functionality and structure to your messages. To explore adding rich message layouts to your app, read through [the guide on our API site](/messaging/#structure) and look through templates of common app flows [in the Block Kit Builder](https://api.slack.com/tools/block-kit-builder?template=1). ``` # Sends a section block with datepicker when someone reacts with a 📅 emoji@app.event("reaction_added")def show_datepicker(event, say): reaction = event["reaction"] if reaction == "calendar": blocks = [{ "type": "section", "text": {"type": "mrkdwn", "text": "Pick a date for me to remind you"}, "accessory": { "type": "datepicker", "action_id": "datepicker_remind", "initial_date": "2020-05-04", "placeholder": {"type": "plain_text", "text": "Select a date"} } }] say( blocks=blocks, text="Pick a date for me to remind you" ) ``` ## Streaming messages {#streaming-messages} You can have your app's messages stream in to replicate conventional agent behavior. Bolt for Python provides a `say_stream` utility as a listener argument available for `app.event` and `app.message` listeners. The `say_stream` utility streamlines calling the Python Slack SDK's [`WebClient.chat_stream`](https://docs.slack.dev/tools/python-slack-sdk/reference/web/client.html#slack_sdk.web.client.WebClient.chat_stream) helper utility by sourcing parameter values from the relevant event payload. Parameter Value `channel_id` Sourced from the event payload. `thread_ts` Sourced from the event payload. Falls back to the `ts` value if available. `recipient_team_id` Sourced from the event `team_id` (`enterprise_id` if the app is installed on an org). `recipient_user_id` Sourced from the `user_id` of the event. If either `channel_id` or `thread_ts` cannot be sourced, the utility will be `None`. For information on calling the `chat_*Stream` API methods directly, see the [_Sending streaming messages_](/tools/python-slack-sdk/web#sending-streaming-messages) section of the Python Slack SDK docs. ### Example {#example} ``` import osfrom slack_bolt import App, SayStreamfrom slack_bolt.adapter.socket_mode import SocketModeHandlerfrom slack_sdk import WebClientapp = App(token=os.environ.get("SLACK_BOT_TOKEN"))@app.event("app_mention")def handle_app_mention(client: WebClient, say_stream: SayStream): stream = say_stream() stream.append(markdown_text="Someone rang the bat signal!") stream.stop()@app.message("")def handle_message(client: WebClient, say_stream: SayStream): stream = say_stream() stream.append(markdown_text="Let me consult my *vast knowledge database*...") stream.stop()if __name__ == "__main__": SocketModeHandler(app, os.environ.get("SLACK_APP_TOKEN")).start() ``` #### Adding feedback buttons after a stream {#adding-feedback-buttons-after-a-stream} You can pass a [feedback buttons](/reference/block-kit/block-elements/feedback-buttons-element) block element to `stream.stop` to provide feedback buttons to the user at the bottom of the message. Interaction with these buttons will send a block action event to your app to receive the feedback. ``` stream.stop(blocks=feedback_block) ``` ``` def create_feedback_block() -> List[Block]: blocks: List[Block] = [ ContextActionsBlock( elements=[ FeedbackButtonsElement( action_id="feedback", positive_button=FeedbackButtonObject( text="Good Response", accessibility_label="Submit positive feedback on this response", value="good-feedback", ), negative_button=FeedbackButtonObject( text="Bad Response", accessibility_label="Submit negative feedback on this response", value="bad-feedback", ), ) ] ) ] return blocks ``` --- Source: https://docs.slack.dev/tools/bolt-python/concepts/opening-modals # Opening modals [Modals](/surfaces/modals) are focused surfaces that allow you to collect user data and display dynamic information. You can open a modal by passing a valid `trigger_id` and a [view payload](/reference/interaction-payloads/view-interactions-payload/#view_submission) to the built-in client's [`views.open`](/reference/methods/views.open/) method. Your app receives `trigger_id` parameters in payloads sent to your Request URL triggered user invocation like a slash command, button press, or interaction with a select menu. Read more about modal composition in the [API documentation](/surfaces/modals#composing_views). Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. ## Example {#example} ``` # Listen for a shortcut invocation@app.shortcut("open_modal")def open_modal(ack, body, client): # Acknowledge the command request ack() # Call views_open with the built-in client client.views_open( # Pass a valid trigger_id within 3 seconds of receiving it trigger_id=body["trigger_id"], # View payload view={ "type": "modal", # View identifier "callback_id": "view_1", "title": {"type": "plain_text", "text": "My App"}, "submit": {"type": "plain_text", "text": "Submit"}, "blocks": [ { "type": "section", "text": {"type": "mrkdwn", "text": "Welcome to a modal with _blocks_"}, "accessory": { "type": "button", "text": {"type": "plain_text", "text": "Click me!"}, "action_id": "button_abc" } }, { "type": "input", "block_id": "input_c", "label": {"type": "plain_text", "text": "What are your hopes and dreams?"}, "element": { "type": "plain_text_input", "action_id": "dreamy_input", "multiline": True } } ] } ) ``` --- Source: https://docs.slack.dev/tools/bolt-python/concepts/select-menu-options # Listening & responding to select menu options The `options()` method listens for incoming option request payloads from Slack. [Similar to `action()`](/tools/bolt-python/concepts/actions), an `action_id` or constraints object is required. In order to load external data into your select menus, you must provide an options load URL in your app configuration, appended with `/slack/events`. While it's recommended to use `action_id` for `external_select` menus, dialogs do not support Block Kit so you'll have to use the constraints object to filter on a `callback_id`. To respond to options requests, you'll need to call `ack()` with a valid `options` or `option_groups` list. Both [external select response examples](/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select) and [dialog response examples](/reference/block-kit/block-elements/multi-select-menu-element#conversation_multi_select) can be found on our API site. Additionally, you may want to apply filtering logic to the returned options based on user input. This can be accomplished by using the `payload` argument to your options listener and checking for the contents of the `value` property within it. Based on the `value` you can return different options. All listeners and middleware handlers in Bolt for Python have access to [many useful arguments](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) - be sure to check them out! Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. ## Example {#example} ``` # Example of responding to an external_select options request@app.options("external_action")def show_options(ack, payload): options = [ { "text": {"type": "plain_text", "text": "Option 1"}, "value": "1-1", }, { "text": {"type": "plain_text", "text": "Option 2"}, "value": "1-2", }, ] keyword = payload.get("value") if keyword is not None and len(keyword) > 0: options = [o for o in options if keyword in o["text"]["text"]] ack(options=options) ``` --- Source: https://docs.slack.dev/tools/bolt-python/concepts/shortcuts # Listening & responding to shortcuts The `shortcut()` method supports both [global shortcuts](/interactivity/implementing-shortcuts#global) and [message shortcuts](/interactivity/implementing-shortcuts#messages). Shortcuts are invokable entry points to apps. Global shortcuts are available from within search and text composer area in Slack. Message shortcuts are available in the context menus of messages. Your app can use the `shortcut()` method to listen to incoming shortcut requests. The method requires a `callback_id` parameter of type `str` or `re.Pattern`. Shortcuts must be acknowledged with `ack()` to inform Slack that your app has received the request. Shortcuts include a `trigger_id` which an app can use to [open a modal](/tools/bolt-python/concepts/opening-modals) that confirms the action the user is taking. When setting up shortcuts within your app configuration, as with other URLs, you'll append `/slack/events` to your request URL. ⚠️ Note that global shortcuts do **not** include a channel ID. If your app needs access to a channel ID, you may use a [`conversations_select`](/reference/block-kit/block-elements/multi-select-menu-element#conversation_multi_select) element within a modal. Message shortcuts do include a channel ID. Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. ``` # The open_modal shortcut listens to a shortcut with the callback_id "open_modal"@app.shortcut("open_modal")def open_modal(ack, shortcut, client): # Acknowledge the shortcut request ack() # Call the views_open method using the built-in WebClient client.views_open( trigger_id=shortcut["trigger_id"], # A simple view payload for a modal view={ "type": "modal", "title": {"type": "plain_text", "text": "My App"}, "close": {"type": "plain_text", "text": "Close"}, "blocks": [ { "type": "section", "text": { "type": "mrkdwn", "text": "About the simplest modal you could conceive of :smile:\n\nMaybe or ." } }, { "type": "context", "elements": [ { "type": "mrkdwn", "text": "Psssst this modal was designed using " } ] } ] } ) ``` ## Listening to shortcuts using a constraint object {#listening-to-shortcuts-using-a-constraint-object} You can use a constraints object to listen to `callback_id`s, and `type`s. Constraints in the object can be of type `str` or `re.Pattern`. ``` # Your listener will only be called when the callback_id matches 'open_modal' AND the type matches 'message_action'@app.shortcut({"callback_id": "open_modal", "type": "message_action"})def open_modal(ack, shortcut, client): # Acknowledge the shortcut request ack() # Call the views_open method using one of the built-in WebClients client.views_open( trigger_id=shortcut["trigger_id"], view={ "type": "modal", "title": {"type": "plain_text", "text": "My App"}, "close": {"type": "plain_text", "text": "Close"}, "blocks": [ { "type": "section", "text": { "type": "mrkdwn", "text": "About the simplest modal you could conceive of :smile:\n\nMaybe or ." } }, { "type": "context", "elements": [ { "type": "mrkdwn", "text": "Psssst this modal was designed using " } ] } ] } ) ``` --- Source: https://docs.slack.dev/tools/bolt-python/concepts/socket-mode # Using Socket Mode With the introduction of [Socket Mode](/apis/events-api/using-socket-mode), Bolt for Python introduced support in version `1.2.0`. With Socket Mode, instead of creating a server with endpoints that Slack sends payloads too, the app will instead connect to Slack via a WebSocket connection and receive data from Slack over the socket connection. Make sure to enable Socket Mode in your app configuration settings. To use the Socket Mode, add `SLACK_APP_TOKEN` as an environment variable. You can get your App Token in your app configuration settings under the **Basic Information** section. While we recommend using [the built-in Socket Mode adapter](https://github.com/slackapi/bolt-python/tree/main/slack_bolt/adapter/socket_mode/builtin), there are a few other 3rd party library based implementations. Here is the list of available adapters. PyPI Project Bolt Adapter [slack\_sdk](https://pypi.org/project/slack-sdk/) [slack\_bolt.adapter.socket\_mode](https://github.com/slackapi/bolt-python/tree/main/slack_bolt/adapter/socket_mode/builtin) [websocket\_client](https://pypi.org/project/websocket_client/) [slack\_bolt.adapter.socket\_mode.websocket\_client](https://github.com/slackapi/bolt-python/tree/main/slack_bolt/adapter/socket_mode/websocket_client) [aiohttp](https://pypi.org/project/aiohttp/) (asyncio-based) [slack\_bolt.adapter.socket\_mode.aiohttp](https://github.com/slackapi/bolt-python/tree/main/slack_bolt/adapter/socket_mode/aiohttp) [websockets](https://pypi.org/project/websockets/) (asyncio-based) [slack\_bolt.adapter.socket\_mode.websockets](https://github.com/slackapi/bolt-python/tree/main/slack_bolt/adapter/socket_mode/websockets) ``` import osfrom slack_bolt import Appfrom slack_bolt.adapter.socket_mode import SocketModeHandler# Install the Slack app and get xoxb- token in advanceapp = App(token=os.environ["SLACK_BOT_TOKEN"])# Add middleware / listeners hereif __name__ == "__main__": # export SLACK_APP_TOKEN=xapp-*** # export SLACK_BOT_TOKEN=xoxb-*** handler = SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]) handler.start() ``` ## Using Async (asyncio) {#using-async-asyncio} To use the asyncio-based adapters such as aiohttp, your whole app needs to be compatible with asyncio's async/await programming model. `AsyncSocketModeHandler` is available for running `AsyncApp` and its async middleware and listeners. To learn how to use `AsyncApp`, checkout the [using Async](/tools/bolt-python/concepts/async) document and relevant [examples](https://github.com/slackapi/bolt-python/tree/main/examples). ``` from slack_bolt.app.async_app import AsyncApp# The default is the aiohttp based implementationfrom slack_bolt.adapter.socket_mode.async_handler import AsyncSocketModeHandlerapp = AsyncApp(token=os.environ["SLACK_BOT_TOKEN"])# Add middleware / listeners hereasync def main(): handler = AsyncSocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]) await handler.start_async()if __name__ == "__main__": import asyncio asyncio.run(main()) ``` --- Source: https://docs.slack.dev/tools/bolt-python/concepts/token-rotation # Token rotation Supported in Bolt for Python as of [v1.7.0](https://github.com/slackapi/bolt-python/releases/tag/v1.7.0), token rotation provides an extra layer of security for your access tokens and is defined by the [OAuth V2 RFC](https://datatracker.ietf.org/doc/html/rfc6749#section-10.4). Instead of an access token representing an existing installation of your Slack app indefinitely, with token rotation enabled, access tokens expire. A refresh token acts as a long-lived way to refresh your access tokens. Bolt for Python supports and will handle token rotation automatically so long as the [built-in OAuth](/tools/bolt-python/concepts/authenticating-oauth) functionality is used. For more information about token rotation, please see the [documentation](/authentication/using-token-rotation). --- Source: https://docs.slack.dev/tools/bolt-python/concepts/updating-pushing-views # Updating & pushing views Modals contain a stack of views. When you call [`views_open`](/reference/methods/views.open/), you add the root view to the modal. After the initial call, you can dynamically update a view by calling [`views_update`](/reference/methods/views.update/), or stack a new view on top of the root view by calling [`views_push`](/reference/methods/views.push/) ## The views_update method {#the-views_update-method} To update a view, you can use the built-in client to call `views_update` with the `view_id` that was generated when you opened the view, and a new `view` including the updated `blocks` list. If you're updating the view when a user interacts with an element inside of an existing view, the `view_id` will be available in the `body` of the request. ## The views_push method {#the-views_push-method} To push a new view onto the view stack, you can use the built-in client to call `views_push` with a valid `trigger_id` a new [view payload](/reference/interaction-payloads/view-interactions-payload/#view_submission). The arguments for `views_push` is the same as [opening modals](/tools/bolt-python/concepts/opening-modals). After you open a modal, you may only push two additional views onto the view stack. Learn more about updating and pushing views in our [API documentation](/surfaces/modals) Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. ``` # Listen for a button invocation with action_id `button_abc` (assume it's inside of a modal)@app.action("button_abc")def update_modal(ack, body, client): # Acknowledge the button request ack() # Call views_update with the built-in client client.views_update( # Pass the view_id view_id=body["view"]["id"], # String that represents view state to protect against race conditions hash=body["view"]["hash"], # View payload with updated blocks view={ "type": "modal", # View identifier "callback_id": "view_1", "title": {"type": "plain_text", "text": "Updated modal"}, "blocks": [ { "type": "section", "text": {"type": "plain_text", "text": "You updated the modal!"} }, { "type": "image", "image_url": "https://media.giphy.com/media/SVZGEcYt7brkFUyU90/giphy.gif", "alt_text": "Yay! The modal was updated" } ] } ) ``` --- Source: https://docs.slack.dev/tools/bolt-python/concepts/using-the-assistant-class # Using the Assistant class Some features within this guide require a paid plan If you don't have a paid workspace for development, you can join the [Developer Program](https://api.slack.com/developer-program) and provision a sandbox with access to all Slack features for free. The `Assistant` class can be used to handle the incoming events expected from a user interacting with an app in Slack that has the Agents & AI Apps feature enabled. A typical flow would look like: 1. [The user starts a thread](#handling-new-thread). The `Assistant` class handles the incoming [`assistant_thread_started`](/reference/events/assistant_thread_started) event. 2. [The thread context may change at any point](#handling-thread-context-changes). The `Assistant` class can handle any incoming [`assistant_thread_context_changed`](/reference/events/assistant_thread_context_changed) events. The class also provides a default `context` store to keep track of thread context changes as the user moves through Slack. 3. [The user responds](#handling-user-response). The `Assistant` class handles the incoming [`message.im`](/reference/events/message.im) event. ``` assistant = Assistant()# This listener is invoked when a human user opened an assistant thread@assistant.thread_starteddef start_assistant_thread( say: Say, get_thread_context: GetThreadContext, set_suggested_prompts: SetSuggestedPrompts, logger: logging.Logger,): try: ...# This listener is invoked when the human user sends a reply in the assistant thread@assistant.user_messagedef respond_in_assistant_thread( client: WebClient, context: BoltContext, get_thread_context: GetThreadContext, logger: logging.Logger, payload: dict, say: Say, set_status: SetStatus,): try: ...# Enable this assistant middleware in your Bolt appapp.use(assistant) ``` Consider the following You _could_ go it alone and [listen](/tools/bolt-python/concepts/event-listening) for the `assistant_thread_started`, `assistant_thread_context_changed`, and `message.im` events in order to implement the AI features in your app. That being said, using the `Assistant` class will streamline the process. And we already wrote this nice guide for you! While the `assistant_thread_started` and `assistant_thread_context_changed` events do provide Slack-client thread context information, the `message.im` event does not. Any subsequent user message events won't contain thread context data. For that reason, Bolt not only provides a way to store thread context — the `threadContextStore` property — but it also provides a `DefaultThreadContextStore` instance that is utilized by default. This implementation relies on storing and retrieving [message metadata](/messaging/message-metadata/) as the user interacts with the app. If you do provide your own `threadContextStore` property, it must feature `find` and `save` methods. Refer to the [reference docs](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. ## Configuring your app to support the Assistant class {#configuring-assistant-class} 1. Within [App Settings](https://api.slack.com/apps), enable the **Agents & AI Apps** feature. 2. Within the App Settings **OAuth & Permissions** page, add the following scopes: * [`assistant:write`](/reference/scopes/assistant.write) * [`chat:write`](/reference/scopes/chat.write) * [`im:history`](/reference/scopes/im.history) 3. Within the App Settings **Event Subscriptions** page, subscribe to the following events: * [`assistant_thread_started`](/reference/events/assistant_thread_started) * [`assistant_thread_context_changed`](/reference/events/assistant_thread_context_changed) * [`message.im`](/reference/events/message.im) ## Handling a new thread {#handling-new-thread} When the user opens a new thread with your AI-enabled app, the [`assistant_thread_started`](/reference/events/assistant_thread_started) event will be sent to your app. When a user opens an app thread while in a channel, the channel info is stored as the thread's `AssistantThreadContext` data. You can grab that info by using the `get_thread_context` utility, as subsequent user message event payloads won't include the channel info. ``` assistant = Assistant()@assistant.thread_starteddef start_assistant_thread( say: Say, get_thread_context: GetThreadContext, set_suggested_prompts: SetSuggestedPrompts, logger: logging.Logger,): try: say("How can I help you?") prompts: List[Dict[str, str]] = [ { "title": "Suggest names for my Slack app", "message": "Can you suggest a few names for my Slack app? The app helps my teammates better organize information and plan priorities and action items.", }, ] thread_context = get_thread_context() if thread_context is not None and thread_context.channel_id is not None: summarize_channel = { "title": "Summarize the referred channel", "message": "Can you generate a brief summary of the referred channel?", } prompts.append(summarize_channel) set_suggested_prompts(prompts=prompts) except Exception as e: logger.exception(f"Failed to handle an assistant_thread_started event: {e}", e) say(f":warning: Something went wrong! ({e})") ``` You can send more complex messages to the user — see [Sending Block Kit alongside messages](#block-kit-interactions) for more info. ## Handling thread context changes {#handling-thread-context-changes} When the user switches channels, the [`assistant_thread_context_changed`](/reference/events/assistant_thread_context_changed) event will be sent to your app. If you use the built-in `Assistant` middleware without any custom configuration, the updated context data is automatically saved as [message metadata](/messaging/message-metadata/) of the first reply from the app. As long as you use the built-in approach, you don't need to store the context data within a datastore. The downside of this default behavior is the overhead of additional calls to the Slack API. These calls include those to `conversations.history`, which are used to look up the stored message metadata that contains the thread context (via `get_thread_context`). To store context elsewhere, pass a custom `AssistantThreadContextStore` implementation to the `Assistant` constructor. We provide `FileAssistantThreadContextStore`, which is a reference implementation that uses the local file system. Since this reference implementation relies on local files, it's not advised for use in production. For production apps, we recommend creating a class that inherits `AssistantThreadContextStore`. ``` from slack_bolt import FileAssistantThreadContextStoreassistant = Assistant(thread_context_store=FileAssistantThreadContextStore()) ``` ## Handling the user response {#handling-user-response} When the user messages your app, the [`message.im`](/reference/events/message.im) event will be sent to your app. Messages sent to the app do not contain a [subtype](/reference/events/message#subtypes) and must be deduced based on their shape and any provided [message metadata](/messaging/message-metadata/). There are three utilities that are particularly useful in curating the user experience: * [`say`](https://docs.slack.dev/tools/bolt-python/reference/#slack_bolt.Say) * [`set_title`](https://docs.slack.dev/tools/bolt-python/reference/#slack_bolt.SetTitle) * [`set_status`](https://docs.slack.dev/tools/bolt-python/reference/#slack_bolt.SetStatus) Within the `set_status` utility, you can cycle through strings passed into a `loading_messages` list. ``` # This listener is invoked when the human user sends a reply in the assistant thread@assistant.user_messagedef respond_in_assistant_thread( client: WebClient, context: BoltContext, get_thread_context: GetThreadContext, logger: logging.Logger, payload: dict, say: Say, set_status: SetStatus,): try: channel_id = payload["channel"] team_id = payload["team"] thread_ts = payload["thread_ts"] user_id = payload["user"] user_message = payload["text"] set_status( status="thinking...", loading_messages=[ "Untangling the internet cables…", "Consulting the office goldfish…", "Convincing the AI to stop overthinking…", ], ) # Collect the conversation history with this user replies = client.conversations_replies( channel=context.channel_id, ts=context.thread_ts, oldest=context.thread_ts, limit=10, ) messages_in_thread: List[Dict[str, str]] = [] for message in replies["messages"]: role = "user" if message.get("bot_id") is None else "assistant" messages_in_thread.append({"role": role, "content": message["text"]}) returned_message = call_llm(messages_in_thread) # Post the result in the assistant thread say(text=returned_message) except Exception as e: logger.exception(f"Failed to respond to an inquiry: {e}") # Don't forget sending a message telling the error # Without this, the status 'is typing...' won't be cleared, therefore the end-user is unable to continue the chat say(f":warning: Sorry, something went wrong during processing your request (error: {e})")# Enable this assistant middleware in your Bolt appapp.use(assistant) ``` ## Sending Block Kit alongside messages {#block-kit-interactions} For advanced use cases, Block Kit buttons may be used instead of suggested prompts, as well as the sending of messages with structured [metadata](/messaging/message-metadata/) to trigger subsequent interactions with the user. For example, an app can display a button such as "Summarize the referring channel" in the initial reply. When the user clicks the button and submits detailed information (such as the number of messages, days to check, purpose of the summary, etc.), the app can handle that information and post a message that describes the request with structured metadata. By default, apps can't respond to their own bot messages (Bolt prevents infinite loops by default). However, if you pass `ignoring_self_assistant_message_events_enabled=False` to the `App` constructor and add a `bot_message` listener to your `Assistant` middleware, your app can continue processing the request as shown below: ``` app = App( token=os.environ["SLACK_BOT_TOKEN"], # This must be set to handle bot message events ignoring_self_assistant_message_events_enabled=False,)assistant = Assistant()@assistant.thread_starteddef start_assistant_thread(say: Say): say( text=":wave: Hi, how can I help you today?", blocks=[ { "type": "section", "text": {"type": "mrkdwn", "text": ":wave: Hi, how can I help you today?"}, }, { "type": "actions", "elements": [ # You can have multiple buttons here { "type": "button", "action_id": "assistant-generate-random-numbers", "text": {"type": "plain_text", "text": "Generate random numbers"}, "value": "clicked", }, ], }, ], )# This listener is invoked when the above button is clicked@app.action("assistant-generate-random-numbers")def configure_random_number_generation(ack: Ack, client: WebClient, body: dict): ack() client.views_open( trigger_id=body["trigger_id"], view={ "type": "modal", "callback_id": "configure_assistant_summarize_channel", "title": {"type": "plain_text", "text": "My Assistant"}, "submit": {"type": "plain_text", "text": "Submit"}, "close": {"type": "plain_text", "text": "Cancel"}, # Relay the assistant thread information to app.view listener "private_metadata": json.dumps( { "channel_id": body["channel"]["id"], "thread_ts": body["message"]["thread_ts"], } ), "blocks": [ { "type": "input", "block_id": "num", "label": {"type": "plain_text", "text": "# of outputs"}, # You can have this kind of predefined input from a user instead of parsing human text "element": { "type": "static_select", "action_id": "input", "placeholder": {"type": "plain_text", "text": "How many numbers do you need?"}, "options": [ {"text": {"type": "plain_text", "text": "5"}, "value": "5"}, {"text": {"type": "plain_text", "text": "10"}, "value": "10"}, {"text": {"type": "plain_text", "text": "20"}, "value": "20"}, ], "initial_option": {"text": {"type": "plain_text", "text": "5"}, "value": "5"}, }, } ], }, )# This listener is invoked when the above modal is submitted@app.view("configure_assistant_summarize_channel")def receive_random_number_generation_details(ack: Ack, client: WebClient, payload: dict): ack() num = payload["state"]["values"]["num"]["input"]["selected_option"]["value"] thread = json.loads(payload["private_metadata"]) # Post a bot message with structured input data # The following assistant.bot_message will continue processing # If you prefer processing this request within this listener, it also works! # If you don't need bot_message listener, no need to set ignoring_self_assistant_message_events_enabled=False client.chat_postMessage( channel=thread["channel_id"], thread_ts=thread["thread_ts"], text=f"OK, you need {num} numbers. I will generate it shortly!", metadata={ "event_type": "assistant-generate-random-numbers", "event_payload": {"num": int(num)}, }, )# This listener is invoked whenever your app's bot user posts a message@assistant.bot_messagedef respond_to_bot_messages(logger: logging.Logger, set_status: SetStatus, say: Say, payload: dict): try: if payload.get("metadata", {}).get("event_type") == "assistant-generate-random-numbers": # Handle the above random-number-generation request set_status("is generating an array of random numbers...") time.sleep(1) nums: Set[str] = set() num = payload["metadata"]["event_payload"]["num"] while len(nums) < num: nums.add(str(random.randint(1, 100))) say(f"Here you are: {', '.join(nums)}") else: # nothing to do for this bot message # If you want to add more patterns here, be careful not to cause infinite loop messaging pass except Exception as e: logger.exception(f"Failed to respond to an inquiry: {e}")... ``` See the [_Creating agents: adding and handling feedback_](/tools/bolt-python/concepts/adding-agent-features#adding-and-handling-feedback) section for adding feedback buttons with Block Kit. Want to see the functionality described throughout this guide in action? We've created a [App Agent Template](https://github.com/slack-samples/bolt-python-assistant-template) repo for you to build from. --- Source: https://docs.slack.dev/tools/bolt-python/concepts/view-submissions # Listening to views If a [view payload](/reference/interaction-payloads/view-interactions-payload/#view_submission) contains any input blocks, you must listen to `view_submission` requests to receive their values. To listen to `view_submission` requests, you can use the built-in `view()` method. `view()` requires a `callback_id` of type `str` or `re.Pattern`. You can access the value of the `input` blocks by accessing the `state` object. `state` contains a `values` object that uses the `block_id` and unique `action_id` to store the input values. * * * ##### Update views on submission {#update-views-on-submission} To update a view in response to a `view_submission` event, you may pass a `response_action` of type `update` with a newly composed `view` to display in your acknowledgement. ``` # Update the view on submission @app.view("view_1")def handle_submission(ack, body): # The build_new_view() method returns a modal view # To build a modal view, we recommend using Block Kit Builder: # https://app.slack.com/block-kit-builder/#%7B%22type%22:%22modal%22,%22callback_id%22:%22view_1%22,%22title%22:%7B%22type%22:%22plain_text%22,%22text%22:%22My%20App%22,%22emoji%22:true%7D,%22blocks%22:%5B%5D%7D ack(response_action="update", view=build_new_view(body)) ``` Similarly, there are options for [displaying errors](/surfaces/modals#displaying_errors) in response to view submissions. Read more about view submissions in our [API documentation](/surfaces/modals#interactions) * * * ##### Handling views on close {#handling-views-on-close} When listening for `view_closed` requests, you must pass `callback_id` and add a `notify_on_close` property to the view during creation. See below for an example of this: See the [API documentation](/surfaces/modals#interactions) for more information about `view_closed`. ``` client.views_open( trigger_id=body.get("trigger_id"), view={ "type": "modal", "callback_id": "modal-id", # Used when calling view_closed "title": { "type": "plain_text", "text": "Modal title" }, "blocks": [], "close": { "type": "plain_text", "text": "Cancel" }, "notify_on_close": True, # This attribute is required })# Handle a view_closed request@app.view_closed("modal-id")def handle_view_closed(ack, body, logger): ack() logger.info(body) ``` Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. ``` # Handle a view_submission request@app.view("view_1")def handle_submission(ack, body, client, view, logger): # Assume there's an input block with `input_c` as the block_id and `dreamy_input` hopes_and_dreams = view["state"]["values"]["input_c"]["dreamy_input"] user = body["user"]["id"] # Validate the inputs errors = {} if hopes_and_dreams is not None and len(hopes_and_dreams) <= 5: errors["input_c"] = "The value must be longer than 5 characters" if len(errors) > 0: ack(response_action="errors", errors=errors) return # Acknowledge the view_submission request and close the modal ack() # Do whatever you want with the input data - here we're saving it to a DB # then sending the user a verification of their submission # Message to send user msg = "" try: # Save to DB msg = f"Your submission of {hopes_and_dreams} was successful" except Exception as e: # Handle error msg = "There was an error with your submission" # Message the user try: client.chat_postMessage(channel=user, text=msg) except Exception as e: logger.exception(f"Failed to post a message {e}") ``` --- Source: https://docs.slack.dev/tools/bolt-python/concepts/web-api # Using the Web API You can call [any Web API method](/reference/methods) using the `WebClient` provided to your Bolt app as either `app.client` or `client` in middleware/listener arguments (given that your app has the appropriate scopes). When you call one the client's methods, it returns a `SlackResponse` which contains the response from Slack. The token used to initialize Bolt can be found in the `context` object, which is required to call most Web API methods. Refer to [the module document](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) to learn the available listener arguments. ## Example {#example} ``` @app.message("wake me up")def say_hello(client, message): # Unix Epoch time for September 30, 2020 11:59:59 PM when_september_ends = 1601510399 channel_id = message["channel"] client.chat_scheduleMessage( channel=channel_id, post_at=when_september_ends, text="Summer has come and passed" ) ``` --- Source: https://docs.slack.dev/tools/bolt-python/creating-an-app # Creating an app with Bolt for Python This guide is meant to walk you through getting up and running with a Slack app using Bolt for Python. Along the way, we’ll create a new Slack app, set up your local environment, and develop an app that listens and responds to messages from a Slack workspace. When you're finished, you'll have created the [Getting Started app](https://github.com/slackapi/bolt-python/tree/main/examples/getting_started) to run, modify, and make your own. ⚡️ * * * ### Create a new app {#create-an-app} First thing's first: before you start developing with Bolt, you'll want to [create a Slack app](https://api.slack.com/apps/new). A place to test and learn We recommend using a workspace where you won't disrupt real work getting done — [you can create a new one for free](https://slack.com/get-started#create). After you fill out an app name (_you can change it later_) and pick a workspace to install it to, hit the `Create App` button and you'll land on your app's **Basic Information** page. This page contains an overview of your app in addition to important credentials you'll need later. ![Basic Information page](/assets/images/basic-information-page-e7d531fe4721830376d61a91de5d933e.png "Basic Information page") Look around, add an app icon and description, and then let's start configuring your app 🔩 * * * ### Tokens and installing apps {#tokens-and-installing-apps} Slack apps use [OAuth to manage access to Slack's APIs](/authentication/installing-with-oauth). When an app is installed, you'll receive a token that the app can use to call API methods. There are three main token types available to a Slack app: user (`xoxp`), bot (`xoxb`), and app-level (`xapp`) tokens. * [User tokens](/authentication/tokens#user) allow you to call API methods on behalf of users after they install or authenticate the app. There may be several user tokens for a single workspace. * [Bot tokens](/authentication/tokens#bot) are associated with bot users, and are only granted once in a workspace where someone installs the app. The bot token your app uses will be the same no matter which user performed the installation. Bot tokens are the token type that _most_ apps use. * [App-level tokens](/authentication/tokens#app-level) represent your app across organizations, including installations by all individual users on all workspaces in a given organization and are commonly used for creating WebSocket connections to your app. We're going to use bot and app-level tokens for this guide. 1. Navigate to **OAuth & Permissions** on the left sidebar and scroll down to the **Bot Token Scopes** section. Click **Add an OAuth Scope**. 2. For now, we'll just add one scope: [`chat:write`](/reference/scopes/chat.write). This grants your app the permission to post messages in channels it's a member of. 3. Scroll up to the top of the **OAuth & Permissions** page and click **Install App to Workspace**. You'll be led through Slack's OAuth UI, where you should allow your app to be installed to your development workspace. 4. Once you authorize the installation, you'll land on the **OAuth & Permissions** page and see a **Bot User OAuth Access Token**. ![OAuth Tokens](/assets/images/bot-token-3d6c761238c7a66557fd08d00a2a1b0c.png "Bot OAuth Token") 5. Head over to **Basic Information** and scroll down under the App Token section and click **Generate Token and Scopes** to generate an app-level token. Add the `connections:write` scope to this token and save the generated `xapp` token. 6. Navigate to **Socket Mode** on the left side menu and toggle to enable. Not sharing is sometimes caring Treat your tokens like passwords and [keep them safe](/concepts/security). Your app uses tokens to post and retrieve information from Slack workspaces. * * * ### Setting up your project {#setting-up-your-project} With the initial configuration handled, it's time to set up a new Bolt project. This is where you'll write the code that handles the logic for your app. If you don’t already have a project, let’s create a new one. Create an empty directory: ``` $ mkdir first-bolt-app$ cd first-bolt-app ``` Next, we recommend using a [Python virtual environment](https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/#creating-a-virtual-environment) to manage your project's dependencies. This is a great way to prevent conflicts with your system's Python packages. Let's create and activate a new virtual environment with [Python 3.7 or later](https://www.python.org/downloads/): ``` $ python3 -m venv .venv$ source .venv/bin/activate$ pip install -r requirements.txt ``` We can confirm that the virtual environment is active by checking that the path to `python3` is _inside_ your project ([a similar command is available on Windows](https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/#activating-a-virtual-environment)): ``` $ which python3# Output: /path/to/first-bolt-app/.venv/bin/python3 ``` Before we install the Bolt for Python package to your new project, let's save the **bot token** and **app-level token** that were generated when you configured your app. 1. **Copy your bot (xoxb) token from the OAuth & Permissions page** and store it in a new environment variable. The following example works on Linux and macOS; but [similar commands are available on Windows](https://superuser.com/questions/212150/how-to-set-env-variable-in-windows-cmd-line/212153#212153). ``` $ export SLACK_BOT_TOKEN=xoxb- ``` 2. **Copy your app-level (xapp) token from the Basic Information page** and then store it in a new environment variable. ``` $ export SLACK_APP_TOKEN= ``` Keep it secret. Keep it safe. Remember to keep your tokens secure. At a minimum, you should avoid checking them into public version control, and access them via environment variables as we've done above. Check out the API documentation for more on [best practices for app security](/concepts/security). Now, let's create your app. Install the `slack_bolt` Python package to your virtual environment using the following command: ``` $ pip install slack_bolt ``` Create a new file called `app.py` in this directory and add the following code: ``` import osfrom slack_bolt import Appfrom slack_bolt.adapter.socket_mode import SocketModeHandler# Initializes your app with your bot token and socket mode handlerapp = App(token=os.environ.get("SLACK_BOT_TOKEN"))# Start your appif __name__ == "__main__": SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]).start() ``` Your tokens are enough to create your first Bolt app. Save your `app.py` file then on the command line run the following: ``` $ python3 app.py ``` Your app should let you know that it's up and running. 🎉 * * * ### Setting up events {#setting-up-events} Your app behaves similarly to people on your team — it can post messages, add emoji reactions, and listen and respond to events. To listen for events happening in a Slack workspace (like when a message is posted or when a reaction is posted to a message) you'll use the [Events API to subscribe to event types](/apis/events-api/). For those just starting, we recommend using [Socket Mode](/apis/events-api/using-socket-mode). Socket Mode allows your app to use the Events API and interactive features without exposing a public HTTP Request URL. This can be helpful during development, or if you're receiving requests from behind a firewall. That being said, you're welcome to set up an app with a public HTTP Request URL. HTTP is more useful for apps being deployed to hosting environments to respond within a large corporate Slack workspaces/organization, or apps intended for distribution via the Slack Marketplace. We've provided instructions for both ways in this guide. * Socket Mode * HTTP 1. Head to your app's configuration page (click on the app [from your app settings page](https://api.slack.com/apps)). Navigate to **Socket Mode** on the left side menu and toggle to enable. 2. Go to **Basic Information** and scroll down under the App-Level Tokens section and click **Generate Token and Scopes** to generate an app-level token. Add the `connections:write` scope to this token and save the generated `xapp` token, we'll use that in just a moment. 3. Finally, it's time to tell Slack what events we'd like to listen for. Under **Event Subscriptions**, toggle the switch labeled **Enable Events**. When an event occurs, Slack will send your app some information about the event, like the user that triggered it and the channel it occurred in. Your app will process the details and can respond accordingly. 1. Go back to your app configuration page (click on the app [from your app management page](https://api.slack.com/apps)). Click **Event Subscriptions** on the left sidebar. Toggle the switch labeled **Enable Events**. 2. Add your Request URL. Slack will send HTTP POST requests corresponding to events to this [Request URL](/apis/events-api/#subscribing) endpoint. Bolt uses the `/slack/events` path to listen to all incoming requests (whether shortcuts, events, or interactivity payloads). When configuring your Request URL within your app configuration, you'll append `/slack/events`, e.g. `https:///slack/events`. 💡 As long as your Bolt app is still running, your URL should become verified. Using proxy services For local development, you can use a proxy service like ngrok to create a public URL and tunnel requests to your development environment. Refer to [ngrok's getting started guide](https://ngrok.com/docs#getting-started-expose) on how to create this tunnel. And when you get to hosting your app, we've collected some of the most common hosting providers Slack developers use to host their apps [on our API site](/app-management/hosting-slack-apps). Navigate to **Event Subscriptions** on the left sidebar and toggle to enable. Under **Subscribe to Bot Events**, you can add events for your bot to respond to. There are four events related to messages: * [`message.channels`](/reference/events/message.channels) listens for messages in public channels that your app is added to. * [`message.groups`](/reference/events/message.groups) listens for messages in 🔒 private channels that your app is added to. * [`message.im`](/reference/events/message.im) listens for messages in your app's DMs with users. * [`message.mpim`](/reference/events/message.mpim) listens for messages in multi-person DMs that your app is added to. If you want your bot to listen to messages from everywhere it is added to, choose all four message events. After you’ve selected the events you want your bot to listen to, click the green **Save Changes** button. * * * ### Listening and responding to a message {#listening-and-responding-to-a-message} Your app is now ready for some logic. Let's start by using the `message()` method to attach a listener for messages. The following example listens and responds to all messages in channels/DMs where your app has been added that contain the word "hello": * Socket Mode * HTTP ``` import osfrom slack_bolt import Appfrom slack_bolt.adapter.socket_mode import SocketModeHandler# Initializes your app with your bot token and socket mode handlerapp = App(token=os.environ.get("SLACK_BOT_TOKEN"))# Listens to incoming messages that contain "hello"# To learn available listener arguments,# visit https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html@app.message("hello")def message_hello(message, say): # say() sends a message to the channel where the event was triggered say(f"Hey there <@{message['user']}>!")# Start your appif __name__ == "__main__": SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]).start() ``` ``` import osfrom slack_bolt import App# Initializes your app with your bot token and signing secretapp = App( token=os.environ.get("SLACK_BOT_TOKEN"), signing_secret=os.environ.get("SLACK_SIGNING_SECRET"))# Listens to incoming messages that contain "hello"# To learn available listener arguments,# visit https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html@app.message("hello")def message_hello(message, say): # say() sends a message to the channel where the event was triggered say(f"Hey there <@{message['user']}>!")# Start your appif __name__ == "__main__": app.start(port=int(os.environ.get("PORT", 3000))) ``` If you restart your app, so long as your bot user has been added to the channel or DM conversation, when you send any message that contains "hello", it will respond. This is a basic example, but it gives you a place to start customizing your app based on your own goals. Let's try something a little more interactive by sending a button rather than plain text. * * * ### Sending and responding to actions {#sending-and-responding-to-actions} To use features like buttons, select menus, datepickers, modals, and shortcuts, you’ll need to enable interactivity. Head over to **Interactivity & Shortcuts** in your app configuration. * Socket Mode * HTTP With Socket Mode on, basic interactivity is enabled by default, so no further action is needed. Similar to events, you'll need to specify a URL for Slack to send the action (such as _user clicked a button_). Back on your app configuration page, click on **Interactivity & Shortcuts** on the left side. You'll see that there's another **Request URL** box. By default, Bolt is configured to use the same endpoint for interactive components that it uses for events, so use the same request URL as above (for example, `https://8e8ec2d7.ngrok.io/slack/events`). Press the **Save Changes** button in the lower right hand corner, and that's it. Your app is set up to handle interactivity! When interactivity is enabled, interactions with shortcuts, modals, or interactive components (such as buttons, select menus, and datepickers) will be sent to your app as events. Now, let's go back to your app's code and add logic to handle those events: * First, we'll send a message that contains an interactive component (in this case a button). * Next, we'll listen for the action of a user clicking the button before responding. Below, the code from the last section is modified to send a message containing a button rather than just a string: * Socket Mode * HTTP ``` import osfrom slack_bolt import Appfrom slack_bolt.adapter.socket_mode import SocketModeHandler# Initializes your app with your bot token and socket mode handlerapp = App( token=os.environ.get("SLACK_BOT_TOKEN"), # signing_secret=os.environ.get("SLACK_SIGNING_SECRET") # not required for socket mode)# Listens to incoming messages that contain "hello"@app.message("hello")def message_hello(message, say): # say() sends a message to the channel where the event was triggered say( blocks=[ { "type": "section", "text": {"type": "mrkdwn", "text": f"Hey there <@{message['user']}>!"}, "accessory": { "type": "button", "text": {"type": "plain_text", "text": "Click Me"}, "action_id": "button_click" } } ], text=f"Hey there <@{message['user']}>!" )# Start your appif __name__ == "__main__": SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]).start() ``` ``` import osfrom slack_bolt import App# Initializes your app with your bot token and signing secretapp = App( token=os.environ.get("SLACK_BOT_TOKEN"), signing_secret=os.environ.get("SLACK_SIGNING_SECRET"))# Listens to incoming messages that contain "hello"@app.message("hello")def message_hello(message, say): # say() sends a message to the channel where the event was triggered say( blocks=[ { "type": "section", "text": {"type": "mrkdwn", "text": f"Hey there <@{message['user']}>!"}, "accessory": { "type": "button", "text": {"type": "plain_text", "text": "Click Me"}, "action_id": "button_click" } } ], text=f"Hey there <@{message['user']}>!" )# Start your appif __name__ == "__main__": app.start(port=int(os.environ.get("PORT", 3000))) ``` The value inside of `say()` is now an object that contains an array of `blocks`. Blocks are the building components of a Slack message and can range from text to images to datepickers. In this case, your app will respond with a section block that includes a button as an accessory. Since we're using `blocks`, the `text` is a fallback for notifications and accessibility. You'll notice in the button `accessory` object, there is an `action_id`. This will act as a unique identifier for the button so your app can specify which action it wants to respond to. Using Block Kit Builder The [Block Kit Builder](https://app.slack.com/block-kit-builder) is an simple way to prototype your interactive messages. The builder lets you (or anyone on your team) mock up messages and generates the corresponding JSON that you can paste directly in your app. Now, if you restart your app and say "hello" in a channel your app is in, you'll see a message with a button. But if you click the button, nothing happens (_yet!_). Let's add a handler to send a follow-up message when someone clicks the button: * Socket Mode * HTTP ``` import osfrom slack_bolt import Appfrom slack_bolt.adapter.socket_mode import SocketModeHandler# Initializes your app with your bot token and socket mode handlerapp = App(token=os.environ.get("SLACK_BOT_TOKEN"))# Listens to incoming messages that contain "hello"@app.message("hello")def message_hello(message, say): # say() sends a message to the channel where the event was triggered say( blocks=[ { "type": "section", "text": {"type": "mrkdwn", "text": f"Hey there <@{message['user']}>!"}, "accessory": { "type": "button", "text": {"type": "plain_text", "text": "Click Me"}, "action_id": "button_click" } } ], text=f"Hey there <@{message['user']}>!" )@app.action("button_click")def action_button_click(body, ack, say): # Acknowledge the action ack() say(f"<@{body['user']['id']}> clicked the button")# Start your appif __name__ == "__main__": SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]).start() ``` ``` import osfrom slack_bolt import App# Initializes your app with your bot token and signing secretapp = App( token=os.environ.get("SLACK_BOT_TOKEN"), signing_secret=os.environ.get("SLACK_SIGNING_SECRET"))# Listens to incoming messages that contain "hello"@app.message("hello")def message_hello(message, say): # say() sends a message to the channel where the event was triggered say( blocks=[ { "type": "section", "text": {"type": "mrkdwn", "text": f"Hey there <@{message['user']}>!"}, "accessory": { "type": "button", "text": {"type": "plain_text", "text": "Click Me"}, "action_id": "button_click" } } ], text=f"Hey there <@{message['user']}>!" )@app.action("button_click")def action_button_click(body, ack, say): # Acknowledge the action ack() say(f"<@{body['user']['id']}> clicked the button")# Start your appif __name__ == "__main__": app.start(port=int(os.environ.get("PORT", 3000))) ``` You can see that we used `app.action()` to listen for the `action_id` that we named `button_click`. If you restart your app and click the button, you'll see a new message from your app that says you clicked the button. * * * ### Next steps {#next-steps} You just built your first [Bolt for Python app](https://github.com/slackapi/bolt-python/tree/main/examples/getting_started)! 🎉 Now that you have a basic app up and running, you can start exploring how to make your Bolt app stand out. Here are some ideas about what to explore next: * Read through the concepts pages to learn about the different methods and features your Bolt app has access to. * Explore the different events your bot can listen to with the [`app.event()`](/tools/bolt-python/concepts/event-listening) method. View the full events reference docs [here](/reference/events). * Bolt allows you to [call Web API methods](/tools/bolt-python/concepts/web-api) with the client attached to your app. There are over 200 methods; view them [here](/reference/methods). * Learn more about the different token types in the [tokens guide](/authentication/tokens). Your app may need different tokens depending on the actions you want it to perform. --- Source: https://docs.slack.dev/tools/bolt-python/experiments # Experiments Bolt for Python occasionally includes experimental features still under active development. These features may be fleeting, may not be perfectly polished, and should be thought of as available for use "at your own risk." Experimental features are categorized as `semver:patch` until the experimental status is removed. We love feedback from our community, so we encourage you to explore and interact with the [GitHub repo](https://github.com/slackapi/bolt-python). Contributions, bug reports, and any feedback are all helpful; let us nurture the Slack CLI together to help make building Slack apps more pleasant for everyone. ## Available experiments {#available-experiments} There are currently no active experiments. We're steadily staying stable. --- Source: https://docs.slack.dev/tools/bolt-python/getting-started # Quickstart guide with Bolt for Python This quickstart guide aims to help you get a Slack app using Bolt for Python up and running as soon as possible! When complete, you'll have a local environment configured with a customized [app](https://github.com/slack-samples/bolt-python-getting-started-app) running to modify and make your own. Reference for readers In search of the complete guide to building an app from scratch? Check out the [building an app](/tools/bolt-python/creating-an-app) guide. #### Prerequisites {#prerequisites} A few tools are needed for the following steps. We recommend using the [**Slack CLI**](/tools/slack-cli/) for the smoothest experience, but other options remain available. You can also begin by installing git and downloading [Python 3.7 or later](https://www.python.org/downloads/), or the latest stable version of Python. Refer to [Python's setup and building guide](https://devguide.python.org/getting-started/setup-building/) for more details. Install the latest version of the Slack CLI to get started: * [Slack CLI for macOS & Linux](/tools/slack-cli/guides/installing-the-slack-cli-for-mac-and-linux) * [Slack CLI for Windows](/tools/slack-cli/guides/installing-the-slack-cli-for-windows) Then confirm a successful installation with the following command: ``` $ slack version ``` An authenticated login is also required if this hasn't been done before: ``` $ slack login ``` A place to belong A workspace where development can happen is also needed. We recommend using [developer sandboxes](/tools/developer-sandboxes) to avoid disruptions where real work gets done. ## Creating a project {#creating-a-project} With the toolchain configured, it's time to set up a new Bolt project. This contains the code that handles logic for your app. If you don’t already have a project, let’s create a new one! * Slack CLI * Terminal A starter template can be used to start with project scaffolding: ``` $ slack create first-bolt-app --template slack-samples/bolt-python-getting-started-app$ cd first-bolt-app ``` After a project is created you'll have a `requirements.txt` file for app dependencies and a `.slack` directory for Slack CLI configuration. A few other files exist too, but we'll visit these later. A starter template can be cloned to start with project scaffolding: ``` $ git clone https://github.com/slack-samples/bolt-python-getting-started-app first-bolt-app$ cd first-bolt-app ``` Outlines of a project are taking shape, so we can move on to running the app! We recommend using a [Python virtual environment](https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/#creating-a-virtual-environment) to manage your project's dependencies. This is a great way to prevent conflicts with your system's Python packages. Let's create and activate a new virtual environment with [Python 3.7 or later](https://www.python.org/downloads/): ``` $ python3 -m venv .venv$ source .venv/bin/activate$ pip install -r requirements.txt ``` Confirm the virtual environment is active by checking that the path to `python3` is _inside_ your project ([a similar command is available on Windows](https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/#activating-a-virtual-environment)): ``` $ which python3# Output: /path/to/first-bolt-app/.venv/bin/python3 ``` ## Running the app {#running-the-app} Before you can start developing with Bolt, you will want a running Slack app. * Slack CLI * Browser The getting started app template contains a `manifest.json` file with details about an app that we will use to get started. Use the following command and select "Create a new app" to install the app to the team of choice: ``` $ slack run...⚡️ Bolt app is running! ``` With the app running, you can test it out with the following steps in Slack: 1. Open a direct message with your app or invite the bot `@first-bolt-app (local)` to a public channel. 2. Send "hello" to the current conversation and wait for a response. 3. Click the attached button labelled "Click Me" to post another reply. After confirming the app responds, celebrate, then interrupt the process by pressing `CTRL+C` in the terminal to stop your app from running. Navigate to your list of apps and [create a new Slack app](https://api.slack.com/apps/new) using the "from a manifest" option: 1. Select the workspace to develop your app in. 2. Copy and paste the `manifest.json` file contents to create your app. 3. Confirm the app features and click "Create". You'll then land on your app's **Basic Information** page, which is an overview of your app and which contains important credentials: ![Basic Information page](/assets/images/basic-information-page-e7d531fe4721830376d61a91de5d933e.png "Basic Information page") To listen for events happening in Slack (such as a new posted message) without opening a port or exposing an endpoint, we will use [Socket Mode](/tools/bolt-python/concepts/socket-mode). This connection requires a specific app token: 1. On the **Basic Information** page, scroll to the **App-Level Tokens** section and click **Generate Token and Scopes**. 2. Name the token "Development" or something similar and add the `connections:write` scope, then click **Generate**. 3. Save the generated `xapp` token as an environment variable within your project: ``` $ export SLACK_APP_TOKEN= ``` The above command works on Linux and macOS but [similar commands are available on Windows](https://superuser.com/questions/212150/how-to-set-env-variable-in-windows-cmd-line/212153#212153). Keep it secret. Keep it safe. Treat your tokens like a password and [keep it safe](/concepts/security). Your app uses these to retrieve and send information to Slack. A bot token is also needed to interact with the Web API methods as your app's bot user. We can gather this as follows: 1. Navigate to the **OAuth & Permissions** on the left sidebar and install your app to your workspace to generate a token. 2. After authorizing the installation, you'll return to the **OAuth & Permissions** page and find a **Bot User OAuth Token**: ![OAuth Tokens](/assets/images/bot-token-3d6c761238c7a66557fd08d00a2a1b0c.png "Bot OAuth Token") 3. Copy the bot token beginning with `xoxb` from the **OAuth & Permissions page** and then store it in a new environment variable: ``` $ export SLACK_BOT_TOKEN=xoxb- ``` After saving tokens for the app you created, it is time to run it: ``` $ python3 app.py...⚡️ Bolt app is running! ``` With the app running, you can test it out with the following steps in Slack: 1. Open a direct message with your app or invite the bot `@BoltApp` to a public channel. 2. Send "hello" to the current conversation and wait for a response. 3. Click the attached button labelled "Click Me" to post another reply. After confirming the app responds, celebrate, then interrupt the process by pressing `CTRL+C` in the terminal to stop your app from running. ## Updating the app {#updating-the-app} At this point, you've successfully run the getting started Bolt for Python [app](https://github.com/slack-samples/bolt-python-getting-started-app)! The defaults included leave opportunities abound, so to personalize this app let's now edit the code to respond with a kind farewell. #### Responding to a farewell {#responding-to-a-farewell} Chat is a common thing apps do and responding to various types of messages can make conversations more interesting. Using an editor of choice, open the `app.py` file and add the following import to the top of the file, and message listener after the "hello" handler: ``` import random@app.message("goodbye")def message_goodbye(say): responses = ["Adios", "Au revoir", "Farewell"] parting = random.choice(responses) say(f"{parting}!") ``` Once the file is updated, save the changes and then we'll make sure those changes are being used. * Slack CLI * Terminal Run the following command and select the app created earlier to start, or restart, your app with the latest changes: ``` $ slack run...⚡️ Bolt app is running! ``` After finding the above output appears, open Slack to perform these steps: 1. Return to the direct message or public channel with your bot. 2. Send "goodbye" to the conversation. 3. Receive a parting response from before and repeat "goodbye" to find another one. Your app can be stopped again by pressing `CTRL+C` in the terminal to end these chats. Run the following command to start, or restart, your app with the latest changes: ``` $ python3 app.py...⚡️ Bolt app is running! ``` After finding the above output appears, open Slack to perform these steps: 1. Return to the direct message or public channel with your bot. 2. Send "goodbye" to the conversation. 3. Receive a parting response from before and repeat "goodbye" to find another one. Your app can be stopped again by pressing `CTRL+C` in the terminal to end these chats. #### Customizing app settings {#customizing-app-settings} The created app will have some placeholder values and a small set of [scopes](/reference/scopes) to start, but we recommend exploring the customizations possible on app settings. * Slack CLI * Browser Open app settings for your app with the following command: ``` $ slack app settings ``` This will open the following page in a web browser: ![Basic Information page](/assets/images/basic-information-page-e7d531fe4721830376d61a91de5d933e.png "Basic Information page") Browse to [https://api.slack.com/apps](https://api.slack.com/apps) and select your app "Getting Started Bolt App" from the list. This will open the following page: ![Basic Information page](/assets/images/basic-information-page-e7d531fe4721830376d61a91de5d933e.png "Basic Information page") On these pages you're free to make changes such as updating your app icon, configuring app features, and perhaps even distributing your app! ## Next steps {#next-steps} You can now continue customizing your app with various features to make it right for whatever job's at hand. Here are some ideas about what to explore next: * Follow along with the steps that went into making this app on the [creating an app](/tools/bolt-python/creating-an-app) guide for an educational overview. * Check out the [Agent quickstart](/ai/agent-quickstart) to get up and running with an agent. * Browse our [curated catalog of samples](/samples) for more apps to use as a starting point for development. --- Source: https://docs.slack.dev/tools/bolt-python/legacy/steps-from-apps # Steps from apps Steps from Apps is a deprecated feature. Steps from Apps are different than, and not interchangeable with, Slack automation workflows. We encourage those who are currently publishing steps from apps to consider the new [Slack automation features](/workflows/), such as [custom steps for Bolt](/workflows/workflow-steps). Please [read the Slack API changelog entry](/changelog/2023-08-workflow-steps-from-apps-step-back) for more information. Steps from apps allow your app to create and process steps that users can add using [Workflow Builder](/workflows/workflow-builder). Steps from apps are made up of three distinct user events: * Adding or editing the step in a Workflow * Saving or updating the step's configuration * The end user's execution of the step All three events must be handled for a step from app to function. Read more about steps from apps in the [API documentation](/workflows/workflow-steps). ## Creating steps from apps {#creating-steps-from-apps} To create a step from app, Bolt provides the `WorkflowStep` class. When instantiating a new `WorkflowStep`, pass in the step's `callback_id` and a configuration object. The configuration object contains three keys: `edit`, `save`, and `execute`. Each of these keys must be a single callback or a list of callbacks. All callbacks have access to a `step` object that contains information about the step from app event. After instantiating a `WorkflowStep`, you can pass it into `app.step()`. Behind the scenes, your app will listen and respond to the step’s events using the callbacks provided in the configuration object. Alternatively, steps from apps can also be created using the `WorkflowStepBuilder` class alongside a decorator pattern. For more information, including an example of this approach, [refer to the documentation](https://docs.slack.dev/tools/bolt-python/reference/workflows/step/step.html#slack_bolt.workflows.step.step.WorkflowStepBuilder). Refer to the module documents ([common](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) / [step-specific](https://docs.slack.dev/tools/bolt-python/reference/workflows/step/utilities/index.html)) to learn the available arguments. ``` import osfrom slack_bolt import Appfrom slack_bolt.workflows.step import WorkflowStep# Initiate the Bolt app as you normally wouldapp = App( token=os.environ.get("SLACK_BOT_TOKEN"), signing_secret=os.environ.get("SLACK_SIGNING_SECRET"))def edit(ack, step, configure): passdef save(ack, view, update): passdef execute(step, complete, fail): pass# Create a new WorkflowStep instancews = WorkflowStep( callback_id="add_task", edit=edit, save=save, execute=execute,)# Pass Step to set up listenersapp.step(ws) ``` ## Adding or editing steps from apps {#adding-or-editing-steps-from-apps} When a builder adds (or later edits) your step in their workflow, your app will receive a `workflow_step_edit` event. The `edit` callback in your `WorkflowStep` configuration will be run when this event is received. Whether a builder is adding or editing a step, you need to send them a step from app configuration modal. This modal is where step-specific settings are chosen, and it has more restrictions than typical modals—most notably, it cannot include `title`, `submit`, or `close` properties. By default, the configuration modal's `callback_id` will be the same as the step from app. Within the `edit` callback, the `configure()` utility can be used to easily open your step's configuration modal by passing in the view's blocks with the corresponding `blocks` argument. To disable saving the configuration before certain conditions are met, you can also pass in `submit_disabled` with a value of `True`. Refer to the module documents ([common](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) / [step-specific](https://docs.slack.dev/tools/bolt-python/reference/workflows/step/utilities/index.html)) to learn the available arguments. ``` def edit(ack, step, configure): ack() blocks = [ { "type": "input", "block_id": "task_name_input", "element": { "type": "plain_text_input", "action_id": "name", "placeholder": {"type": "plain_text", "text": "Add a task name"}, }, "label": {"type": "plain_text", "text": "Task name"}, }, { "type": "input", "block_id": "task_description_input", "element": { "type": "plain_text_input", "action_id": "description", "placeholder": {"type": "plain_text", "text": "Add a task description"}, }, "label": {"type": "plain_text", "text": "Task description"}, }, ] configure(blocks=blocks)ws = WorkflowStep( callback_id="add_task", edit=edit, save=save, execute=execute,)app.step(ws) ``` ## Saving step configurations {#saving-step-configurations} After the configuration modal is opened, your app will listen for the `view_submission` event. The `save` callback in your `WorkflowStep` configuration will be run when this event is received. Within the `save` callback, the `update()` method can be used to save the builder's step configuration by passing in the following arguments: * `inputs` is a dictionary representing the data your app expects to receive from the user upon step execution. * `outputs` is a list of objects containing data that your app will provide upon the step's completion. Outputs can then be used in subsequent steps of the workflow. * `step_name` overrides the default Step name * `step_image_url` overrides the default Step image Refer to the module documents ([common](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) / [step-specific](https://docs.slack.dev/tools/bolt-python/reference/workflows/step/utilities/index.html)) to learn the available arguments. ``` def save(ack, view, update): ack() values = view["state"]["values"] task_name = values["task_name_input"]["name"] task_description = values["task_description_input"]["description"] inputs = { "task_name": {"value": task_name["value"]}, "task_description": {"value": task_description["value"]} } outputs = [ { "type": "text", "name": "task_name", "label": "Task name", }, { "type": "text", "name": "task_description", "label": "Task description", } ] update(inputs=inputs, outputs=outputs)ws = WorkflowStep( callback_id="add_task", edit=edit, save=save, execute=execute,)app.step(ws) ``` ## Executing steps from apps {#executing-steps-from-apps} When your step from app is executed by an end user, your app will receive a `workflow_step_execute` event. The `execute` callback in your `WorkflowStep` configuration will be run when this event is received. Using the `inputs` from the `save` callback, this is where you can make third-party API calls, save information to a database, update the user's Home tab, or decide the outputs that will be available to subsequent steps from apps by mapping values to the `outputs` object. Within the `execute` callback, your app must either call `complete()` to indicate that the step's execution was successful, or `fail()` to indicate that the step's execution failed. Refer to the module documents ([common](https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection/args.html) / [step-specific](https://docs.slack.dev/tools/bolt-python/reference/workflows/step/utilities/index.html)) to learn the available arguments. ``` def execute(step, complete, fail): inputs = step["inputs"] # if everything was successful outputs = { "task_name": inputs["task_name"]["value"], "task_description": inputs["task_description"]["value"], } complete(outputs=outputs) # if something went wrong error = {"message": "Just testing step failure!"} fail(error=error)ws = WorkflowStep( callback_id="add_task", edit=edit, save=save, execute=execute,)app.step(ws) ``` --- Source: https://docs.slack.dev/tools/bolt-python/reference # Package slack_bolt A Python framework to build Slack apps in a flash with the latest platform features.Read the [getting started guide](https://docs.slack.dev/tools/bolt-python/creating-an-app) and look at our [code examples](https://github.com/slackapi/bolt-python/tree/main/examples) to learn how to build apps using Bolt. * Website: [https://docs.slack.dev/tools/bolt-python/](https://docs.slack.dev/tools/bolt-python/) * GitHub repository: [https://github.com/slackapi/bolt-python](https://github.com/slackapi/bolt-python) * The class representing a Bolt app: `[slack_bolt.app.app](app/app.html "slack_bolt.app.app")` ## Sub-modules `[slack_bolt.adapter](adapter/index.html "slack_bolt.adapter")` Adapter modules for running Bolt apps along with Web frameworks or Socket Mode. `[slack_bolt.app](app/index.html "slack_bolt.app")` Application interface in Bolt … `[slack_bolt.async_app](async_app.html "slack_bolt.async_app")` Module for creating asyncio based apps … `[slack_bolt.authorization](authorization/index.html "slack_bolt.authorization")` Authorization is the process of determining which Slack credentials should be available while processing an incoming Slack event … `[slack_bolt.context](context/index.html "slack_bolt.context")` All listeners have access to a context dictionary, which can be used to enrich events with additional information. Bolt automatically attaches … `[slack_bolt.error](error/index.html "slack_bolt.error")` Bolt specific error types. `[slack_bolt.kwargs_injection](kwargs_injection/index.html "slack_bolt.kwargs_injection")` For middleware/listener arguments, Bolt does flexible data injection in accordance with their names … `[slack_bolt.lazy_listener](lazy_listener/index.html "slack_bolt.lazy_listener")` Lazy listener runner is a beta feature for the apps running on Function-as-a-Service platforms … `[slack_bolt.listener](listener/index.html "slack_bolt.listener")` Listeners process an incoming request from Slack if the request's type or data structure matches the predefined conditions of the listener. Typically, … `[slack_bolt.listener_matcher](listener_matcher/index.html "slack_bolt.listener_matcher")` A listener matcher is a simplified version of listener middleware. A listener matcher function returns bool value instead of `next()` method … `[slack_bolt.logger](logger/index.html "slack_bolt.logger")` Bolt for Python relies on the standard `logging` module. `[slack_bolt.middleware](middleware/index.html "slack_bolt.middleware")` A middleware processes request data and calls `next()` method if the execution chain should continue running the following middleware … `[slack_bolt.oauth](oauth/index.html "slack_bolt.oauth")` Slack OAuth flow support for building an app that is installable in any workspaces … `[slack_bolt.request](request/index.html "slack_bolt.request")` Incoming request from Slack through either HTTP request or Socket Mode connection … `[slack_bolt.response](response/index.html "slack_bolt.response")` This interface represents Bolt's synchronous response to Slack … `[slack_bolt.util](util/index.html "slack_bolt.util")` Internal utilities for the Bolt framework. `[slack_bolt.version](version.html "slack_bolt.version")` Check the latest version at [https://pypi.org/project/slack-bolt/](https://pypi.org/project/slack-bolt/) `[slack_bolt.workflows](workflows/index.html "slack_bolt.workflows")` Steps from apps enables developers to build their own steps … ## Classes `class Ack` Expand source code ``` class Ack: response: Optional[BoltResponse] def __init__(self): self.response: Optional[BoltResponse] = None def __call__( self, text: Union[str, dict] = "", # text: str or whole_response: dict blocks: Optional[Sequence[Union[dict, Block]]] = None, attachments: Optional[Sequence[Union[dict, Attachment]]] = None, unfurl_links: Optional[bool] = None, unfurl_media: Optional[bool] = None, response_type: Optional[str] = None, # in_channel / ephemeral # block_suggestion / dialog_suggestion options: Optional[Sequence[Union[dict, Option]]] = None, option_groups: Optional[Sequence[Union[dict, OptionGroup]]] = None, # view_submission response_action: Optional[str] = None, # errors / update / push / clear errors: Optional[Dict[str, str]] = None, view: Optional[Union[dict, View]] = None, ) -> BoltResponse: return _set_response( self, text_or_whole_response=text, blocks=blocks, attachments=attachments, unfurl_links=unfurl_links, unfurl_media=unfurl_media, response_type=response_type, options=options, option_groups=option_groups, response_action=response_action, errors=errors, view=view, ) ``` ### Class variables `var response : [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None` The type of the None singleton. `class App (*, logger: logging.Logger | None = None, name: str | None = None, process_before_response: bool = False, raise_error_for_unhandled_request: bool = False, signing_secret: str | None = None, token: str | None = None, token_verification_enabled: bool = True, client: slack_sdk.web.client.WebClient | None = None, before_authorize: [Middleware](middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware") | Callable[..., Any] | None = None, authorize: Callable[..., [AuthorizeResult](authorization/authorize_result.html#slack_bolt.authorization.authorize_result.AuthorizeResult "slack_bolt.authorization.authorize_result.AuthorizeResult")] | None = None, user_facing_authorize_error_message: str | None = None, installation_store: slack_sdk.oauth.installation_store.installation_store.InstallationStore | None = None, installation_store_bot_only: bool | None = None, request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, ignoring_self_assistant_message_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, oauth_settings: [OAuthSettings](oauth/oauth_settings.html#slack_bolt.oauth.oauth_settings.OAuthSettings "slack_bolt.oauth.oauth_settings.OAuthSettings") | None = None, oauth_flow: [OAuthFlow](oauth/oauth_flow.html#slack_bolt.oauth.oauth_flow.OAuthFlow "slack_bolt.oauth.oauth_flow.OAuthFlow") | None = None, verification_token: str | None = None, listener_executor: concurrent.futures._base.Executor | None = None, assistant_thread_context_store: [AssistantThreadContextStore](context/assistant/thread_context_store/store.html#slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore "slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore") | None = None, attaching_conversation_kwargs_enabled: bool = True)` Expand source code ``` class App: def __init__( self, *, logger: Optional[logging.Logger] = None, # Used in logger name: Optional[str] = None, # Set True when you run this app on a FaaS platform process_before_response: bool = False, # Set True if you want to handle an unhandled request as an exception raise_error_for_unhandled_request: bool = False, # Basic Information > Credentials > Signing Secret signing_secret: Optional[str] = None, # for single-workspace apps token: Optional[str] = None, token_verification_enabled: bool = True, client: Optional[WebClient] = None, # for multi-workspace apps before_authorize: Optional[Union[Middleware, Callable[..., Any]]] = None, authorize: Optional[Callable[..., AuthorizeResult]] = None, user_facing_authorize_error_message: Optional[str] = None, installation_store: Optional[InstallationStore] = None, # for either only bot scope usage or v1.0.x compatibility installation_store_bot_only: Optional[bool] = None, # for customizing the built-in middleware request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, ignoring_self_assistant_message_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, # for the OAuth flow oauth_settings: Optional[OAuthSettings] = None, oauth_flow: Optional[OAuthFlow] = None, # No need to set (the value is used only in response to ssl_check requests) verification_token: Optional[str] = None, # Set this one only when you want to customize the executor listener_executor: Optional[Executor] = None, # for AI Agents & Assistants assistant_thread_context_store: Optional[AssistantThreadContextStore] = None, attaching_conversation_kwargs_enabled: bool = True, ): """Bolt App that provides functionalities to register middleware/listeners. import os from slack_bolt import App # Initializes your app with your bot token and signing secret app = App( token=os.environ.get("SLACK_BOT_TOKEN"), signing_secret=os.environ.get("SLACK_SIGNING_SECRET") ) # Listens to incoming messages that contain "hello" @app.message("hello") def message_hello(message, say): # say() sends a message to the channel where the event was triggered say(f"Hey there <@{message['user']}>!") # Start your app if __name__ == "__main__": app.start(port=int(os.environ.get("PORT", 3000))) Refer to https://docs.slack.dev/tools/bolt-python/creating-an-app for details. If you would like to build an OAuth app for enabling the app to run with multiple workspaces, refer to https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth to learn how to configure the app. Args: logger: The custom logger that can be used in this app. name: The application name that will be used in logging. If absent, the source file name will be used. process_before_response: True if this app runs on Function as a Service. (Default: False) raise_error_for_unhandled_request: True if you want to raise exceptions for unhandled requests and use @app.error listeners instead of the built-in handler, which pints warning logs and returns 404 to Slack (Default: False) signing_secret: The Signing Secret value used for verifying requests from Slack. token: The bot/user access token required only for single-workspace app. token_verification_enabled: Verifies the validity of the given token if True. client: The singleton `slack_sdk.WebClient` instance for this app. before_authorize: A global middleware that can be executed right before authorize function authorize: The function to authorize an incoming request from Slack by checking if there is a team/user in the installation data. user_facing_authorize_error_message: The user-facing error message to display when the app is installed but the installation is not managed by this app's installation store installation_store: The module offering save/find operations of installation data installation_store_bot_only: Use `InstallationStore#find_bot()` if True (Default: False) request_verification_enabled: False if you would like to disable the built-in middleware (Default: True). `RequestVerification` is a built-in middleware that verifies the signature in HTTP Mode requests. Make sure if it's safe enough when you turn a built-in middleware off. We strongly recommend using RequestVerification for better security. If you have a proxy that verifies request signature in front of the Bolt app, it's totally fine to disable RequestVerification to avoid duplication of work. Don't turn it off just for easiness of development. ignoring_self_events_enabled: False if you would like to disable the built-in middleware (Default: True). `IgnoringSelfEvents` is a built-in middleware that enables Bolt apps to easily skip the events generated by this app's bot user (this is useful for avoiding code error causing an infinite loop). ignoring_self_assistant_message_events_enabled: False if you would like to disable the built-in middleware. `IgnoringSelfEvents` for this app's bot user message events within an assistant thread This is useful for avoiding code error causing an infinite loop; Default: True url_verification_enabled: False if you would like to disable the built-in middleware (Default: True). `UrlVerification` is a built-in middleware that handles url_verification requests that verify the endpoint for Events API in HTTP Mode requests. attaching_function_token_enabled: False if you would like to disable the built-in middleware (Default: True). `AttachingFunctionToken` is a built-in middleware that injects the just-in-time workflow-execution tokens when your app receives `function_executed` or interactivity events scoped to a custom step. ssl_check_enabled: bool = False if you would like to disable the built-in middleware (Default: True). `SslCheck` is a built-in middleware that handles ssl_check requests from Slack. oauth_settings: The settings related to Slack app installation flow (OAuth flow) oauth_flow: Instantiated `slack_bolt.oauth.OAuthFlow`. This is always prioritized over oauth_settings. verification_token: Deprecated verification mechanism. This can be used only for ssl_check requests. listener_executor: Custom executor to run background tasks. If absent, the default `ThreadPoolExecutor` will be used. assistant_thread_context_store: Custom AssistantThreadContext store (Default: the built-in implementation, which uses a parent message's metadata to store the latest context) """ if signing_secret is None: signing_secret = os.environ.get("SLACK_SIGNING_SECRET", "") token = token or os.environ.get("SLACK_BOT_TOKEN") self._name: str = name or inspect.stack()[1].filename.split(os.path.sep)[-1] self._signing_secret: str = signing_secret self._verification_token: Optional[str] = verification_token or os.environ.get("SLACK_VERIFICATION_TOKEN", None) # If a logger is explicitly passed when initializing, the logger works as the base logger. # The base logger's logging settings will be propagated to all the loggers created by bolt-python. self._base_logger = logger # The framework logger is supposed to be used for the internal logging. # Also, it's accessible via `app.logger` as the app's singleton logger. self._framework_logger = logger or get_bolt_logger(App) self._raise_error_for_unhandled_request = raise_error_for_unhandled_request self._token: Optional[str] = token if client is not None: if not isinstance(client, WebClient): raise BoltError(error_client_invalid_type()) self._client = client self._token = client.token if token is not None: self._framework_logger.warning(warning_client_prioritized_and_token_skipped()) else: self._client = create_web_client( # NOTE: the token here can be None token=token, logger=self._framework_logger, ) # -------------------------------------- # Authorize & OAuthFlow initialization # -------------------------------------- self._before_authorize: Optional[Middleware] = None if before_authorize is not None: if callable(before_authorize): self._before_authorize = CustomMiddleware( app_name=self._name, func=before_authorize, base_logger=self._framework_logger, ) elif isinstance(before_authorize, Middleware): self._before_authorize = before_authorize self._authorize: Optional[Authorize] = None if authorize is not None: if isinstance(authorize, Authorize): # As long as an advanced developer understands what they're doing, # bolt-python should not prevent customizing authorize middleware self._authorize = authorize else: if oauth_settings is not None or oauth_flow is not None: # If the given authorize is a simple function, # it does not work along with installation_store. raise BoltError(error_authorize_conflicts()) self._authorize = CallableAuthorize(logger=self._framework_logger, func=authorize) self._installation_store: Optional[InstallationStore] = installation_store if self._installation_store is not None and self._authorize is None: settings = oauth_flow.settings if oauth_flow is not None else oauth_settings self._authorize = InstallationStoreAuthorize( installation_store=self._installation_store, client_id=settings.client_id if settings is not None else None, client_secret=settings.client_secret if settings is not None else None, logger=self._framework_logger, bot_only=installation_store_bot_only or False, client=self._client, # for proxy use cases etc. user_token_resolution=(settings.user_token_resolution if settings is not None else "authed_user"), ) self._oauth_flow: Optional[OAuthFlow] = None if ( oauth_settings is None and os.environ.get("SLACK_CLIENT_ID") is not None and os.environ.get("SLACK_CLIENT_SECRET") is not None ): # initialize with the default settings oauth_settings = OAuthSettings() if oauth_flow is None and installation_store is None: # show info-level log for avoiding confusions self._framework_logger.info(info_default_oauth_settings_loaded()) if oauth_flow is not None: self._oauth_flow = oauth_flow installation_store = select_consistent_installation_store( client_id=self._oauth_flow.client_id, app_store=self._installation_store, oauth_flow_store=self._oauth_flow.settings.installation_store, logger=self._framework_logger, ) self._installation_store = installation_store if installation_store is not None: self._oauth_flow.settings.installation_store = installation_store if self._oauth_flow._client is None: self._oauth_flow._client = self._client if self._authorize is None: self._authorize = self._oauth_flow.settings.authorize elif oauth_settings is not None: installation_store = select_consistent_installation_store( client_id=oauth_settings.client_id, app_store=self._installation_store, oauth_flow_store=oauth_settings.installation_store, logger=self._framework_logger, ) self._installation_store = installation_store if installation_store is not None: oauth_settings.installation_store = installation_store self._oauth_flow = OAuthFlow(client=self.client, logger=self.logger, settings=oauth_settings) if self._authorize is None: self._authorize = self._oauth_flow.settings.authorize self._authorize.token_rotation_expiration_minutes = oauth_settings.token_rotation_expiration_minutes # type: ignore[attr-defined] # noqa: E501 if (self._installation_store is not None or self._authorize is not None) and self._token is not None: self._token = None self._framework_logger.warning(warning_token_skipped()) # after setting bot_only here, __init__ cannot replace authorize function if installation_store_bot_only is not None and self._oauth_flow is not None: app_bot_only = installation_store_bot_only or False oauth_flow_bot_only = self._oauth_flow.settings.installation_store_bot_only if app_bot_only != oauth_flow_bot_only: self.logger.warning(warning_bot_only_conflicts()) self._oauth_flow.settings.installation_store_bot_only = app_bot_only self._authorize.bot_only = app_bot_only # type: ignore[union-attr] self._tokens_revocation_listeners: Optional[TokenRevocationListeners] = None if self._installation_store is not None: self._tokens_revocation_listeners = TokenRevocationListeners(self._installation_store) # -------------------------------------- # Middleware Initialization # -------------------------------------- self._middleware_list: List[Middleware] = [] self._listeners: List[Listener] = [] if listener_executor is None: listener_executor = ThreadPoolExecutor(max_workers=5) self._assistant_thread_context_store = assistant_thread_context_store self._attaching_conversation_kwargs_enabled = attaching_conversation_kwargs_enabled self._process_before_response = process_before_response self._listener_runner = ThreadListenerRunner( logger=self._framework_logger, process_before_response=process_before_response, listener_error_handler=DefaultListenerErrorHandler(logger=self._framework_logger), listener_start_handler=DefaultListenerStartHandler(logger=self._framework_logger), listener_completion_handler=DefaultListenerCompletionHandler(logger=self._framework_logger), listener_executor=listener_executor, lazy_listener_runner=ThreadLazyListenerRunner( logger=self._framework_logger, executor=listener_executor, ), ) self._middleware_error_handler: MiddlewareErrorHandler = DefaultMiddlewareErrorHandler( logger=self._framework_logger, ) self._init_middleware_list_done = False self._init_middleware_list( token_verification_enabled=token_verification_enabled, request_verification_enabled=request_verification_enabled, ignoring_self_events_enabled=ignoring_self_events_enabled, ignoring_self_assistant_message_events_enabled=ignoring_self_assistant_message_events_enabled, ssl_check_enabled=ssl_check_enabled, url_verification_enabled=url_verification_enabled, attaching_function_token_enabled=attaching_function_token_enabled, user_facing_authorize_error_message=user_facing_authorize_error_message, ) def _init_middleware_list( self, token_verification_enabled: bool = True, request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, ignoring_self_assistant_message_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, user_facing_authorize_error_message: Optional[str] = None, ): if self._init_middleware_list_done: return if ssl_check_enabled is True: self._middleware_list.append( SslCheck( verification_token=self._verification_token, base_logger=self._base_logger, ) ) if request_verification_enabled is True: self._middleware_list.append(RequestVerification(self._signing_secret, base_logger=self._base_logger)) if self._before_authorize is not None: self._middleware_list.append(self._before_authorize) # As authorize is required for making a Bolt app function, we don't offer the flag to disable this if self._oauth_flow is None: if self._token is not None: try: auth_test_result = None if token_verification_enabled: # This API call is for eagerly validating the token auth_test_result = self._client.auth_test(token=self._token) self._middleware_list.append( SingleTeamAuthorization( auth_test_result=auth_test_result, base_logger=self._base_logger, user_facing_authorize_error_message=user_facing_authorize_error_message, ) ) except SlackApiError as err: raise BoltError(error_auth_test_failure(err.response)) elif self._authorize is not None: self._middleware_list.append( MultiTeamsAuthorization( authorize=self._authorize, base_logger=self._base_logger, user_facing_authorize_error_message=user_facing_authorize_error_message, ) ) else: raise BoltError(error_token_required()) elif self._authorize is not None: self._middleware_list.append( MultiTeamsAuthorization( authorize=self._authorize, base_logger=self._base_logger, user_token_resolution=self._oauth_flow.settings.user_token_resolution, user_facing_authorize_error_message=user_facing_authorize_error_message, ) ) else: raise BoltError(error_oauth_flow_or_authorize_required()) if ignoring_self_events_enabled is True: self._middleware_list.append( IgnoringSelfEvents( base_logger=self._base_logger, ignoring_self_assistant_message_events_enabled=ignoring_self_assistant_message_events_enabled, ) ) if url_verification_enabled is True: self._middleware_list.append(UrlVerification(base_logger=self._base_logger)) if attaching_function_token_enabled is True: self._middleware_list.append(AttachingFunctionToken()) self._init_middleware_list_done = True # ------------------------- # accessors @property def name(self) -> str: """The name of this app (default: the filename)""" return self._name @property def oauth_flow(self) -> Optional[OAuthFlow]: """Configured `OAuthFlow` object if exists.""" return self._oauth_flow @property def logger(self) -> logging.Logger: """The logger this app uses.""" return self._framework_logger @property def client(self) -> WebClient: """The singleton `slack_sdk.WebClient` instance in this app.""" return self._client @property def installation_store(self) -> Optional[InstallationStore]: """The `slack_sdk.oauth.InstallationStore` that can be used in the `authorize` middleware.""" return self._installation_store @property def listener_runner(self) -> ThreadListenerRunner: """The thread executor for asynchronously running listeners.""" return self._listener_runner @property def process_before_response(self) -> bool: return self._process_before_response or False # ------------------------- # standalone server def start( self, port: int = 3000, path: str = "/slack/events", http_server_logger_enabled: bool = True, ) -> None: """Starts a web server for local development. # With the default settings, `http://localhost:3000/slack/events` # is available for handling incoming requests from Slack app.start() This method internally starts a Web server process built with the `http.server` module. For production, consider using a production-ready WSGI server such as Gunicorn. Args: port: The port to listen on (Default: 3000) path: The path to handle request from Slack (Default: `/slack/events`) http_server_logger_enabled: The flag to enable http.server logging if True (Default: True) """ self._development_server = SlackAppDevelopmentServer( port=port, path=path, app=self, oauth_flow=self.oauth_flow, http_server_logger_enabled=http_server_logger_enabled, ) self._development_server.start() # ------------------------- # main dispatcher def dispatch(self, req: BoltRequest) -> BoltResponse: """Applies all middleware and dispatches an incoming request from Slack to the right code path. Args: req: An incoming request from Slack Returns: The response generated by this Bolt app """ starting_time = time.time() self._init_context(req) resp: Optional[BoltResponse] = BoltResponse(status=200, body="") middleware_state = {"next_called": False} def middleware_next(): middleware_state["next_called"] = True try: for middleware in self._middleware_list: middleware_state["next_called"] = False if self._framework_logger.level <= logging.DEBUG: self._framework_logger.debug(debug_applying_middleware(middleware.name)) resp = middleware.process(req=req, resp=resp, next=middleware_next) # type: ignore[arg-type] if not middleware_state["next_called"]: if resp is None: # next() method was not called without providing the response to return to Slack # This should not be an intentional handling in usual use cases. resp = BoltResponse(status=404, body={"error": "no next() calls in middleware"}) if self._raise_error_for_unhandled_request is True: try: raise BoltUnhandledRequestError( request=req, current_response=resp, last_global_middleware_name=middleware.name, ) except BoltUnhandledRequestError as e: self._listener_runner.listener_error_handler.handle( error=e, request=req, response=resp, ) return resp self._framework_logger.warning(warning_unhandled_by_global_middleware(middleware.name, req)) return resp return resp for listener in self._listeners: listener_name = get_name_for_callable(listener.ack_function) self._framework_logger.debug(debug_checking_listener(listener_name)) if listener.matches(req=req, resp=resp): # type: ignore[arg-type] # run all the middleware attached to this listener first middleware_resp, next_was_not_called = listener.run_middleware( req=req, resp=resp # type: ignore[arg-type] ) if next_was_not_called: if middleware_resp is not None: if self._framework_logger.level <= logging.DEBUG: debug_message = debug_return_listener_middleware_response( listener_name, middleware_resp.status, middleware_resp.body, starting_time, ) self._framework_logger.debug(debug_message) return middleware_resp # The last listener middleware didn't call next() method. # This means the listener is not for this incoming request. continue if middleware_resp is not None: resp = middleware_resp self._framework_logger.debug(debug_running_listener(listener_name)) listener_response: Optional[BoltResponse] = self._listener_runner.run( request=req, response=resp, # type: ignore[arg-type] listener_name=listener_name, listener=listener, ) if listener_response is not None: return listener_response if resp is None: resp = BoltResponse(status=404, body={"error": "unhandled request"}) if self._raise_error_for_unhandled_request is True: try: raise BoltUnhandledRequestError( request=req, current_response=resp, ) except BoltUnhandledRequestError as e: self._listener_runner.listener_error_handler.handle( error=e, request=req, response=resp, ) return resp return self._handle_unmatched_requests(req, resp) except Exception as error: resp = BoltResponse(status=500, body="") self._middleware_error_handler.handle( error=error, request=req, response=resp, ) return resp def _handle_unmatched_requests(self, req: BoltRequest, resp: BoltResponse) -> BoltResponse: self._framework_logger.warning(warning_unhandled_request(req)) return resp # ------------------------- # middleware def use(self, *args) -> Optional[Callable]: """Registers a new global middleware to this app. This method can be used as either a decorator or a method. Refer to `App#middleware()` method's docstring for details.""" return self.middleware(*args) def middleware(self, *args) -> Optional[Callable]: """Registers a new middleware to this app. This method can be used as either a decorator or a method. # Use this method as a decorator @app.middleware def middleware_func(logger, body, next): logger.info(f"request body: {body}") next() # Pass a function to this method app.middleware(middleware_func) Refer to https://docs.slack.dev/tools/bolt-python/concepts/global-middleware for details. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: *args: A function that works as a global middleware. """ if len(args) > 0: middleware_or_callable = args[0] if isinstance(middleware_or_callable, Middleware): middleware: Middleware = middleware_or_callable self._middleware_list.append(middleware) if isinstance(middleware, Assistant) and middleware.thread_context_store is not None: self._assistant_thread_context_store = middleware.thread_context_store elif callable(middleware_or_callable): self._middleware_list.append( CustomMiddleware( app_name=self.name, func=middleware_or_callable, base_logger=self._base_logger, ) ) return middleware_or_callable else: raise BoltError(f"Unexpected type for a middleware ({type(middleware_or_callable)})") return None # ------------------------- # AI Agents & Assistants def assistant(self, assistant: Assistant) -> Optional[Callable]: return self.middleware(assistant) # ------------------------- # Workflows: Steps from apps def step( self, callback_id: Union[str, Pattern, WorkflowStep, WorkflowStepBuilder], edit: Optional[Union[Callable[..., Optional[BoltResponse]], Listener, Sequence[Callable]]] = None, save: Optional[Union[Callable[..., Optional[BoltResponse]], Listener, Sequence[Callable]]] = None, execute: Optional[Union[Callable[..., Optional[BoltResponse]], Listener, Sequence[Callable]]] = None, ): """ Deprecated: Steps from apps for legacy workflows are now deprecated. Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new step from app listener. Unlike others, this method doesn't behave as a decorator. If you want to register a step from app by a decorator, use `WorkflowStepBuilder`'s methods. # Create a new WorkflowStep instance from slack_bolt.workflows.step import WorkflowStep ws = WorkflowStep( callback_id="add_task", edit=edit, save=save, execute=execute, ) # Pass Step to set up listeners app.step(ws) Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. For further information about WorkflowStep specific function arguments such as `configure`, `update`, `complete`, and `fail`, refer to `slack_bolt.workflows.step.utilities` API documents. Args: callback_id: The Callback ID for this step from app edit: The function for displaying a modal in the Workflow Builder save: The function for handling configuration in the Workflow Builder execute: The function for handling the step execution """ warnings.warn( ( "Steps from apps for legacy workflows are now deprecated. " "Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/" ), category=DeprecationWarning, ) step = callback_id if isinstance(callback_id, (str, Pattern)): step = WorkflowStep( callback_id=callback_id, edit=edit, # type: ignore[arg-type] save=save, # type: ignore[arg-type] execute=execute, # type: ignore[arg-type] base_logger=self._base_logger, ) elif isinstance(step, WorkflowStepBuilder): step = step.build(base_logger=self._base_logger) elif not isinstance(step, WorkflowStep): raise BoltError(f"Invalid step object ({type(step)})") self.use(WorkflowStepMiddleware(step)) # ------------------------- # global error handler def error(self, func: Callable[..., Optional[BoltResponse]]) -> Callable[..., Optional[BoltResponse]]: """Updates the global error handler. This method can be used as either a decorator or a method. # Use this method as a decorator @app.error def custom_error_handler(error, body, logger): logger.exception(f"Error: {error}") logger.info(f"Request body: {body}") # Pass a function to this method app.error(custom_error_handler) To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: func: The function that is supposed to be executed when getting an unhandled error in Bolt app. """ self._listener_runner.listener_error_handler = CustomListenerErrorHandler( logger=self._framework_logger, func=func, ) self._middleware_error_handler = CustomMiddlewareErrorHandler( logger=self._framework_logger, func=func, ) return func # ------------------------- # events def event( self, event: Union[ str, Pattern, Dict[str, Optional[Union[str, Sequence[Optional[Union[str, Pattern]]]]]], ], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new event listener. This method can be used as either a decorator or a method. # Use this method as a decorator @app.event("team_join") def ask_for_introduction(event, say): welcome_channel_id = "C12345" user_id = event["user"] text = f"Welcome to the team, <@{user_id}>! :tada: You can introduce yourself in this channel." say(text=text, channel=welcome_channel_id) # Pass a function to this method app.event("team_join")(ask_for_introduction) Refer to https://docs.slack.dev/apis/events-api/ for details of Events API. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: event: The conditions that match a request payload. If you pass a dict for this, you can have type, subtype in the constraint. matchers: A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ middleware = list(middleware) if middleware else [] def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.event(event, base_logger=self._base_logger) if self._attaching_conversation_kwargs_enabled: middleware.insert(0, AttachingConversationKwargs(self._assistant_thread_context_store)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) return __call__ def message( self, keyword: Union[str, Pattern] = "", matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new message event listener. This method can be used as either a decorator or a method. Check the `App#event` method's docstring for details. # Use this method as a decorator @app.message(":wave:") def say_hello(message, say): user = message['user'] say(f"Hi there, <@{user}>!") # Pass a function to this method app.message(":wave:")(say_hello) Refer to https://docs.slack.dev/reference/events/message/ for details of `message` events. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: keyword: The keyword to match matchers: A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ matchers = list(matchers) if matchers else [] middleware = list(middleware) if middleware else [] def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) constraints = { "type": "message", "subtype": ( # In most cases, new message events come with no subtype. None, # As of Jan 2021, most bot messages no longer have the subtype bot_message. # By contrast, messages posted using classic app's bot token still have the subtype. "bot_message", # If an end-user posts a message with "Also send to #channel" checked, # the message event comes with this subtype. "thread_broadcast", # If an end-user posts a message with attached files, # the message event comes with this subtype. "file_share", ), } primary_matcher = builtin_matchers.message_event( keyword=keyword, constraints=constraints, base_logger=self._base_logger ) if self._attaching_conversation_kwargs_enabled: middleware.insert(0, AttachingConversationKwargs(self._assistant_thread_context_store)) middleware.insert(0, MessageListenerMatches(keyword)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) return __call__ def function( self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, auto_acknowledge: bool = True, ack_timeout: int = 3, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new Function listener. This method can be used as either a decorator or a method. # Use this method as a decorator @app.function("reverse") def reverse_string(ack: Ack, inputs: dict, complete: Complete, fail: Fail): try: ack() string_to_reverse = inputs["stringToReverse"] complete(outputs={"reverseString": string_to_reverse[::-1]}) except Exception as e: fail(f"Cannot reverse string (error: {e})") raise e # Pass a function to this method app.function("reverse")(reverse_string) To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: callback_id: The callback id to identify the function matchers: A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ if auto_acknowledge is True: if ack_timeout != 3: self._framework_logger.warning(warning_ack_timeout_has_no_effect(callback_id, ack_timeout)) matchers = list(matchers) if matchers else [] middleware = list(middleware) if middleware else [] def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.function_executed(callback_id=callback_id, base_logger=self._base_logger) return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge, ack_timeout) return __call__ # ------------------------- # slash commands def command( self, command: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new slash command listener. This method can be used as either a decorator or a method. # Use this method as a decorator @app.command("/echo") def repeat_text(ack, say, command): # Acknowledge command request ack() say(f"{command['text']}") # Pass a function to this method app.command("/echo")(repeat_text) Refer to https://docs.slack.dev/interactivity/implementing-slash-commands/ for details of Slash Commands. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: command: The conditions that match a request payload matchers: A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.command(command, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ # ------------------------- # shortcut def shortcut( self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new shortcut listener. This method can be used as either a decorator or a method. # Use this method as a decorator @app.shortcut("open_modal") def open_modal(ack, body, client): # Acknowledge the command request ack() # Call views_open with the built-in client client.views_open( # Pass a valid trigger_id within 3 seconds of receiving it trigger_id=body["trigger_id"], # View payload view={ ... } ) # Pass a function to this method app.shortcut("open_modal")(open_modal) Refer to https://docs.slack.dev/interactivity/implementing-shortcuts/ for details about Shortcuts. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: constraints: The conditions that match a request payload. matchers: A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.shortcut(constraints, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ def global_shortcut( self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new global shortcut listener.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.global_shortcut(callback_id, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ def message_shortcut( self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new message shortcut listener.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.message_shortcut(callback_id, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ # ------------------------- # action def action( self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new action listener. This method can be used as either a decorator or a method. # Use this method as a decorator @app.action("approve_button") def update_message(ack): ack() # Pass a function to this method app.action("approve_button")(update_message) * Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for actions in `blocks`. * Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for actions in `attachments`. * Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for actions in dialogs. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: constraints: The conditions that match a request payload matchers: A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.action(constraints, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ def block_action( self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `block_actions` action listener. Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for details. """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.block_action(constraints, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ def attachment_action( self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `interactive_message` action listener. Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.attachment_action(callback_id, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ def dialog_submission( self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_submission` listener. Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.dialog_submission(callback_id, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ def dialog_cancellation( self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_cancellation` listener. Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.dialog_cancellation(callback_id, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ # ------------------------- # view def view( self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_submission`/`view_closed` event listener. This method can be used as either a decorator or a method. # Use this method as a decorator @app.view("view_1") def handle_submission(ack, body, client, view): # Assume there's an input block with `block_c` as the block_id and `dreamy_input` hopes_and_dreams = view["state"]["values"]["block_c"]["dreamy_input"] user = body["user"]["id"] # Validate the inputs errors = {} if hopes_and_dreams is not None and len(hopes_and_dreams) <= 5: errors["block_c"] = "The value must be longer than 5 characters" if len(errors) > 0: ack(response_action="errors", errors=errors) return # Acknowledge the view_submission event and close the modal ack() # Do whatever you want with the input data - here we're saving it to a DB # Pass a function to this method app.view("view_1")(handle_submission) Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: constraints: The conditions that match a request payload matchers: A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.view(constraints, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ def view_submission( self, constraints: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_submission` listener. Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details. """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.view_submission(constraints, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ def view_closed( self, constraints: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_closed` listener. Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_closed for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.view_closed(constraints, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ # ------------------------- # options def options( self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new options listener. This method can be used as either a decorator or a method. # Use this method as a decorator @app.options("menu_selection") def show_menu_options(ack): options = [ { "text": {"type": "plain_text", "text": "Option 1"}, "value": "1-1", }, { "text": {"type": "plain_text", "text": "Option 2"}, "value": "1-2", }, ] ack(options=options) # Pass a function to this method app.options("menu_selection")(show_menu_options) Refer to the following documents for details: * https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element#external_select * https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: matchers: A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.options(constraints, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ def block_suggestion( self, action_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `block_suggestion` listener.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.block_suggestion(action_id, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ def dialog_suggestion( self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_suggestion` listener. Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.dialog_suggestion(callback_id, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ # ------------------------- # built-in listener functions def default_tokens_revoked_event_listener( self, ) -> Callable[..., Optional[BoltResponse]]: if self._tokens_revocation_listeners is None: raise BoltError(error_installation_store_required_for_builtin_listeners()) return self._tokens_revocation_listeners.handle_tokens_revoked_events def default_app_uninstalled_event_listener( self, ) -> Callable[..., Optional[BoltResponse]]: if self._tokens_revocation_listeners is None: raise BoltError(error_installation_store_required_for_builtin_listeners()) return self._tokens_revocation_listeners.handle_app_uninstalled_events def enable_token_revocation_listeners(self) -> None: self.event("tokens_revoked")(self.default_tokens_revoked_event_listener()) self.event("app_uninstalled")(self.default_app_uninstalled_event_listener()) # ------------------------- def _init_context(self, req: BoltRequest): req.context["logger"] = get_bolt_app_logger(app_name=self.name, base_logger=self._base_logger) req.context["token"] = self._token # Prior to version 1.15, when the token is static, self._client was passed to `req.context`. # The intention was to avoid creating a new instance per request # in the interest of runtime performance/memory footprint optimization. # However, developers may want to replace the token held by req.context.client in some situations. # In this case, this behavior can result in thread-unsafe data modification on `self._client`. # (`self._client` a.k.a. `app.client` is a singleton object per an App instance) # Thus, we've changed the behavior to create a new instance per request regardless of token argument # in the App initialization starting v1.15. # The overhead brought by this change is slight so that we believe that it is ignorable in any cases. client_per_request: WebClient = WebClient( token=self._token, # this can be None, and it can be set later on base_url=self._client.base_url, timeout=self._client.timeout, ssl=self._client.ssl, proxy=self._client.proxy, headers=self._client.headers, team_id=req.context.team_id, logger=self._client.logger, retry_handlers=self._client.retry_handlers.copy() if self._client.retry_handlers is not None else None, ) req.context["client"] = client_per_request # Most apps do not need this "listener_runner" instance. # It is intended for apps that start lazy listeners from their custom global middleware. req.context["listener_runner"] = self.listener_runner @staticmethod def _to_listener_functions( kwargs: dict, ) -> Optional[Sequence[Callable[..., Optional[BoltResponse]]]]: if kwargs: functions = [kwargs["ack"]] for sub in kwargs["lazy"]: functions.append(sub) return functions return None def _register_listener( self, functions: Sequence[Callable[..., Optional[BoltResponse]]], primary_matcher: ListenerMatcher, matchers: Optional[Sequence[Callable[..., bool]]], middleware: Optional[Sequence[Union[Callable, Middleware]]], auto_acknowledgement: bool = False, ack_timeout: int = 3, ) -> Optional[Callable[..., Optional[BoltResponse]]]: value_to_return = None if not isinstance(functions, list): functions = list(functions) if len(functions) == 1: # In the case where the function is registered using decorator, # the registration should return the original function. value_to_return = functions[0] listener_matchers: List[ListenerMatcher] = [ CustomListenerMatcher(app_name=self.name, func=f, base_logger=self._base_logger) for f in (matchers or []) ] listener_matchers.insert(0, primary_matcher) listener_middleware = [] for m in middleware or []: if isinstance(m, Middleware): listener_middleware.append(m) elif callable(m): listener_middleware.append(CustomMiddleware(app_name=self.name, func=m, base_logger=self._base_logger)) else: raise ValueError(error_unexpected_listener_middleware(type(m))) self._listeners.append( CustomListener( app_name=self.name, ack_function=functions.pop(0), lazy_functions=functions, # type: ignore[arg-type] matchers=listener_matchers, middleware=listener_middleware, auto_acknowledgement=auto_acknowledgement, ack_timeout=ack_timeout, base_logger=self._base_logger, ) ) return value_to_return ``` Bolt App that provides functionalities to register middleware/listeners. ``` import os from slack_bolt import App # Initializes your app with your bot token and signing secret app = App( token=os.environ.get("SLACK_BOT_TOKEN"), signing_secret=os.environ.get("SLACK_SIGNING_SECRET") ) # Listens to incoming messages that contain "hello" @app.message("hello") def message_hello(message, say): # say() sends a message to the channel where the event was triggered say(f"Hey there <@{message['user']}>!") # Start your app if __name__ == "__main__": app.start(port=int(os.environ.get("PORT", 3000))) ``` Refer to [https://docs.slack.dev/tools/bolt-python/creating-an-app](https://docs.slack.dev/tools/bolt-python/creating-an-app) for details. If you would like to build an OAuth app for enabling the app to run with multiple workspaces, refer to [https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth](https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth) to learn how to configure the app. ## Args **`logger`** The custom logger that can be used in this app. **`name`** The application name that will be used in logging. If absent, the source file name will be used. **`process_before_response`** True if this app runs on Function as a Service. (Default: False) **`raise_error_for_unhandled_request`** True if you want to raise exceptions for unhandled requests and use @app.error listeners instead of the built-in handler, which pints warning logs and returns 404 to Slack (Default: False) **`signing_secret`** The Signing Secret value used for verifying requests from Slack. **`token`** The bot/user access token required only for single-workspace app. **`token_verification_enabled`** Verifies the validity of the given token if True. **`client`** The singleton `slack_sdk.WebClient` instance for this app. **`before_authorize`** A global middleware that can be executed right before authorize function **`authorize`** The function to authorize an incoming request from Slack by checking if there is a team/user in the installation data. **`user_facing_authorize_error_message`** The user-facing error message to display when the app is installed but the installation is not managed by this app's installation store **`installation_store`** The module offering save/find operations of installation data **`installation_store_bot_only`** Use `InstallationStore#find_bot()` if True (Default: False) **`request_verification_enabled`** False if you would like to disable the built-in middleware (Default: True). `RequestVerification` is a built-in middleware that verifies the signature in HTTP Mode requests. Make sure if it's safe enough when you turn a built-in middleware off. We strongly recommend using RequestVerification for better security. If you have a proxy that verifies request signature in front of the Bolt app, it's totally fine to disable RequestVerification to avoid duplication of work. Don't turn it off just for easiness of development. **`ignoring_self_events_enabled`** False if you would like to disable the built-in middleware (Default: True). `IgnoringSelfEvents` is a built-in middleware that enables Bolt apps to easily skip the events generated by this app's bot user (this is useful for avoiding code error causing an infinite loop). **`ignoring_self_assistant_message_events_enabled`** False if you would like to disable the built-in middleware. `IgnoringSelfEvents` for this app's bot user message events within an assistant thread This is useful for avoiding code error causing an infinite loop; Default: True **`url_verification_enabled`** False if you would like to disable the built-in middleware (Default: True). `UrlVerification` is a built-in middleware that handles url\_verification requests that verify the endpoint for Events API in HTTP Mode requests. **`attaching_function_token_enabled`** False if you would like to disable the built-in middleware (Default: True). `AttachingFunctionToken` is a built-in middleware that injects the just-in-time workflow-execution tokens when your app receives `function_executed` or interactivity events scoped to a custom step. **`ssl_check_enabled`** bool = False if you would like to disable the built-in middleware (Default: True). `SslCheck` is a built-in middleware that handles ssl\_check requests from Slack. **`oauth_settings`** The settings related to Slack app installation flow (OAuth flow) **`oauth_flow`** Instantiated `[OAuthFlow](oauth/index.html#slack_bolt.oauth.OAuthFlow "slack_bolt.oauth.OAuthFlow")`. This is always prioritized over oauth\_settings. **`verification_token`** Deprecated verification mechanism. This can be used only for ssl\_check requests. **`listener_executor`** Custom executor to run background tasks. If absent, the default `ThreadPoolExecutor` will be used. **`assistant_thread_context_store`** Custom AssistantThreadContext store (Default: the built-in implementation, which uses a parent message's metadata to store the latest context) ### Instance variables `prop client : slack_sdk.web.client.WebClient` Expand source code ``` @property def client(self) -> WebClient: """The singleton `slack_sdk.WebClient` instance in this app.""" return self._client ``` The singleton `slack_sdk.WebClient` instance in this app. `prop installation_store : slack_sdk.oauth.installation_store.installation_store.InstallationStore | None` Expand source code ``` @property def installation_store(self) -> Optional[InstallationStore]: """The `slack_sdk.oauth.InstallationStore` that can be used in the `authorize` middleware.""" return self._installation_store ``` The `slack_sdk.oauth.InstallationStore` that can be used in the `authorize` middleware. `prop listener_runner : [ThreadListenerRunner](listener/thread_runner.html#slack_bolt.listener.thread_runner.ThreadListenerRunner "slack_bolt.listener.thread_runner.ThreadListenerRunner")` Expand source code ``` @property def listener_runner(self) -> ThreadListenerRunner: """The thread executor for asynchronously running listeners.""" return self._listener_runner ``` The thread executor for asynchronously running listeners. `prop logger : logging.Logger` Expand source code ``` @property def logger(self) -> logging.Logger: """The logger this app uses.""" return self._framework_logger ``` The logger this app uses. `prop name : str` Expand source code ``` @property def name(self) -> str: """The name of this app (default: the filename)""" return self._name ``` The name of this app (default: the filename) `prop oauth_flow : [OAuthFlow](oauth/oauth_flow.html#slack_bolt.oauth.oauth_flow.OAuthFlow "slack_bolt.oauth.oauth_flow.OAuthFlow") | None` Expand source code ``` @property def oauth_flow(self) -> Optional[OAuthFlow]: """Configured `OAuthFlow` object if exists.""" return self._oauth_flow ``` Configured `OAuthFlow` object if exists. `prop process_before_response : bool` Expand source code ``` @property def process_before_response(self) -> bool: return self._process_before_response or False ``` ### Methods `def action(self, constraints: str | Pattern | Dict[str, str | Pattern], matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def action( self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new action listener. This method can be used as either a decorator or a method. # Use this method as a decorator @app.action("approve_button") def update_message(ack): ack() # Pass a function to this method app.action("approve_button")(update_message) * Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for actions in `blocks`. * Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for actions in `attachments`. * Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for actions in dialogs. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: constraints: The conditions that match a request payload matchers: A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.action(constraints, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ ``` Registers a new action listener. This method can be used as either a decorator or a method. ``` # Use this method as a decorator @app.action("approve_button") def update_message(ack): ack() # Pass a function to this method app.action("approve_button")(update_message) ``` * Refer to [https://docs.slack.dev/reference/interaction-payloads/block\_actions-payload/](https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/) for actions in `blocks`. * Refer to [https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/](https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/) for actions in `attachments`. * Refer to [https://docs.slack.dev/legacy/legacy-dialogs/](https://docs.slack.dev/legacy/legacy-dialogs/) for actions in dialogs. To learn available arguments for middleware/listeners, see `[slack_bolt.kwargs_injection.args](kwargs_injection/args.html "slack_bolt.kwargs_injection.args")`'s API document. ## Args **`constraints`** The conditions that match a request payload **`matchers`** A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. **`middleware`** A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. `def assistant(self, assistant: [Assistant](middleware/assistant/assistant.html#slack_bolt.middleware.assistant.assistant.Assistant "slack_bolt.middleware.assistant.assistant.Assistant")) ‑> Callable | None` Expand source code ``` def assistant(self, assistant: Assistant) -> Optional[Callable]: return self.middleware(assistant) ``` `def attachment_action(self, callback_id: str | Pattern, matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def attachment_action( self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `interactive_message` action listener. Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.attachment_action(callback_id, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ ``` Registers a new `interactive_message` action listener. Refer to [https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/](https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/) for details. `def block_action(self, constraints: str | Pattern | Dict[str, str | Pattern], matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def block_action( self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `block_actions` action listener. Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for details. """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.block_action(constraints, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ ``` Registers a new `block_actions` action listener. Refer to [https://docs.slack.dev/reference/interaction-payloads/block\_actions-payload/](https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/) for details. `def block_suggestion(self, action_id: str | Pattern, matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def block_suggestion( self, action_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `block_suggestion` listener.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.block_suggestion(action_id, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ ``` Registers a new `block_suggestion` listener. `def command(self, command: str | Pattern, matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def command( self, command: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new slash command listener. This method can be used as either a decorator or a method. # Use this method as a decorator @app.command("/echo") def repeat_text(ack, say, command): # Acknowledge command request ack() say(f"{command['text']}") # Pass a function to this method app.command("/echo")(repeat_text) Refer to https://docs.slack.dev/interactivity/implementing-slash-commands/ for details of Slash Commands. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: command: The conditions that match a request payload matchers: A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.command(command, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ ``` Registers a new slash command listener. This method can be used as either a decorator or a method. ``` # Use this method as a decorator @app.command("/echo") def repeat_text(ack, say, command): # Acknowledge command request ack() say(f"{command['text']}") # Pass a function to this method app.command("/echo")(repeat_text) ``` Refer to [https://docs.slack.dev/interactivity/implementing-slash-commands/](https://docs.slack.dev/interactivity/implementing-slash-commands/) for details of Slash Commands. To learn available arguments for middleware/listeners, see `[slack_bolt.kwargs_injection.args](kwargs_injection/args.html "slack_bolt.kwargs_injection.args")`'s API document. ## Args **`command`** The conditions that match a request payload **`matchers`** A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. **`middleware`** A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. `def default_app_uninstalled_event_listener(self) ‑> Callable[..., [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None]` Expand source code ``` def default_app_uninstalled_event_listener( self, ) -> Callable[..., Optional[BoltResponse]]: if self._tokens_revocation_listeners is None: raise BoltError(error_installation_store_required_for_builtin_listeners()) return self._tokens_revocation_listeners.handle_app_uninstalled_events ``` `def default_tokens_revoked_event_listener(self) ‑> Callable[..., [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None]` Expand source code ``` def default_tokens_revoked_event_listener( self, ) -> Callable[..., Optional[BoltResponse]]: if self._tokens_revocation_listeners is None: raise BoltError(error_installation_store_required_for_builtin_listeners()) return self._tokens_revocation_listeners.handle_tokens_revoked_events ``` `def dialog_cancellation(self, callback_id: str | Pattern, matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def dialog_cancellation( self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_cancellation` listener. Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.dialog_cancellation(callback_id, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ ``` Registers a new `dialog_cancellation` listener. Refer to [https://docs.slack.dev/legacy/legacy-dialogs/](https://docs.slack.dev/legacy/legacy-dialogs/) for details. `def dialog_submission(self, callback_id: str | Pattern, matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def dialog_submission( self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_submission` listener. Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.dialog_submission(callback_id, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ ``` Registers a new `dialog_submission` listener. Refer to [https://docs.slack.dev/legacy/legacy-dialogs/](https://docs.slack.dev/legacy/legacy-dialogs/) for details. `def dialog_suggestion(self, callback_id: str | Pattern, matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def dialog_suggestion( self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_suggestion` listener. Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.dialog_suggestion(callback_id, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ ``` Registers a new `dialog_suggestion` listener. Refer to [https://docs.slack.dev/legacy/legacy-dialogs/](https://docs.slack.dev/legacy/legacy-dialogs/) for details. `def dispatch(self, req: [BoltRequest](request/request.html#slack_bolt.request.request.BoltRequest "slack_bolt.request.request.BoltRequest")) ‑> [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse")` Expand source code ``` def dispatch(self, req: BoltRequest) -> BoltResponse: """Applies all middleware and dispatches an incoming request from Slack to the right code path. Args: req: An incoming request from Slack Returns: The response generated by this Bolt app """ starting_time = time.time() self._init_context(req) resp: Optional[BoltResponse] = BoltResponse(status=200, body="") middleware_state = {"next_called": False} def middleware_next(): middleware_state["next_called"] = True try: for middleware in self._middleware_list: middleware_state["next_called"] = False if self._framework_logger.level <= logging.DEBUG: self._framework_logger.debug(debug_applying_middleware(middleware.name)) resp = middleware.process(req=req, resp=resp, next=middleware_next) # type: ignore[arg-type] if not middleware_state["next_called"]: if resp is None: # next() method was not called without providing the response to return to Slack # This should not be an intentional handling in usual use cases. resp = BoltResponse(status=404, body={"error": "no next() calls in middleware"}) if self._raise_error_for_unhandled_request is True: try: raise BoltUnhandledRequestError( request=req, current_response=resp, last_global_middleware_name=middleware.name, ) except BoltUnhandledRequestError as e: self._listener_runner.listener_error_handler.handle( error=e, request=req, response=resp, ) return resp self._framework_logger.warning(warning_unhandled_by_global_middleware(middleware.name, req)) return resp return resp for listener in self._listeners: listener_name = get_name_for_callable(listener.ack_function) self._framework_logger.debug(debug_checking_listener(listener_name)) if listener.matches(req=req, resp=resp): # type: ignore[arg-type] # run all the middleware attached to this listener first middleware_resp, next_was_not_called = listener.run_middleware( req=req, resp=resp # type: ignore[arg-type] ) if next_was_not_called: if middleware_resp is not None: if self._framework_logger.level <= logging.DEBUG: debug_message = debug_return_listener_middleware_response( listener_name, middleware_resp.status, middleware_resp.body, starting_time, ) self._framework_logger.debug(debug_message) return middleware_resp # The last listener middleware didn't call next() method. # This means the listener is not for this incoming request. continue if middleware_resp is not None: resp = middleware_resp self._framework_logger.debug(debug_running_listener(listener_name)) listener_response: Optional[BoltResponse] = self._listener_runner.run( request=req, response=resp, # type: ignore[arg-type] listener_name=listener_name, listener=listener, ) if listener_response is not None: return listener_response if resp is None: resp = BoltResponse(status=404, body={"error": "unhandled request"}) if self._raise_error_for_unhandled_request is True: try: raise BoltUnhandledRequestError( request=req, current_response=resp, ) except BoltUnhandledRequestError as e: self._listener_runner.listener_error_handler.handle( error=e, request=req, response=resp, ) return resp return self._handle_unmatched_requests(req, resp) except Exception as error: resp = BoltResponse(status=500, body="") self._middleware_error_handler.handle( error=error, request=req, response=resp, ) return resp ``` Applies all middleware and dispatches an incoming request from Slack to the right code path. ## Args **`req`** An incoming request from Slack ## Returns The response generated by this Bolt app `def enable_token_revocation_listeners(self) ‑> None` Expand source code ``` def enable_token_revocation_listeners(self) -> None: self.event("tokens_revoked")(self.default_tokens_revoked_event_listener()) self.event("app_uninstalled")(self.default_app_uninstalled_event_listener()) ``` `def error(self, func: Callable[..., [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None]) ‑> Callable[..., [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None]` Expand source code ``` def error(self, func: Callable[..., Optional[BoltResponse]]) -> Callable[..., Optional[BoltResponse]]: """Updates the global error handler. This method can be used as either a decorator or a method. # Use this method as a decorator @app.error def custom_error_handler(error, body, logger): logger.exception(f"Error: {error}") logger.info(f"Request body: {body}") # Pass a function to this method app.error(custom_error_handler) To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: func: The function that is supposed to be executed when getting an unhandled error in Bolt app. """ self._listener_runner.listener_error_handler = CustomListenerErrorHandler( logger=self._framework_logger, func=func, ) self._middleware_error_handler = CustomMiddlewareErrorHandler( logger=self._framework_logger, func=func, ) return func ``` Updates the global error handler. This method can be used as either a decorator or a method. ``` # Use this method as a decorator @app.error def custom_error_handler(error, body, logger): logger.exception(f"Error: {error}") logger.info(f"Request body: {body}") # Pass a function to this method app.error(custom_error_handler) ``` To learn available arguments for middleware/listeners, see `[slack_bolt.kwargs_injection.args](kwargs_injection/args.html "slack_bolt.kwargs_injection.args")`'s API document. ## Args **`func`** The function that is supposed to be executed when getting an unhandled error in Bolt app. `def event(self, event: str | Pattern | Dict[str, str | Sequence[str | Pattern | None] | None], matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def event( self, event: Union[ str, Pattern, Dict[str, Optional[Union[str, Sequence[Optional[Union[str, Pattern]]]]]], ], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new event listener. This method can be used as either a decorator or a method. # Use this method as a decorator @app.event("team_join") def ask_for_introduction(event, say): welcome_channel_id = "C12345" user_id = event["user"] text = f"Welcome to the team, <@{user_id}>! :tada: You can introduce yourself in this channel." say(text=text, channel=welcome_channel_id) # Pass a function to this method app.event("team_join")(ask_for_introduction) Refer to https://docs.slack.dev/apis/events-api/ for details of Events API. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: event: The conditions that match a request payload. If you pass a dict for this, you can have type, subtype in the constraint. matchers: A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ middleware = list(middleware) if middleware else [] def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.event(event, base_logger=self._base_logger) if self._attaching_conversation_kwargs_enabled: middleware.insert(0, AttachingConversationKwargs(self._assistant_thread_context_store)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) return __call__ ``` Registers a new event listener. This method can be used as either a decorator or a method. ``` # Use this method as a decorator @app.event("team_join") def ask_for_introduction(event, say): welcome_channel_id = "C12345" user_id = event["user"] text = f"Welcome to the team, <@{user_id}>! :tada: You can introduce yourself in this channel." say(text=text, channel=welcome_channel_id) # Pass a function to this method app.event("team_join")(ask_for_introduction) ``` Refer to [https://docs.slack.dev/apis/events-api/](https://docs.slack.dev/apis/events-api/) for details of Events API. To learn available arguments for middleware/listeners, see `[slack_bolt.kwargs_injection.args](kwargs_injection/args.html "slack_bolt.kwargs_injection.args")`'s API document. ## Args **`event`** The conditions that match a request payload. If you pass a dict for this, you can have type, subtype in the constraint. **`matchers`** A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. **`middleware`** A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. `def function(self, callback_id: str | Pattern, matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None, auto_acknowledge: bool = True, ack_timeout: int = 3) ‑> Callable[..., Callable[..., [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def function( self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, auto_acknowledge: bool = True, ack_timeout: int = 3, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new Function listener. This method can be used as either a decorator or a method. # Use this method as a decorator @app.function("reverse") def reverse_string(ack: Ack, inputs: dict, complete: Complete, fail: Fail): try: ack() string_to_reverse = inputs["stringToReverse"] complete(outputs={"reverseString": string_to_reverse[::-1]}) except Exception as e: fail(f"Cannot reverse string (error: {e})") raise e # Pass a function to this method app.function("reverse")(reverse_string) To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: callback_id: The callback id to identify the function matchers: A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ if auto_acknowledge is True: if ack_timeout != 3: self._framework_logger.warning(warning_ack_timeout_has_no_effect(callback_id, ack_timeout)) matchers = list(matchers) if matchers else [] middleware = list(middleware) if middleware else [] def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.function_executed(callback_id=callback_id, base_logger=self._base_logger) return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge, ack_timeout) return __call__ ``` Registers a new Function listener. This method can be used as either a decorator or a method. ``` # Use this method as a decorator @app.function("reverse") def reverse_string(ack: Ack, inputs: dict, complete: Complete, fail: Fail): try: ack() string_to_reverse = inputs["stringToReverse"] complete(outputs={"reverseString": string_to_reverse[::-1]}) except Exception as e: fail(f"Cannot reverse string (error: {e})") raise e # Pass a function to this method app.function("reverse")(reverse_string) ``` To learn available arguments for middleware/listeners, see `[slack_bolt.kwargs_injection.args](kwargs_injection/args.html "slack_bolt.kwargs_injection.args")`'s API document. ## Args **`callback_id`** The callback id to identify the function **`matchers`** A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. **`middleware`** A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. `def global_shortcut(self, callback_id: str | Pattern, matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def global_shortcut( self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new global shortcut listener.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.global_shortcut(callback_id, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ ``` Registers a new global shortcut listener. `def message(self, keyword: str | Pattern = '', matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def message( self, keyword: Union[str, Pattern] = "", matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new message event listener. This method can be used as either a decorator or a method. Check the `App#event` method's docstring for details. # Use this method as a decorator @app.message(":wave:") def say_hello(message, say): user = message['user'] say(f"Hi there, <@{user}>!") # Pass a function to this method app.message(":wave:")(say_hello) Refer to https://docs.slack.dev/reference/events/message/ for details of `message` events. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: keyword: The keyword to match matchers: A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ matchers = list(matchers) if matchers else [] middleware = list(middleware) if middleware else [] def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) constraints = { "type": "message", "subtype": ( # In most cases, new message events come with no subtype. None, # As of Jan 2021, most bot messages no longer have the subtype bot_message. # By contrast, messages posted using classic app's bot token still have the subtype. "bot_message", # If an end-user posts a message with "Also send to #channel" checked, # the message event comes with this subtype. "thread_broadcast", # If an end-user posts a message with attached files, # the message event comes with this subtype. "file_share", ), } primary_matcher = builtin_matchers.message_event( keyword=keyword, constraints=constraints, base_logger=self._base_logger ) if self._attaching_conversation_kwargs_enabled: middleware.insert(0, AttachingConversationKwargs(self._assistant_thread_context_store)) middleware.insert(0, MessageListenerMatches(keyword)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) return __call__ ``` Registers a new message event listener. This method can be used as either a decorator or a method. Check the `App#event` method's docstring for details. ``` # Use this method as a decorator @app.message(":wave:") def say_hello(message, say): user = message['user'] say(f"Hi there, <@{user}>!") # Pass a function to this method app.message(":wave:")(say_hello) ``` Refer to [https://docs.slack.dev/reference/events/message/](https://docs.slack.dev/reference/events/message/) for details of `message` events. To learn available arguments for middleware/listeners, see `[slack_bolt.kwargs_injection.args](kwargs_injection/args.html "slack_bolt.kwargs_injection.args")`'s API document. ## Args **`keyword`** The keyword to match **`matchers`** A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. **`middleware`** A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. `def message_shortcut(self, callback_id: str | Pattern, matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def message_shortcut( self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new message shortcut listener.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.message_shortcut(callback_id, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ ``` Registers a new message shortcut listener. `def middleware(self, *args) ‑> Callable | None` Expand source code ``` def middleware(self, *args) -> Optional[Callable]: """Registers a new middleware to this app. This method can be used as either a decorator or a method. # Use this method as a decorator @app.middleware def middleware_func(logger, body, next): logger.info(f"request body: {body}") next() # Pass a function to this method app.middleware(middleware_func) Refer to https://docs.slack.dev/tools/bolt-python/concepts/global-middleware for details. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: *args: A function that works as a global middleware. """ if len(args) > 0: middleware_or_callable = args[0] if isinstance(middleware_or_callable, Middleware): middleware: Middleware = middleware_or_callable self._middleware_list.append(middleware) if isinstance(middleware, Assistant) and middleware.thread_context_store is not None: self._assistant_thread_context_store = middleware.thread_context_store elif callable(middleware_or_callable): self._middleware_list.append( CustomMiddleware( app_name=self.name, func=middleware_or_callable, base_logger=self._base_logger, ) ) return middleware_or_callable else: raise BoltError(f"Unexpected type for a middleware ({type(middleware_or_callable)})") return None ``` Registers a new middleware to this app. This method can be used as either a decorator or a method. ``` # Use this method as a decorator @app.middleware def middleware_func(logger, body, next): logger.info(f"request body: {body}") next() # Pass a function to this method app.middleware(middleware_func) ``` Refer to [https://docs.slack.dev/tools/bolt-python/concepts/global-middleware](https://docs.slack.dev/tools/bolt-python/concepts/global-middleware) for details. To learn available arguments for middleware/listeners, see `[slack_bolt.kwargs_injection.args](kwargs_injection/args.html "slack_bolt.kwargs_injection.args")`'s API document. ## Args **`*args`** A function that works as a global middleware. `def options(self, constraints: str | Pattern | Dict[str, str | Pattern], matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def options( self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new options listener. This method can be used as either a decorator or a method. # Use this method as a decorator @app.options("menu_selection") def show_menu_options(ack): options = [ { "text": {"type": "plain_text", "text": "Option 1"}, "value": "1-1", }, { "text": {"type": "plain_text", "text": "Option 2"}, "value": "1-2", }, ] ack(options=options) # Pass a function to this method app.options("menu_selection")(show_menu_options) Refer to the following documents for details: * https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element#external_select * https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: matchers: A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.options(constraints, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ ``` Registers a new options listener. This method can be used as either a decorator or a method. ``` # Use this method as a decorator @app.options("menu_selection") def show_menu_options(ack): options = [ { "text": {"type": "plain_text", "text": "Option 1"}, "value": "1-1", }, { "text": {"type": "plain_text", "text": "Option 2"}, "value": "1-2", }, ] ack(options=options) # Pass a function to this method app.options("menu_selection")(show_menu_options) ``` Refer to the following documents for details: * [https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element#external\_select](https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element#external_select) * [https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external\_multi\_select](https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select) To learn available arguments for middleware/listeners, see `[slack_bolt.kwargs_injection.args](kwargs_injection/args.html "slack_bolt.kwargs_injection.args")`'s API document. ## Args **`matchers`** A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. **`middleware`** A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. `def shortcut(self, constraints: str | Pattern | Dict[str, str | Pattern], matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def shortcut( self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new shortcut listener. This method can be used as either a decorator or a method. # Use this method as a decorator @app.shortcut("open_modal") def open_modal(ack, body, client): # Acknowledge the command request ack() # Call views_open with the built-in client client.views_open( # Pass a valid trigger_id within 3 seconds of receiving it trigger_id=body["trigger_id"], # View payload view={ ... } ) # Pass a function to this method app.shortcut("open_modal")(open_modal) Refer to https://docs.slack.dev/interactivity/implementing-shortcuts/ for details about Shortcuts. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: constraints: The conditions that match a request payload. matchers: A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.shortcut(constraints, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ ``` Registers a new shortcut listener. This method can be used as either a decorator or a method. ``` # Use this method as a decorator @app.shortcut("open_modal") def open_modal(ack, body, client): # Acknowledge the command request ack() # Call views_open with the built-in client client.views_open( # Pass a valid trigger_id within 3 seconds of receiving it trigger_id=body["trigger_id"], # View payload view={ ... } ) # Pass a function to this method app.shortcut("open_modal")(open_modal) ``` Refer to [https://docs.slack.dev/interactivity/implementing-shortcuts/](https://docs.slack.dev/interactivity/implementing-shortcuts/) for details about Shortcuts. To learn available arguments for middleware/listeners, see `[slack_bolt.kwargs_injection.args](kwargs_injection/args.html "slack_bolt.kwargs_injection.args")`'s API document. ## Args **`constraints`** The conditions that match a request payload. **`matchers`** A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. **`middleware`** A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. `def start(self, port: int = 3000, path: str = '/slack/events', http_server_logger_enabled: bool = True) ‑> None` Expand source code ``` def start( self, port: int = 3000, path: str = "/slack/events", http_server_logger_enabled: bool = True, ) -> None: """Starts a web server for local development. # With the default settings, `http://localhost:3000/slack/events` # is available for handling incoming requests from Slack app.start() This method internally starts a Web server process built with the `http.server` module. For production, consider using a production-ready WSGI server such as Gunicorn. Args: port: The port to listen on (Default: 3000) path: The path to handle request from Slack (Default: `/slack/events`) http_server_logger_enabled: The flag to enable http.server logging if True (Default: True) """ self._development_server = SlackAppDevelopmentServer( port=port, path=path, app=self, oauth_flow=self.oauth_flow, http_server_logger_enabled=http_server_logger_enabled, ) self._development_server.start() ``` Starts a web server for local development. ``` # With the default settings, `http://localhost:3000/slack/events` # is available for handling incoming requests from Slack app.start() ``` This method internally starts a Web server process built with the `http.server` module. For production, consider using a production-ready WSGI server such as Gunicorn. ## Args **`port`** The port to listen on (Default: 3000) **`path`** The path to handle request from Slack (Default: `/slack/events`) **`http_server_logger_enabled`** The flag to enable http.server logging if True (Default: True) `def step(self, callback_id: str | Pattern | [WorkflowStep](workflows/step/step.html#slack_bolt.workflows.step.step.WorkflowStep "slack_bolt.workflows.step.step.WorkflowStep") | [WorkflowStepBuilder](workflows/step/step.html#slack_bolt.workflows.step.step.WorkflowStepBuilder "slack_bolt.workflows.step.step.WorkflowStepBuilder"), edit: Callable[..., [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | [Listener](listener/listener.html#slack_bolt.listener.listener.Listener "slack_bolt.listener.listener.Listener") | Sequence[Callable] | None = None, save: Callable[..., [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | [Listener](listener/listener.html#slack_bolt.listener.listener.Listener "slack_bolt.listener.listener.Listener") | Sequence[Callable] | None = None, execute: Callable[..., [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | [Listener](listener/listener.html#slack_bolt.listener.listener.Listener "slack_bolt.listener.listener.Listener") | Sequence[Callable] | None = None)` Expand source code ``` def step( self, callback_id: Union[str, Pattern, WorkflowStep, WorkflowStepBuilder], edit: Optional[Union[Callable[..., Optional[BoltResponse]], Listener, Sequence[Callable]]] = None, save: Optional[Union[Callable[..., Optional[BoltResponse]], Listener, Sequence[Callable]]] = None, execute: Optional[Union[Callable[..., Optional[BoltResponse]], Listener, Sequence[Callable]]] = None, ): """ Deprecated: Steps from apps for legacy workflows are now deprecated. Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new step from app listener. Unlike others, this method doesn't behave as a decorator. If you want to register a step from app by a decorator, use `WorkflowStepBuilder`'s methods. # Create a new WorkflowStep instance from slack_bolt.workflows.step import WorkflowStep ws = WorkflowStep( callback_id="add_task", edit=edit, save=save, execute=execute, ) # Pass Step to set up listeners app.step(ws) Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. For further information about WorkflowStep specific function arguments such as `configure`, `update`, `complete`, and `fail`, refer to `slack_bolt.workflows.step.utilities` API documents. Args: callback_id: The Callback ID for this step from app edit: The function for displaying a modal in the Workflow Builder save: The function for handling configuration in the Workflow Builder execute: The function for handling the step execution """ warnings.warn( ( "Steps from apps for legacy workflows are now deprecated. " "Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/" ), category=DeprecationWarning, ) step = callback_id if isinstance(callback_id, (str, Pattern)): step = WorkflowStep( callback_id=callback_id, edit=edit, # type: ignore[arg-type] save=save, # type: ignore[arg-type] execute=execute, # type: ignore[arg-type] base_logger=self._base_logger, ) elif isinstance(step, WorkflowStepBuilder): step = step.build(base_logger=self._base_logger) elif not isinstance(step, WorkflowStep): raise BoltError(f"Invalid step object ({type(step)})") self.use(WorkflowStepMiddleware(step)) ``` ## Deprecated Steps from apps for legacy workflows are now deprecated. Use new custom steps: [https://docs.slack.dev/workflows/workflow-steps/](https://docs.slack.dev/workflows/workflow-steps/) Registers a new step from app listener. Unlike others, this method doesn't behave as a decorator. If you want to register a step from app by a decorator, use `WorkflowStepBuilder`'s methods. ``` # Create a new WorkflowStep instance from slack_bolt.workflows.step import WorkflowStep ws = WorkflowStep( callback_id="add_task", edit=edit, save=save, execute=execute, ) # Pass Step to set up listeners app.step(ws) ``` Refer to [https://docs.slack.dev/legacy/legacy-steps-from-apps/](https://docs.slack.dev/legacy/legacy-steps-from-apps/) for details of steps from apps. To learn available arguments for middleware/listeners, see `[slack_bolt.kwargs_injection.args](kwargs_injection/args.html "slack_bolt.kwargs_injection.args")`'s API document. For further information about WorkflowStep specific function arguments such as `configure`, `update`, `complete`, and `fail`, refer to `[slack_bolt.workflows.step.utilities](workflows/step/utilities/index.html "slack_bolt.workflows.step.utilities")` API documents. ## Args **`callback_id`** The Callback ID for this step from app **`edit`** The function for displaying a modal in the Workflow Builder **`save`** The function for handling configuration in the Workflow Builder **`execute`** The function for handling the step execution `def use(self, *args) ‑> Callable | None` Expand source code ``` def use(self, *args) -> Optional[Callable]: """Registers a new global middleware to this app. This method can be used as either a decorator or a method. Refer to `App#middleware()` method's docstring for details.""" return self.middleware(*args) ``` Registers a new global middleware to this app. This method can be used as either a decorator or a method. Refer to `App#middleware()` method's docstring for details. `def view(self, constraints: str | Pattern | Dict[str, str | Pattern], matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def view( self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_submission`/`view_closed` event listener. This method can be used as either a decorator or a method. # Use this method as a decorator @app.view("view_1") def handle_submission(ack, body, client, view): # Assume there's an input block with `block_c` as the block_id and `dreamy_input` hopes_and_dreams = view["state"]["values"]["block_c"]["dreamy_input"] user = body["user"]["id"] # Validate the inputs errors = {} if hopes_and_dreams is not None and len(hopes_and_dreams) <= 5: errors["block_c"] = "The value must be longer than 5 characters" if len(errors) > 0: ack(response_action="errors", errors=errors) return # Acknowledge the view_submission event and close the modal ack() # Do whatever you want with the input data - here we're saving it to a DB # Pass a function to this method app.view("view_1")(handle_submission) Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: constraints: The conditions that match a request payload matchers: A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.view(constraints, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ ``` Registers a new `view_submission`/`view_closed` event listener. This method can be used as either a decorator or a method. ``` # Use this method as a decorator @app.view("view_1") def handle_submission(ack, body, client, view): # Assume there's an input block with block\_c as the block_id and dreamy\_input hopes_and_dreams = view["state"]["values"]["block_c"]["dreamy_input"] user = body["user"]["id"] # Validate the inputs errors = {} if hopes_and_dreams is not None and len(hopes_and_dreams) <= 5: errors["block_c"] = "The value must be longer than 5 characters" if len(errors) > 0: ack(response_action="errors", errors=errors) return # Acknowledge the view_submission event and close the modal ack() # Do whatever you want with the input data - here we're saving it to a DB # Pass a function to this method app.view("view_1")(handle_submission) ``` Refer to [https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload](https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload) for details of payloads. To learn available arguments for middleware/listeners, see `[slack_bolt.kwargs_injection.args](kwargs_injection/args.html "slack_bolt.kwargs_injection.args")`'s API document. ## Args **`constraints`** The conditions that match a request payload **`matchers`** A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. **`middleware`** A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. `def view_closed(self, constraints: str | Pattern, matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def view_closed( self, constraints: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_closed` listener. Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_closed for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.view_closed(constraints, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ ``` Registers a new `view_closed` listener. Refer to [https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view\_closed](https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_closed) for details. `def view_submission(self, constraints: str | Pattern, matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def view_submission( self, constraints: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_submission` listener. Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details. """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.view_submission(constraints, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ ``` Registers a new `view_submission` listener. Refer to [https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view\_submission](https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission) for details. `class Args (*, logger: logging.Logger, client: slack_sdk.web.client.WebClient, req: [BoltRequest](request/request.html#slack_bolt.request.request.BoltRequest "slack_bolt.request.request.BoltRequest"), resp: [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse"), context: [BoltContext](context/context.html#slack_bolt.context.context.BoltContext "slack_bolt.context.context.BoltContext"), body: Dict[str, Any], payload: Dict[str, Any], options: Dict[str, Any] | None = None, shortcut: Dict[str, Any] | None = None, action: Dict[str, Any] | None = None, view: Dict[str, Any] | None = None, command: Dict[str, Any] | None = None, event: Dict[str, Any] | None = None, message: Dict[str, Any] | None = None, ack: [Ack](context/ack/ack.html#slack_bolt.context.ack.ack.Ack "slack_bolt.context.ack.ack.Ack"), say: [Say](context/say/say.html#slack_bolt.context.say.say.Say "slack_bolt.context.say.say.Say"), respond: [Respond](context/respond/respond.html#slack_bolt.context.respond.respond.Respond "slack_bolt.context.respond.respond.Respond"), complete: [Complete](context/complete/complete.html#slack_bolt.context.complete.complete.Complete "slack_bolt.context.complete.complete.Complete"), fail: [Fail](context/fail/fail.html#slack_bolt.context.fail.fail.Fail "slack_bolt.context.fail.fail.Fail"), set_status: [SetStatus](context/set_status/set_status.html#slack_bolt.context.set_status.set_status.SetStatus "slack_bolt.context.set_status.set_status.SetStatus") | None = None, set_title: [SetTitle](context/set_title/set_title.html#slack_bolt.context.set_title.set_title.SetTitle "slack_bolt.context.set_title.set_title.SetTitle") | None = None, set_suggested_prompts: [SetSuggestedPrompts](context/set_suggested_prompts/set_suggested_prompts.html#slack_bolt.context.set_suggested_prompts.set_suggested_prompts.SetSuggestedPrompts "slack_bolt.context.set_suggested_prompts.set_suggested_prompts.SetSuggestedPrompts") | None = None, get_thread_context: [GetThreadContext](context/get_thread_context/get_thread_context.html#slack_bolt.context.get_thread_context.get_thread_context.GetThreadContext "slack_bolt.context.get_thread_context.get_thread_context.GetThreadContext") | None = None, save_thread_context: [SaveThreadContext](context/save_thread_context/save_thread_context.html#slack_bolt.context.save_thread_context.save_thread_context.SaveThreadContext "slack_bolt.context.save_thread_context.save_thread_context.SaveThreadContext") | None = None, say_stream: [SayStream](context/say_stream/say_stream.html#slack_bolt.context.say_stream.say_stream.SayStream "slack_bolt.context.say_stream.say_stream.SayStream") | None = None, next: Callable[[], None], **kwargs)` Expand source code ``` class Args: """All the arguments in this class are available in any middleware / listeners. You can inject the named variables in the argument list in arbitrary order. @app.action("link_button") def handle_buttons(ack, respond, logger, context, body, client): logger.info(f"request body: {body}") ack() if context.channel_id is not None: respond("Hi!") client.views_open( trigger_id=body["trigger_id"], view={ ... } ) Alternatively, you can include a parameter named `args` and it will be injected with an instance of this class. @app.action("link_button") def handle_buttons(args): args.logger.info(f"request body: {args.body}") args.ack() if args.context.channel_id is not None: args.respond("Hi!") args.client.views_open( trigger_id=args.body["trigger_id"], view={ ... } ) """ client: WebClient """`slack_sdk.web.WebClient` instance with a valid token""" logger: Logger """Logger instance""" req: BoltRequest """Incoming request from Slack""" resp: BoltResponse """Response representation""" request: BoltRequest """Incoming request from Slack""" response: BoltResponse """Response representation""" context: BoltContext """Context data associated with the incoming request""" body: Dict[str, Any] """Parsed request body data""" # payload payload: Dict[str, Any] """The unwrapped core data in the request body""" options: Optional[Dict[str, Any]] # payload alias """An alias for payload in an `@app.options` listener""" shortcut: Optional[Dict[str, Any]] # payload alias """An alias for payload in an `@app.shortcut` listener""" action: Optional[Dict[str, Any]] # payload alias """An alias for payload in an `@app.action` listener""" view: Optional[Dict[str, Any]] # payload alias """An alias for payload in an `@app.view` listener""" command: Optional[Dict[str, Any]] # payload alias """An alias for payload in an `@app.command` listener""" event: Optional[Dict[str, Any]] # payload alias """An alias for payload in an `@app.event` listener""" message: Optional[Dict[str, Any]] # payload alias """An alias for payload in an `@app.message` listener""" # utilities ack: Ack """`ack()` utility function, which returns acknowledgement to the Slack servers""" say: Say """`say()` utility function, which calls `chat.postMessage` API with the associated channel ID""" respond: Respond """`respond()` utility function, which utilizes the associated `response_url`""" complete: Complete """`complete()` utility function, signals a successful completion of the custom function""" fail: Fail """`fail()` utility function, signal that the custom function failed to complete""" set_status: Optional[SetStatus] """`set_status()` utility function for AI Agents & Assistants""" set_title: Optional[SetTitle] """`set_title()` utility function for AI Agents & Assistants""" set_suggested_prompts: Optional[SetSuggestedPrompts] """`set_suggested_prompts()` utility function for AI Agents & Assistants""" get_thread_context: Optional[GetThreadContext] """`get_thread_context()` utility function for AI Agents & Assistants""" save_thread_context: Optional[SaveThreadContext] """`save_thread_context()` utility function for AI Agents & Assistants""" say_stream: Optional[SayStream] """`say_stream()` utility function for conversations, AI Agents & Assistants""" # middleware next: Callable[[], None] """`next()` utility function, which tells the middleware chain that it can continue with the next one""" next_: Callable[[], None] """An alias of `next()` for avoiding the Python built-in method overrides in middleware functions""" def __init__( self, *, logger: logging.Logger, client: WebClient, req: BoltRequest, resp: BoltResponse, context: BoltContext, body: Dict[str, Any], payload: Dict[str, Any], options: Optional[Dict[str, Any]] = None, shortcut: Optional[Dict[str, Any]] = None, action: Optional[Dict[str, Any]] = None, view: Optional[Dict[str, Any]] = None, command: Optional[Dict[str, Any]] = None, event: Optional[Dict[str, Any]] = None, message: Optional[Dict[str, Any]] = None, ack: Ack, say: Say, respond: Respond, complete: Complete, fail: Fail, set_status: Optional[SetStatus] = None, set_title: Optional[SetTitle] = None, set_suggested_prompts: Optional[SetSuggestedPrompts] = None, get_thread_context: Optional[GetThreadContext] = None, save_thread_context: Optional[SaveThreadContext] = None, say_stream: Optional[SayStream] = None, # As this method is not supposed to be invoked by bolt-python users, # the naming conflict with the built-in one affects # only the internals of this method next: Callable[[], None], **kwargs, # noqa ): self.logger: logging.Logger = logger self.client: WebClient = client self.request = self.req = req self.response = self.resp = resp self.context: BoltContext = context self.body: Dict[str, Any] = body self.payload: Dict[str, Any] = payload self.options: Optional[Dict[str, Any]] = options self.shortcut: Optional[Dict[str, Any]] = shortcut self.action: Optional[Dict[str, Any]] = action self.view: Optional[Dict[str, Any]] = view self.command: Optional[Dict[str, Any]] = command self.event: Optional[Dict[str, Any]] = event self.message: Optional[Dict[str, Any]] = message self.ack: Ack = ack self.say: Say = say self.respond: Respond = respond self.complete: Complete = complete self.fail: Fail = fail self.set_status = set_status self.set_title = set_title self.set_suggested_prompts = set_suggested_prompts self.get_thread_context = get_thread_context self.save_thread_context = save_thread_context self.say_stream = say_stream self.next: Callable[[], None] = next self.next_: Callable[[], None] = next ``` All the arguments in this class are available in any middleware / listeners. You can inject the named variables in the argument list in arbitrary order. ``` @app.action("link_button") def handle_buttons(ack, respond, logger, context, body, client): logger.info(f"request body: {body}") ack() if context.channel_id is not None: respond("Hi!") client.views_open( trigger_id=body["trigger_id"], view={ ... } ) ``` Alternatively, you can include a parameter named `args` and it will be injected with an instance of this class. ``` @app.action("link_button") def handle_buttons(args): args.logger.info(f"request body: {args.body}") args.ack() if args.context.channel_id is not None: args.respond("Hi!") args.client.views_open( trigger_id=args.body["trigger_id"], view={ ... } ) ``` ### Class variables `var ack : [Ack](context/ack/ack.html#slack_bolt.context.ack.ack.Ack "slack_bolt.context.ack.ack.Ack")` `ack()` utility function, which returns acknowledgement to the Slack servers `var action : Dict[str, Any] | None` An alias for payload in an `@app.action` listener `var body : Dict[str, Any]` Parsed request body data `var client : slack_sdk.web.client.WebClient` `slack_sdk.web.WebClient` instance with a valid token `var command : Dict[str, Any] | None` An alias for payload in an `@app.command` listener `var complete : [Complete](context/complete/complete.html#slack_bolt.context.complete.complete.Complete "slack_bolt.context.complete.complete.Complete")` `complete()` utility function, signals a successful completion of the custom function `var context : [BoltContext](context/context.html#slack_bolt.context.context.BoltContext "slack_bolt.context.context.BoltContext")` Context data associated with the incoming request `var event : Dict[str, Any] | None` An alias for payload in an `@app.event` listener `var fail : [Fail](context/fail/fail.html#slack_bolt.context.fail.fail.Fail "slack_bolt.context.fail.fail.Fail")` `fail()` utility function, signal that the custom function failed to complete `var get_thread_context : [GetThreadContext](context/get_thread_context/get_thread_context.html#slack_bolt.context.get_thread_context.get_thread_context.GetThreadContext "slack_bolt.context.get_thread_context.get_thread_context.GetThreadContext") | None` `get_thread_context()` utility function for AI Agents & Assistants `var logger : logging.Logger` Logger instance `var message : Dict[str, Any] | None` An alias for payload in an `@app.message` listener `var next : Callable[[], None]` `next()` utility function, which tells the middleware chain that it can continue with the next one `var next_ : Callable[[], None]` An alias of `next()` for avoiding the Python built-in method overrides in middleware functions `var options : Dict[str, Any] | None` An alias for payload in an `@app.options` listener `var payload : Dict[str, Any]` The unwrapped core data in the request body `var req : [BoltRequest](request/request.html#slack_bolt.request.request.BoltRequest "slack_bolt.request.request.BoltRequest")` Incoming request from Slack `var request : [BoltRequest](request/request.html#slack_bolt.request.request.BoltRequest "slack_bolt.request.request.BoltRequest")` Incoming request from Slack `var resp : [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse")` Response representation `var respond : [Respond](context/respond/respond.html#slack_bolt.context.respond.respond.Respond "slack_bolt.context.respond.respond.Respond")` `respond()` utility function, which utilizes the associated `response_url` `var response : [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse")` Response representation `var save_thread_context : [SaveThreadContext](context/save_thread_context/save_thread_context.html#slack_bolt.context.save_thread_context.save_thread_context.SaveThreadContext "slack_bolt.context.save_thread_context.save_thread_context.SaveThreadContext") | None` `save_thread_context()` utility function for AI Agents & Assistants `var say : [Say](context/say/say.html#slack_bolt.context.say.say.Say "slack_bolt.context.say.say.Say")` `say()` utility function, which calls `chat.postMessage` API with the associated channel ID `var say_stream : [SayStream](context/say_stream/say_stream.html#slack_bolt.context.say_stream.say_stream.SayStream "slack_bolt.context.say_stream.say_stream.SayStream") | None` `say_stream()` utility function for conversations, AI Agents & Assistants `var set_status : [SetStatus](context/set_status/set_status.html#slack_bolt.context.set_status.set_status.SetStatus "slack_bolt.context.set_status.set_status.SetStatus") | None` `set_status()` utility function for AI Agents & Assistants `var set_suggested_prompts : [SetSuggestedPrompts](context/set_suggested_prompts/set_suggested_prompts.html#slack_bolt.context.set_suggested_prompts.set_suggested_prompts.SetSuggestedPrompts "slack_bolt.context.set_suggested_prompts.set_suggested_prompts.SetSuggestedPrompts") | None` `set_suggested_prompts()` utility function for AI Agents & Assistants `var set_title : [SetTitle](context/set_title/set_title.html#slack_bolt.context.set_title.set_title.SetTitle "slack_bolt.context.set_title.set_title.SetTitle") | None` `set_title()` utility function for AI Agents & Assistants `var shortcut : Dict[str, Any] | None` An alias for payload in an `@app.shortcut` listener `var view : Dict[str, Any] | None` An alias for payload in an `@app.view` listener `class Assistant (*, app_name: str = 'assistant', thread_context_store: [AssistantThreadContextStore](context/assistant/thread_context_store/store.html#slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore "slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore") | None = None, logger: logging.Logger | None = None)` Expand source code ``` class Assistant(Middleware): _thread_started_listeners: Optional[List[Listener]] _thread_context_changed_listeners: Optional[List[Listener]] _user_message_listeners: Optional[List[Listener]] _bot_message_listeners: Optional[List[Listener]] thread_context_store: Optional[AssistantThreadContextStore] base_logger: Optional[logging.Logger] def __init__( self, *, app_name: str = "assistant", thread_context_store: Optional[AssistantThreadContextStore] = None, logger: Optional[logging.Logger] = None, ): self.app_name = app_name self.thread_context_store = thread_context_store self.base_logger = logger self._thread_started_listeners = None self._thread_context_changed_listeners = None self._user_message_listeners = None self._bot_message_listeners = None def thread_started( self, *args, matchers: Optional[Union[Callable[..., bool], ListenerMatcher]] = None, middleware: Optional[Union[Callable, Middleware]] = None, lazy: Optional[List[Callable[..., None]]] = None, ): if self._thread_started_listeners is None: self._thread_started_listeners = [] all_matchers = self._merge_matchers(is_assistant_thread_started_event, matchers) if is_used_without_argument(args): func = args[0] self._thread_started_listeners.append( self.build_listener( listener_or_functions=func, matchers=all_matchers, middleware=middleware, # type: ignore[arg-type] ) ) return func def _inner(func): functions = [func] + (lazy if lazy is not None else []) self._thread_started_listeners.append( self.build_listener( listener_or_functions=functions, matchers=all_matchers, middleware=middleware, ) ) @wraps(func) def _wrapper(*args, **kwargs): return func(*args, **kwargs) return _wrapper return _inner def user_message( self, *args, matchers: Optional[Union[Callable[..., bool], ListenerMatcher]] = None, middleware: Optional[Union[Callable, Middleware]] = None, lazy: Optional[List[Callable[..., None]]] = None, ): if self._user_message_listeners is None: self._user_message_listeners = [] all_matchers = self._merge_matchers(is_user_message_event_in_assistant_thread, matchers) if is_used_without_argument(args): func = args[0] self._user_message_listeners.append( self.build_listener( listener_or_functions=func, matchers=all_matchers, middleware=middleware, # type: ignore[arg-type] ) ) return func def _inner(func): functions = [func] + (lazy if lazy is not None else []) self._user_message_listeners.append( self.build_listener( listener_or_functions=functions, matchers=all_matchers, middleware=middleware, ) ) @wraps(func) def _wrapper(*args, **kwargs): return func(*args, **kwargs) return _wrapper return _inner def bot_message( self, *args, matchers: Optional[Union[Callable[..., bool], ListenerMatcher]] = None, middleware: Optional[Union[Callable, Middleware]] = None, lazy: Optional[List[Callable[..., None]]] = None, ): if self._bot_message_listeners is None: self._bot_message_listeners = [] all_matchers = self._merge_matchers(is_bot_message_event_in_assistant_thread, matchers) if is_used_without_argument(args): func = args[0] self._bot_message_listeners.append( self.build_listener( listener_or_functions=func, matchers=all_matchers, middleware=middleware, # type: ignore[arg-type] ) ) return func def _inner(func): functions = [func] + (lazy if lazy is not None else []) self._bot_message_listeners.append( self.build_listener( listener_or_functions=functions, matchers=all_matchers, middleware=middleware, ) ) @wraps(func) def _wrapper(*args, **kwargs): return func(*args, **kwargs) return _wrapper return _inner def thread_context_changed( self, *args, matchers: Optional[Union[Callable[..., bool], ListenerMatcher]] = None, middleware: Optional[Union[Callable, Middleware]] = None, lazy: Optional[List[Callable[..., None]]] = None, ): if self._thread_context_changed_listeners is None: self._thread_context_changed_listeners = [] all_matchers = self._merge_matchers(is_assistant_thread_context_changed_event, matchers) if is_used_without_argument(args): func = args[0] self._thread_context_changed_listeners.append( self.build_listener( listener_or_functions=func, matchers=all_matchers, middleware=middleware, # type: ignore[arg-type] ) ) return func def _inner(func): functions = [func] + (lazy if lazy is not None else []) self._thread_context_changed_listeners.append( self.build_listener( listener_or_functions=functions, matchers=all_matchers, middleware=middleware, ) ) @wraps(func) def _wrapper(*args, **kwargs): return func(*args, **kwargs) return _wrapper return _inner def _merge_matchers( self, primary_matcher: Callable[..., bool], custom_matchers: Optional[Union[Callable[..., bool], ListenerMatcher]], ): return [CustomListenerMatcher(app_name=self.app_name, func=primary_matcher)] + ( custom_matchers or [] ) # type: ignore[operator] @staticmethod def default_thread_context_changed(save_thread_context: SaveThreadContext, payload: dict): save_thread_context(payload["assistant_thread"]["context"]) def process( # type: ignore[return] self, *, req: BoltRequest, resp: BoltResponse, next: Callable[[], BoltResponse] ) -> Optional[BoltResponse]: if self._thread_context_changed_listeners is None: self.thread_context_changed(self.default_thread_context_changed) listener_runner: ThreadListenerRunner = req.context.listener_runner for listeners in [ self._thread_started_listeners, self._thread_context_changed_listeners, self._user_message_listeners, self._bot_message_listeners, ]: if listeners is not None: for listener in listeners: if listener.matches(req=req, resp=resp): middleware_resp, next_was_not_called = listener.run_middleware(req=req, resp=resp) if next_was_not_called: if middleware_resp is not None: return middleware_resp # The listener middleware didn't call next(). # Skip this listener and try the next one. continue if middleware_resp is not None: resp = middleware_resp return listener_runner.run( request=req, response=resp, listener_name="assistant_listener", listener=listener, ) if is_other_message_sub_event_in_assistant_thread(req.body): # message_changed, message_deleted, etc. return req.context.ack() next() def build_listener( self, listener_or_functions: Union[Listener, Callable, List[Callable]], matchers: Optional[List[Union[ListenerMatcher, Callable[..., bool]]]] = None, middleware: Optional[List[Middleware]] = None, base_logger: Optional[Logger] = None, ) -> Listener: if isinstance(listener_or_functions, Callable): # type: ignore[arg-type] listener_or_functions = [listener_or_functions] # type: ignore[list-item] if isinstance(listener_or_functions, Listener): return listener_or_functions elif isinstance(listener_or_functions, list): middleware = middleware if middleware else [] middleware.insert(0, AttachingConversationKwargs(self.thread_context_store)) functions = listener_or_functions ack_function = functions.pop(0) matchers = matchers if matchers else [] listener_matchers: List[ListenerMatcher] = [] for matcher in matchers: if isinstance(matcher, ListenerMatcher): listener_matchers.append(matcher) elif isinstance(matcher, Callable): # type: ignore[arg-type] listener_matchers.append( build_listener_matcher( func=matcher, asyncio=False, base_logger=base_logger, ) ) return CustomListener( app_name=self.app_name, matchers=listener_matchers, middleware=middleware, ack_function=ack_function, lazy_functions=functions, auto_acknowledgement=True, base_logger=base_logger or self.base_logger, ) else: raise BoltError(f"Invalid listener: {type(listener_or_functions)} detected") ``` A middleware can process request data before other middleware and listener functions. ### Ancestors * [Middleware](middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware") ### Class variables `var base_logger : logging.Logger | None` The type of the None singleton. `var thread_context_store : [AssistantThreadContextStore](context/assistant/thread_context_store/store.html#slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore "slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore") | None` The type of the None singleton. ### Static methods `def default_thread_context_changed(save_thread_context: [SaveThreadContext](context/save_thread_context/save_thread_context.html#slack_bolt.context.save_thread_context.save_thread_context.SaveThreadContext "slack_bolt.context.save_thread_context.save_thread_context.SaveThreadContext"), payload: dict)` Expand source code ``` @staticmethod def default_thread_context_changed(save_thread_context: SaveThreadContext, payload: dict): save_thread_context(payload["assistant_thread"]["context"]) ``` ### Methods `def bot_message(self, *args, matchers: Callable[..., bool] | [ListenerMatcher](listener_matcher/listener_matcher.html#slack_bolt.listener_matcher.listener_matcher.ListenerMatcher "slack_bolt.listener_matcher.listener_matcher.ListenerMatcher") | None = None, middleware: Callable | [Middleware](middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware") | None = None, lazy: List[Callable[..., None]] | None = None)` Expand source code ``` def bot_message( self, *args, matchers: Optional[Union[Callable[..., bool], ListenerMatcher]] = None, middleware: Optional[Union[Callable, Middleware]] = None, lazy: Optional[List[Callable[..., None]]] = None, ): if self._bot_message_listeners is None: self._bot_message_listeners = [] all_matchers = self._merge_matchers(is_bot_message_event_in_assistant_thread, matchers) if is_used_without_argument(args): func = args[0] self._bot_message_listeners.append( self.build_listener( listener_or_functions=func, matchers=all_matchers, middleware=middleware, # type: ignore[arg-type] ) ) return func def _inner(func): functions = [func] + (lazy if lazy is not None else []) self._bot_message_listeners.append( self.build_listener( listener_or_functions=functions, matchers=all_matchers, middleware=middleware, ) ) @wraps(func) def _wrapper(*args, **kwargs): return func(*args, **kwargs) return _wrapper return _inner ``` `def build_listener(self, listener_or_functions: [Listener](listener/listener.html#slack_bolt.listener.listener.Listener "slack_bolt.listener.listener.Listener") | Callable | List[Callable], matchers: List[[ListenerMatcher](listener_matcher/listener_matcher.html#slack_bolt.listener_matcher.listener_matcher.ListenerMatcher "slack_bolt.listener_matcher.listener_matcher.ListenerMatcher") | Callable[..., bool]] | None = None, middleware: List[[Middleware](middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None, base_logger: logging.Logger | None = None) ‑> [Listener](listener/listener.html#slack_bolt.listener.listener.Listener "slack_bolt.listener.listener.Listener")` Expand source code ``` def build_listener( self, listener_or_functions: Union[Listener, Callable, List[Callable]], matchers: Optional[List[Union[ListenerMatcher, Callable[..., bool]]]] = None, middleware: Optional[List[Middleware]] = None, base_logger: Optional[Logger] = None, ) -> Listener: if isinstance(listener_or_functions, Callable): # type: ignore[arg-type] listener_or_functions = [listener_or_functions] # type: ignore[list-item] if isinstance(listener_or_functions, Listener): return listener_or_functions elif isinstance(listener_or_functions, list): middleware = middleware if middleware else [] middleware.insert(0, AttachingConversationKwargs(self.thread_context_store)) functions = listener_or_functions ack_function = functions.pop(0) matchers = matchers if matchers else [] listener_matchers: List[ListenerMatcher] = [] for matcher in matchers: if isinstance(matcher, ListenerMatcher): listener_matchers.append(matcher) elif isinstance(matcher, Callable): # type: ignore[arg-type] listener_matchers.append( build_listener_matcher( func=matcher, asyncio=False, base_logger=base_logger, ) ) return CustomListener( app_name=self.app_name, matchers=listener_matchers, middleware=middleware, ack_function=ack_function, lazy_functions=functions, auto_acknowledgement=True, base_logger=base_logger or self.base_logger, ) else: raise BoltError(f"Invalid listener: {type(listener_or_functions)} detected") ``` `def thread_context_changed(self, *args, matchers: Callable[..., bool] | [ListenerMatcher](listener_matcher/listener_matcher.html#slack_bolt.listener_matcher.listener_matcher.ListenerMatcher "slack_bolt.listener_matcher.listener_matcher.ListenerMatcher") | None = None, middleware: Callable | [Middleware](middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware") | None = None, lazy: List[Callable[..., None]] | None = None)` Expand source code ``` def thread_context_changed( self, *args, matchers: Optional[Union[Callable[..., bool], ListenerMatcher]] = None, middleware: Optional[Union[Callable, Middleware]] = None, lazy: Optional[List[Callable[..., None]]] = None, ): if self._thread_context_changed_listeners is None: self._thread_context_changed_listeners = [] all_matchers = self._merge_matchers(is_assistant_thread_context_changed_event, matchers) if is_used_without_argument(args): func = args[0] self._thread_context_changed_listeners.append( self.build_listener( listener_or_functions=func, matchers=all_matchers, middleware=middleware, # type: ignore[arg-type] ) ) return func def _inner(func): functions = [func] + (lazy if lazy is not None else []) self._thread_context_changed_listeners.append( self.build_listener( listener_or_functions=functions, matchers=all_matchers, middleware=middleware, ) ) @wraps(func) def _wrapper(*args, **kwargs): return func(*args, **kwargs) return _wrapper return _inner ``` `def thread_started(self, *args, matchers: Callable[..., bool] | [ListenerMatcher](listener_matcher/listener_matcher.html#slack_bolt.listener_matcher.listener_matcher.ListenerMatcher "slack_bolt.listener_matcher.listener_matcher.ListenerMatcher") | None = None, middleware: Callable | [Middleware](middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware") | None = None, lazy: List[Callable[..., None]] | None = None)` Expand source code ``` def thread_started( self, *args, matchers: Optional[Union[Callable[..., bool], ListenerMatcher]] = None, middleware: Optional[Union[Callable, Middleware]] = None, lazy: Optional[List[Callable[..., None]]] = None, ): if self._thread_started_listeners is None: self._thread_started_listeners = [] all_matchers = self._merge_matchers(is_assistant_thread_started_event, matchers) if is_used_without_argument(args): func = args[0] self._thread_started_listeners.append( self.build_listener( listener_or_functions=func, matchers=all_matchers, middleware=middleware, # type: ignore[arg-type] ) ) return func def _inner(func): functions = [func] + (lazy if lazy is not None else []) self._thread_started_listeners.append( self.build_listener( listener_or_functions=functions, matchers=all_matchers, middleware=middleware, ) ) @wraps(func) def _wrapper(*args, **kwargs): return func(*args, **kwargs) return _wrapper return _inner ``` `def user_message(self, *args, matchers: Callable[..., bool] | [ListenerMatcher](listener_matcher/listener_matcher.html#slack_bolt.listener_matcher.listener_matcher.ListenerMatcher "slack_bolt.listener_matcher.listener_matcher.ListenerMatcher") | None = None, middleware: Callable | [Middleware](middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware") | None = None, lazy: List[Callable[..., None]] | None = None)` Expand source code ``` def user_message( self, *args, matchers: Optional[Union[Callable[..., bool], ListenerMatcher]] = None, middleware: Optional[Union[Callable, Middleware]] = None, lazy: Optional[List[Callable[..., None]]] = None, ): if self._user_message_listeners is None: self._user_message_listeners = [] all_matchers = self._merge_matchers(is_user_message_event_in_assistant_thread, matchers) if is_used_without_argument(args): func = args[0] self._user_message_listeners.append( self.build_listener( listener_or_functions=func, matchers=all_matchers, middleware=middleware, # type: ignore[arg-type] ) ) return func def _inner(func): functions = [func] + (lazy if lazy is not None else []) self._user_message_listeners.append( self.build_listener( listener_or_functions=functions, matchers=all_matchers, middleware=middleware, ) ) @wraps(func) def _wrapper(*args, **kwargs): return func(*args, **kwargs) return _wrapper return _inner ``` ### Inherited members * `**[Middleware](middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")**`: * `[name](middleware/middleware.html#slack_bolt.middleware.middleware.Middleware.name "slack_bolt.middleware.middleware.Middleware.name")` * `[process](middleware/middleware.html#slack_bolt.middleware.middleware.Middleware.process "slack_bolt.middleware.middleware.Middleware.process")` `class AssistantThreadContext (payload: dict)` Expand source code ``` class AssistantThreadContext(dict): enterprise_id: Optional[str] team_id: Optional[str] channel_id: str def __init__(self, payload: dict): dict.__init__(self, **payload) self.enterprise_id = payload.get("enterprise_id") self.team_id = payload.get("team_id") self.channel_id = payload["channel_id"] ``` dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d\[k\] = v dict(\*\*kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2) ### Ancestors * builtins.dict ### Class variables `var channel_id : str` The type of the None singleton. `var enterprise_id : str | None` The type of the None singleton. `var team_id : str | None` The type of the None singleton. `class AssistantThreadContextStore` Expand source code ``` class AssistantThreadContextStore: def save(self, *, channel_id: str, thread_ts: str, context: Dict[str, str]) -> None: raise NotImplementedError() def find(self, *, channel_id: str, thread_ts: str) -> Optional[AssistantThreadContext]: raise NotImplementedError() ``` ### Subclasses * [DefaultAssistantThreadContextStore](context/assistant/thread_context_store/default_store.html#slack_bolt.context.assistant.thread_context_store.default_store.DefaultAssistantThreadContextStore "slack_bolt.context.assistant.thread_context_store.default_store.DefaultAssistantThreadContextStore") * [FileAssistantThreadContextStore](context/assistant/thread_context_store/file/index.html#slack_bolt.context.assistant.thread_context_store.file.FileAssistantThreadContextStore "slack_bolt.context.assistant.thread_context_store.file.FileAssistantThreadContextStore") ### Methods `def find(self, *, channel_id: str, thread_ts: str) ‑> [AssistantThreadContext](context/assistant/thread_context/index.html#slack_bolt.context.assistant.thread_context.AssistantThreadContext "slack_bolt.context.assistant.thread_context.AssistantThreadContext") | None` Expand source code ``` def find(self, *, channel_id: str, thread_ts: str) -> Optional[AssistantThreadContext]: raise NotImplementedError() ``` `def save(self, *, channel_id: str, thread_ts: str, context: Dict[str, str]) ‑> None` Expand source code ``` def save(self, *, channel_id: str, thread_ts: str, context: Dict[str, str]) -> None: raise NotImplementedError() ``` `class BoltContext (*args, **kwargs)` Expand source code ``` class BoltContext(BaseContext): """Context object associated with a request from Slack.""" def to_copyable(self) -> "BoltContext": new_dict = {} for prop_name, prop_value in self.items(): if prop_name in self.copyable_standard_property_names: # all the standard properties are copiable new_dict[prop_name] = prop_value elif prop_name in self.non_copyable_standard_property_names: # Do nothing with this property (e.g., listener_runner) continue else: try: copied_value = create_copy(prop_value) new_dict[prop_name] = copied_value except TypeError as te: self.logger.warning( f"Skipped setting '{prop_name}' to a copied request for lazy listeners " "due to a deep-copy creation error. Consider passing the value not as part of context object " f"(error: {te})" ) return BoltContext(new_dict) # The return type is intentionally string to avoid circular imports @property def listener_runner(self) -> "ThreadListenerRunner": """The properly configured listener_runner that is available for middleware/listeners.""" return self["listener_runner"] @property def client(self) -> WebClient: """The `WebClient` instance available for this request. @app.event("app_mention") def handle_events(context): context.client.chat_postMessage( channel=context.channel_id, text="Thanks!", ) # You can access "client" this way too. @app.event("app_mention") def handle_events(client, context): client.chat_postMessage( channel=context.channel_id, text="Thanks!", ) Returns: `WebClient` instance """ if "client" not in self: self["client"] = WebClient(token=None) return self["client"] @property def ack(self) -> Ack: """`ack()` function for this request. @app.action("button") def handle_button_clicks(context): context.ack() # You can access "ack" this way too. @app.action("button") def handle_button_clicks(ack): ack() Returns: Callable `ack()` function """ if "ack" not in self: self["ack"] = Ack() return self["ack"] @property def say(self) -> Say: """`say()` function for this request. @app.action("button") def handle_button_clicks(context): context.ack() context.say("Hi!") # You can access "ack" this way too. @app.action("button") def handle_button_clicks(ack, say): ack() say("Hi!") Returns: Callable `say()` function """ if "say" not in self: self["say"] = Say(client=self.client, channel=self.channel_id) return self["say"] @property def respond(self) -> Optional[Respond]: """`respond()` function for this request. @app.action("button") def handle_button_clicks(context): context.ack() context.respond("Hi!") # You can access "ack" this way too. @app.action("button") def handle_button_clicks(ack, respond): ack() respond("Hi!") Returns: Callable `respond()` function """ if "respond" not in self: self["respond"] = Respond( response_url=self.response_url, proxy=self.client.proxy, ssl=self.client.ssl, ) return self["respond"] @property def complete(self) -> Complete: """`complete()` function for this request. Once a custom function's state is set to complete, any outputs the function returns will be passed along to the next step of its housing workflow, or complete the workflow if the function is the last step in a workflow. Additionally, any interactivity handlers associated to a function invocation will no longer be invocable. @app.function("reverse") def handle_button_clicks(ack, complete): ack() complete(outputs={"stringReverse":"olleh"}) @app.function("reverse") def handle_button_clicks(context): context.ack() context.complete(outputs={"stringReverse":"olleh"}) Returns: Callable `complete()` function """ if "complete" not in self: self["complete"] = Complete(client=self.client, function_execution_id=self.function_execution_id) return self["complete"] @property def fail(self) -> Fail: """`fail()` function for this request. Once a custom function's state is set to error, its housing workflow will be interrupted and any provided error message will be passed on to the end user through SlackBot. Additionally, any interactivity handlers associated to a function invocation will no longer be invocable. @app.function("reverse") def handle_button_clicks(ack, fail): ack() fail(error="something went wrong") @app.function("reverse") def handle_button_clicks(context): context.ack() context.fail(error="something went wrong") Returns: Callable `fail()` function """ if "fail" not in self: self["fail"] = Fail(client=self.client, function_execution_id=self.function_execution_id) return self["fail"] @property def set_title(self) -> Optional[SetTitle]: return self.get("set_title") @property def set_status(self) -> Optional[SetStatus]: return self.get("set_status") @property def set_suggested_prompts(self) -> Optional[SetSuggestedPrompts]: return self.get("set_suggested_prompts") @property def get_thread_context(self) -> Optional[GetThreadContext]: return self.get("get_thread_context") @property def say_stream(self) -> Optional[SayStream]: return self.get("say_stream") @property def save_thread_context(self) -> Optional[SaveThreadContext]: return self.get("save_thread_context") ``` Context object associated with a request from Slack. ### Ancestors * [BaseContext](context/base_context.html#slack_bolt.context.base_context.BaseContext "slack_bolt.context.base_context.BaseContext") * builtins.dict ### Instance variables `prop ack : [Ack](context/ack/ack.html#slack_bolt.context.ack.ack.Ack "slack_bolt.context.ack.ack.Ack")` Expand source code ``` @property def ack(self) -> Ack: """`ack()` function for this request. @app.action("button") def handle_button_clicks(context): context.ack() # You can access "ack" this way too. @app.action("button") def handle_button_clicks(ack): ack() Returns: Callable `ack()` function """ if "ack" not in self: self["ack"] = Ack() return self["ack"] ``` `ack()` function for this request. ``` @app.action("button") def handle_button_clicks(context): context.ack() # You can access "ack" this way too. @app.action("button") def handle_button_clicks(ack): ack() ``` ## Returns Callable `ack()` function `prop client : slack_sdk.web.client.WebClient` Expand source code ``` @property def client(self) -> WebClient: """The `WebClient` instance available for this request. @app.event("app_mention") def handle_events(context): context.client.chat_postMessage( channel=context.channel_id, text="Thanks!", ) # You can access "client" this way too. @app.event("app_mention") def handle_events(client, context): client.chat_postMessage( channel=context.channel_id, text="Thanks!", ) Returns: `WebClient` instance """ if "client" not in self: self["client"] = WebClient(token=None) return self["client"] ``` The `WebClient` instance available for this request. ``` @app.event("app_mention") def handle_events(context): context.client.chat_postMessage( channel=context.channel_id, text="Thanks!", ) # You can access "client" this way too. @app.event("app_mention") def handle_events(client, context): client.chat_postMessage( channel=context.channel_id, text="Thanks!", ) ``` ## Returns `WebClient` instance `prop complete : [Complete](context/complete/complete.html#slack_bolt.context.complete.complete.Complete "slack_bolt.context.complete.complete.Complete")` Expand source code ``` @property def complete(self) -> Complete: """`complete()` function for this request. Once a custom function's state is set to complete, any outputs the function returns will be passed along to the next step of its housing workflow, or complete the workflow if the function is the last step in a workflow. Additionally, any interactivity handlers associated to a function invocation will no longer be invocable. @app.function("reverse") def handle_button_clicks(ack, complete): ack() complete(outputs={"stringReverse":"olleh"}) @app.function("reverse") def handle_button_clicks(context): context.ack() context.complete(outputs={"stringReverse":"olleh"}) Returns: Callable `complete()` function """ if "complete" not in self: self["complete"] = Complete(client=self.client, function_execution_id=self.function_execution_id) return self["complete"] ``` `complete()` function for this request. Once a custom function's state is set to complete, any outputs the function returns will be passed along to the next step of its housing workflow, or complete the workflow if the function is the last step in a workflow. Additionally, any interactivity handlers associated to a function invocation will no longer be invocable. ``` @app.function("reverse") def handle_button_clicks(ack, complete): ack() complete(outputs={"stringReverse":"olleh"}) @app.function("reverse") def handle_button_clicks(context): context.ack() context.complete(outputs={"stringReverse":"olleh"}) ``` ## Returns Callable `complete()` function `prop fail : [Fail](context/fail/fail.html#slack_bolt.context.fail.fail.Fail "slack_bolt.context.fail.fail.Fail")` Expand source code ``` @property def fail(self) -> Fail: """`fail()` function for this request. Once a custom function's state is set to error, its housing workflow will be interrupted and any provided error message will be passed on to the end user through SlackBot. Additionally, any interactivity handlers associated to a function invocation will no longer be invocable. @app.function("reverse") def handle_button_clicks(ack, fail): ack() fail(error="something went wrong") @app.function("reverse") def handle_button_clicks(context): context.ack() context.fail(error="something went wrong") Returns: Callable `fail()` function """ if "fail" not in self: self["fail"] = Fail(client=self.client, function_execution_id=self.function_execution_id) return self["fail"] ``` `fail()` function for this request. Once a custom function's state is set to error, its housing workflow will be interrupted and any provided error message will be passed on to the end user through SlackBot. Additionally, any interactivity handlers associated to a function invocation will no longer be invocable. ``` @app.function("reverse") def handle_button_clicks(ack, fail): ack() fail(error="something went wrong") @app.function("reverse") def handle_button_clicks(context): context.ack() context.fail(error="something went wrong") ``` ## Returns Callable `fail()` function `prop get_thread_context : [GetThreadContext](context/get_thread_context/get_thread_context.html#slack_bolt.context.get_thread_context.get_thread_context.GetThreadContext "slack_bolt.context.get_thread_context.get_thread_context.GetThreadContext") | None` Expand source code ``` @property def get_thread_context(self) -> Optional[GetThreadContext]: return self.get("get_thread_context") ``` `prop listener_runner : ThreadListenerRunner` Expand source code ``` @property def listener_runner(self) -> "ThreadListenerRunner": """The properly configured listener_runner that is available for middleware/listeners.""" return self["listener_runner"] ``` The properly configured listener\_runner that is available for middleware/listeners. `prop respond : [Respond](context/respond/respond.html#slack_bolt.context.respond.respond.Respond "slack_bolt.context.respond.respond.Respond") | None` Expand source code ``` @property def respond(self) -> Optional[Respond]: """`respond()` function for this request. @app.action("button") def handle_button_clicks(context): context.ack() context.respond("Hi!") # You can access "ack" this way too. @app.action("button") def handle_button_clicks(ack, respond): ack() respond("Hi!") Returns: Callable `respond()` function """ if "respond" not in self: self["respond"] = Respond( response_url=self.response_url, proxy=self.client.proxy, ssl=self.client.ssl, ) return self["respond"] ``` `respond()` function for this request. ``` @app.action("button") def handle_button_clicks(context): context.ack() context.respond("Hi!") # You can access "ack" this way too. @app.action("button") def handle_button_clicks(ack, respond): ack() respond("Hi!") ``` ## Returns Callable `respond()` function `prop save_thread_context : [SaveThreadContext](context/save_thread_context/save_thread_context.html#slack_bolt.context.save_thread_context.save_thread_context.SaveThreadContext "slack_bolt.context.save_thread_context.save_thread_context.SaveThreadContext") | None` Expand source code ``` @property def save_thread_context(self) -> Optional[SaveThreadContext]: return self.get("save_thread_context") ``` `prop say : [Say](context/say/say.html#slack_bolt.context.say.say.Say "slack_bolt.context.say.say.Say")` Expand source code ``` @property def say(self) -> Say: """`say()` function for this request. @app.action("button") def handle_button_clicks(context): context.ack() context.say("Hi!") # You can access "ack" this way too. @app.action("button") def handle_button_clicks(ack, say): ack() say("Hi!") Returns: Callable `say()` function """ if "say" not in self: self["say"] = Say(client=self.client, channel=self.channel_id) return self["say"] ``` `say()` function for this request. ``` @app.action("button") def handle_button_clicks(context): context.ack() context.say("Hi!") # You can access "ack" this way too. @app.action("button") def handle_button_clicks(ack, say): ack() say("Hi!") ``` ## Returns Callable `say()` function `prop say_stream : [SayStream](context/say_stream/say_stream.html#slack_bolt.context.say_stream.say_stream.SayStream "slack_bolt.context.say_stream.say_stream.SayStream") | None` Expand source code ``` @property def say_stream(self) -> Optional[SayStream]: return self.get("say_stream") ``` `prop set_status : [SetStatus](context/set_status/set_status.html#slack_bolt.context.set_status.set_status.SetStatus "slack_bolt.context.set_status.set_status.SetStatus") | None` Expand source code ``` @property def set_status(self) -> Optional[SetStatus]: return self.get("set_status") ``` `prop set_suggested_prompts : [SetSuggestedPrompts](context/set_suggested_prompts/set_suggested_prompts.html#slack_bolt.context.set_suggested_prompts.set_suggested_prompts.SetSuggestedPrompts "slack_bolt.context.set_suggested_prompts.set_suggested_prompts.SetSuggestedPrompts") | None` Expand source code ``` @property def set_suggested_prompts(self) -> Optional[SetSuggestedPrompts]: return self.get("set_suggested_prompts") ``` `prop set_title : [SetTitle](context/set_title/set_title.html#slack_bolt.context.set_title.set_title.SetTitle "slack_bolt.context.set_title.set_title.SetTitle") | None` Expand source code ``` @property def set_title(self) -> Optional[SetTitle]: return self.get("set_title") ``` ### Methods `def to_copyable(self) ‑> [BoltContext](context/context.html#slack_bolt.context.context.BoltContext "slack_bolt.context.context.BoltContext")` Expand source code ``` def to_copyable(self) -> "BoltContext": new_dict = {} for prop_name, prop_value in self.items(): if prop_name in self.copyable_standard_property_names: # all the standard properties are copiable new_dict[prop_name] = prop_value elif prop_name in self.non_copyable_standard_property_names: # Do nothing with this property (e.g., listener_runner) continue else: try: copied_value = create_copy(prop_value) new_dict[prop_name] = copied_value except TypeError as te: self.logger.warning( f"Skipped setting '{prop_name}' to a copied request for lazy listeners " "due to a deep-copy creation error. Consider passing the value not as part of context object " f"(error: {te})" ) return BoltContext(new_dict) ``` ### Inherited members * `**[BaseContext](context/base_context.html#slack_bolt.context.base_context.BaseContext "slack_bolt.context.base_context.BaseContext")**`: * `[actor_enterprise_id](context/base_context.html#slack_bolt.context.base_context.BaseContext.actor_enterprise_id "slack_bolt.context.base_context.BaseContext.actor_enterprise_id")` * `[actor_team_id](context/base_context.html#slack_bolt.context.base_context.BaseContext.actor_team_id "slack_bolt.context.base_context.BaseContext.actor_team_id")` * `[actor_user_id](context/base_context.html#slack_bolt.context.base_context.BaseContext.actor_user_id "slack_bolt.context.base_context.BaseContext.actor_user_id")` * `[authorize_result](context/base_context.html#slack_bolt.context.base_context.BaseContext.authorize_result "slack_bolt.context.base_context.BaseContext.authorize_result")` * `[bot_id](context/base_context.html#slack_bolt.context.base_context.BaseContext.bot_id "slack_bolt.context.base_context.BaseContext.bot_id")` * `[bot_token](context/base_context.html#slack_bolt.context.base_context.BaseContext.bot_token "slack_bolt.context.base_context.BaseContext.bot_token")` * `[bot_user_id](context/base_context.html#slack_bolt.context.base_context.BaseContext.bot_user_id "slack_bolt.context.base_context.BaseContext.bot_user_id")` * `[channel_id](context/base_context.html#slack_bolt.context.base_context.BaseContext.channel_id "slack_bolt.context.base_context.BaseContext.channel_id")` * `[copyable_standard_property_names](context/base_context.html#slack_bolt.context.base_context.BaseContext.copyable_standard_property_names "slack_bolt.context.base_context.BaseContext.copyable_standard_property_names")` * `[enterprise_id](context/base_context.html#slack_bolt.context.base_context.BaseContext.enterprise_id "slack_bolt.context.base_context.BaseContext.enterprise_id")` * `[function_bot_access_token](context/base_context.html#slack_bolt.context.base_context.BaseContext.function_bot_access_token "slack_bolt.context.base_context.BaseContext.function_bot_access_token")` * `[function_execution_id](context/base_context.html#slack_bolt.context.base_context.BaseContext.function_execution_id "slack_bolt.context.base_context.BaseContext.function_execution_id")` * `[inputs](context/base_context.html#slack_bolt.context.base_context.BaseContext.inputs "slack_bolt.context.base_context.BaseContext.inputs")` * `[is_enterprise_install](context/base_context.html#slack_bolt.context.base_context.BaseContext.is_enterprise_install "slack_bolt.context.base_context.BaseContext.is_enterprise_install")` * `[logger](context/base_context.html#slack_bolt.context.base_context.BaseContext.logger "slack_bolt.context.base_context.BaseContext.logger")` * `[matches](context/base_context.html#slack_bolt.context.base_context.BaseContext.matches "slack_bolt.context.base_context.BaseContext.matches")` * `[non_copyable_standard_property_names](context/base_context.html#slack_bolt.context.base_context.BaseContext.non_copyable_standard_property_names "slack_bolt.context.base_context.BaseContext.non_copyable_standard_property_names")` * `[response_url](context/base_context.html#slack_bolt.context.base_context.BaseContext.response_url "slack_bolt.context.base_context.BaseContext.response_url")` * `[standard_property_names](context/base_context.html#slack_bolt.context.base_context.BaseContext.standard_property_names "slack_bolt.context.base_context.BaseContext.standard_property_names")` * `[team_id](context/base_context.html#slack_bolt.context.base_context.BaseContext.team_id "slack_bolt.context.base_context.BaseContext.team_id")` * `[thread_ts](context/base_context.html#slack_bolt.context.base_context.BaseContext.thread_ts "slack_bolt.context.base_context.BaseContext.thread_ts")` * `[token](context/base_context.html#slack_bolt.context.base_context.BaseContext.token "slack_bolt.context.base_context.BaseContext.token")` * `[user_id](context/base_context.html#slack_bolt.context.base_context.BaseContext.user_id "slack_bolt.context.base_context.BaseContext.user_id")` * `[user_token](context/base_context.html#slack_bolt.context.base_context.BaseContext.user_token "slack_bolt.context.base_context.BaseContext.user_token")` `class BoltRequest (*, body: str | dict, query: str | Dict[str, str] | Dict[str, Sequence[str]] | None = None, headers: Dict[str, str | Sequence[str]] | None = None, context: Dict[str, Any] | None = None, mode: str = 'http')` Expand source code ``` class BoltRequest: raw_body: str query: Dict[str, Sequence[str]] headers: Dict[str, Sequence[str]] content_type: Optional[str] body: Dict[str, Any] context: BoltContext lazy_only: bool lazy_function_name: Optional[str] mode: str # either "http" or "socket_mode" def __init__( self, *, body: Union[str, dict], query: Optional[Union[str, Dict[str, str], Dict[str, Sequence[str]]]] = None, headers: Optional[Dict[str, Union[str, Sequence[str]]]] = None, context: Optional[Dict[str, Any]] = None, mode: str = "http", # either "http" or "socket_mode" ): """Request to a Bolt app. Args: body: The raw request body (only plain text is supported for "http" mode) query: The query string data in any data format. headers: The request headers. context: The context in this request. mode: The mode used for this request. (either "http" or "socket_mode") """ if mode == "http": # HTTP Mode if body is not None and not isinstance(body, str): raise BoltError(error_message_raw_body_required_in_http_mode()) self.raw_body = body if body is not None else "" else: # Socket Mode if body is not None and isinstance(body, str): self.raw_body = body else: # We don't convert the dict value to str # as doing so does not guarantee to keep the original structure/format. self.raw_body = "" self.query = parse_query(query) self.headers = build_normalized_headers(headers) self.content_type = extract_content_type(self.headers) if isinstance(body, str): self.body = parse_body(self.raw_body, self.content_type) elif isinstance(body, dict): self.body = body else: self.body = {} self.context = build_context(BoltContext(context if context else {}), self.body) self.lazy_only = bool(self.headers.get("x-slack-bolt-lazy-only", [False])[0]) self.lazy_function_name = self.headers.get("x-slack-bolt-lazy-function-name", [None])[0] self.mode = mode def to_copyable(self) -> "BoltRequest": body: Union[str, dict] = self.raw_body if self.mode == "http" else self.body return BoltRequest( body=body, query=self.query, headers=self.headers, context=self.context.to_copyable(), mode=self.mode, ) ``` Request to a Bolt app. ## Args **`body`** The raw request body (only plain text is supported for "http" mode) **`query`** The query string data in any data format. **`headers`** The request headers. **`context`** The context in this request. **`mode`** The mode used for this request. (either "http" or "socket\_mode") ### Class variables `var body : Dict[str, Any]` The type of the None singleton. `var content_type : str | None` The type of the None singleton. `var context : [BoltContext](context/context.html#slack_bolt.context.context.BoltContext "slack_bolt.context.context.BoltContext")` The type of the None singleton. `var headers : Dict[str, Sequence[str]]` The type of the None singleton. `var lazy_function_name : str | None` The type of the None singleton. `var lazy_only : bool` The type of the None singleton. `var mode : str` The type of the None singleton. `var query : Dict[str, Sequence[str]]` The type of the None singleton. `var raw_body : str` The type of the None singleton. ### Methods `def to_copyable(self) ‑> [BoltRequest](request/request.html#slack_bolt.request.request.BoltRequest "slack_bolt.request.request.BoltRequest")` Expand source code ``` def to_copyable(self) -> "BoltRequest": body: Union[str, dict] = self.raw_body if self.mode == "http" else self.body return BoltRequest( body=body, query=self.query, headers=self.headers, context=self.context.to_copyable(), mode=self.mode, ) ``` `class BoltResponse (*, status: int, body: str | dict = '', headers: Dict[str, str | Sequence[str]] | None = None)` Expand source code ``` class BoltResponse: status: int body: str headers: Dict[str, Sequence[str]] def __init__( self, *, status: int, body: Union[str, dict] = "", headers: Optional[Dict[str, Union[str, Sequence[str]]]] = None, ): """The response from a Bolt app. Args: status: HTTP status code body: The response body (dict and str are supported) headers: The response headers. """ self.status: int = status self.body: str = json.dumps(body) if isinstance(body, dict) else body self.headers: Dict[str, Sequence[str]] = {} if headers is not None: for name, value in headers.items(): if value is None: continue if isinstance(value, list): self.headers[name.lower()] = value elif isinstance(value, set): self.headers[name.lower()] = list(value) else: self.headers[name.lower()] = [str(value)] if "content-type" not in self.headers.keys(): if self.body and self.body.startswith("{"): self.headers["content-type"] = ["application/json;charset=utf-8"] else: self.headers["content-type"] = ["text/plain;charset=utf-8"] def first_headers(self) -> Dict[str, str]: return {k: list(v)[0] for k, v in self.headers.items()} def first_headers_without_set_cookie(self) -> Dict[str, str]: return {k: list(v)[0] for k, v in self.headers.items() if k != "set-cookie"} def cookies(self) -> Sequence[SimpleCookie]: header_values = self.headers.get("set-cookie", []) return [self._to_simple_cookie(v) for v in header_values] @staticmethod def _to_simple_cookie(header_value: str) -> SimpleCookie: c = SimpleCookie() c.load(header_value) return c ``` The response from a Bolt app. ## Args **`status`** HTTP status code **`body`** The response body (dict and str are supported) **`headers`** The response headers. ### Class variables `var body : str` The type of the None singleton. `var headers : Dict[str, Sequence[str]]` The type of the None singleton. `var status : int` The type of the None singleton. ### Methods `def cookies(self) ‑> Sequence[http.cookies.SimpleCookie]` Expand source code ``` def cookies(self) -> Sequence[SimpleCookie]: header_values = self.headers.get("set-cookie", []) return [self._to_simple_cookie(v) for v in header_values] ``` `def first_headers(self) ‑> Dict[str, str]` Expand source code ``` def first_headers(self) -> Dict[str, str]: return {k: list(v)[0] for k, v in self.headers.items()} ``` `def first_headers_without_set_cookie(self) ‑> Dict[str, str]` Expand source code ``` def first_headers_without_set_cookie(self) -> Dict[str, str]: return {k: list(v)[0] for k, v in self.headers.items() if k != "set-cookie"} ``` `class Complete (client: slack_sdk.web.client.WebClient, function_execution_id: str | None)` Expand source code ``` class Complete: client: WebClient function_execution_id: Optional[str] _called: bool def __init__( self, client: WebClient, function_execution_id: Optional[str], ): self.client = client self.function_execution_id = function_execution_id self._called = False def __call__(self, outputs: Optional[Dict[str, Any]] = None) -> SlackResponse: """Signal the successful completion of the custom function. Kwargs: outputs: Json serializable object containing the output values Returns: SlackResponse: The response object returned from slack Raises: ValueError: If this function cannot be used. """ if self.function_execution_id is None: raise ValueError("complete is unsupported here as there is no function_execution_id") self._called = True return self.client.functions_completeSuccess(function_execution_id=self.function_execution_id, outputs=outputs or {}) def has_been_called(self) -> bool: """Check if this complete function has been called. Returns: bool: True if the complete function has been called, False otherwise. """ return self._called ``` ### Class variables `var client : slack_sdk.web.client.WebClient` The type of the None singleton. `var function_execution_id : str | None` The type of the None singleton. ### Methods `def has_been_called(self) ‑> bool` Expand source code ``` def has_been_called(self) -> bool: """Check if this complete function has been called. Returns: bool: True if the complete function has been called, False otherwise. """ return self._called ``` Check if this complete function has been called. ## Returns `bool` True if the complete function has been called, False otherwise. `class CustomListenerMatcher (*, app_name: str, func: Callable[..., bool], base_logger: logging.Logger | None = None)` Expand source code ``` class CustomListenerMatcher(ListenerMatcher): app_name: str func: Callable[..., bool] arg_names: MutableSequence[str] logger: Logger def __init__(self, *, app_name: str, func: Callable[..., bool], base_logger: Optional[Logger] = None): self.app_name = app_name self.func = func self.arg_names = get_arg_names_of_callable(func) self.logger = get_bolt_app_logger(self.app_name, self.func, base_logger) def matches(self, req: BoltRequest, resp: BoltResponse) -> bool: return self.func( **build_required_kwargs( logger=self.logger, required_arg_names=self.arg_names, request=req, response=resp, this_func=self.func, ) ) ``` ### Ancestors * [ListenerMatcher](listener_matcher/listener_matcher.html#slack_bolt.listener_matcher.listener_matcher.ListenerMatcher "slack_bolt.listener_matcher.listener_matcher.ListenerMatcher") ### Class variables `var app_name : str` The type of the None singleton. `var arg_names : MutableSequence[str]` The type of the None singleton. `var func : Callable[..., bool]` The type of the None singleton. `var logger : logging.Logger` The type of the None singleton. ### Inherited members * `**[ListenerMatcher](listener_matcher/listener_matcher.html#slack_bolt.listener_matcher.listener_matcher.ListenerMatcher "slack_bolt.listener_matcher.listener_matcher.ListenerMatcher")**`: * `[matches](listener_matcher/listener_matcher.html#slack_bolt.listener_matcher.listener_matcher.ListenerMatcher.matches "slack_bolt.listener_matcher.listener_matcher.ListenerMatcher.matches")` `class Fail (client: slack_sdk.web.client.WebClient, function_execution_id: str | None)` Expand source code ``` class Fail: client: WebClient function_execution_id: Optional[str] _called: bool def __init__( self, client: WebClient, function_execution_id: Optional[str], ): self.client = client self.function_execution_id = function_execution_id self._called = False def __call__(self, error: str) -> SlackResponse: """Signal that the custom function failed to complete. Kwargs: error: Error message to return to slack Returns: SlackResponse: The response object returned from slack Raises: ValueError: If this function cannot be used. """ if self.function_execution_id is None: raise ValueError("fail is unsupported here as there is no function_execution_id") self._called = True return self.client.functions_completeError(function_execution_id=self.function_execution_id, error=error) def has_been_called(self) -> bool: """Check if this fail function has been called. Returns: bool: True if the fail function has been called, False otherwise. """ return self._called ``` ### Class variables `var client : slack_sdk.web.client.WebClient` The type of the None singleton. `var function_execution_id : str | None` The type of the None singleton. ### Methods `def has_been_called(self) ‑> bool` Expand source code ``` def has_been_called(self) -> bool: """Check if this fail function has been called. Returns: bool: True if the fail function has been called, False otherwise. """ return self._called ``` Check if this fail function has been called. ## Returns `bool` True if the fail function has been called, False otherwise. `class FileAssistantThreadContextStore (base_dir: str = '/Users/eden.zimbelman/.bolt-app-assistant-thread-contexts')` Expand source code ``` class FileAssistantThreadContextStore(AssistantThreadContextStore): def __init__( self, base_dir: str = str(Path.home()) + "/.bolt-app-assistant-thread-contexts", ): self.base_dir = base_dir self._mkdir(self.base_dir) def save(self, *, channel_id: str, thread_ts: str, context: Dict[str, str]) -> None: path = f"{self.base_dir}/{channel_id}-{thread_ts}.json" with open(path, "w") as f: f.write(json.dumps(context)) def find(self, *, channel_id: str, thread_ts: str) -> Optional[AssistantThreadContext]: path = f"{self.base_dir}/{channel_id}-{thread_ts}.json" try: with open(path) as f: data = json.loads(f.read()) if data.get("channel_id") is not None: return AssistantThreadContext(data) except FileNotFoundError: pass return None @staticmethod def _mkdir(path: Union[str, Path]): if isinstance(path, str): path = Path(path) path.mkdir(parents=True, exist_ok=True) ``` ### Ancestors * [AssistantThreadContextStore](context/assistant/thread_context_store/store.html#slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore "slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore") ### Methods `def find(self, *, channel_id: str, thread_ts: str) ‑> [AssistantThreadContext](context/assistant/thread_context/index.html#slack_bolt.context.assistant.thread_context.AssistantThreadContext "slack_bolt.context.assistant.thread_context.AssistantThreadContext") | None` Expand source code ``` def find(self, *, channel_id: str, thread_ts: str) -> Optional[AssistantThreadContext]: path = f"{self.base_dir}/{channel_id}-{thread_ts}.json" try: with open(path) as f: data = json.loads(f.read()) if data.get("channel_id") is not None: return AssistantThreadContext(data) except FileNotFoundError: pass return None ``` `def save(self, *, channel_id: str, thread_ts: str, context: Dict[str, str]) ‑> None` Expand source code ``` def save(self, *, channel_id: str, thread_ts: str, context: Dict[str, str]) -> None: path = f"{self.base_dir}/{channel_id}-{thread_ts}.json" with open(path, "w") as f: f.write(json.dumps(context)) ``` `class Listener` Expand source code ``` class Listener(metaclass=ABCMeta): matchers: Sequence[ListenerMatcher] middleware: Sequence[Middleware] ack_function: Callable[..., BoltResponse] lazy_functions: Sequence[Callable[..., None]] auto_acknowledgement: bool ack_timeout: int = 3 def matches( self, *, req: BoltRequest, resp: BoltResponse, ) -> bool: is_matched: bool = False for matcher in self.matchers: is_matched = matcher.matches(req, resp) if not is_matched: return is_matched return is_matched def run_middleware( self, *, req: BoltRequest, resp: BoltResponse, ) -> Tuple[Optional[BoltResponse], bool]: """Runs a middleware. Args: req: The incoming request resp: The current response Returns: A tuple of the processed response and a flag indicating termination """ for m in self.middleware: middleware_state = {"next_called": False} def next_(): middleware_state["next_called"] = True resp = m.process(req=req, resp=resp, next=next_) # type: ignore[assignment] if not middleware_state["next_called"]: # next() was not called in this middleware return (resp, True) return (resp, False) @abstractmethod def run_ack_function(self, *, request: BoltRequest, response: BoltResponse) -> Optional[BoltResponse]: """Runs all the registered middleware and then run the listener function. Args: request: The incoming request response: The current response Returns: The processed response """ raise NotImplementedError() ``` ### Subclasses * [CustomListener](listener/custom_listener.html#slack_bolt.listener.custom_listener.CustomListener "slack_bolt.listener.custom_listener.CustomListener") ### Class variables `var ack_function : Callable[..., [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse")]` The type of the None singleton. `var ack_timeout : int` The type of the None singleton. `var auto_acknowledgement : bool` The type of the None singleton. `var lazy_functions : Sequence[Callable[..., None]]` The type of the None singleton. `var matchers : Sequence[[ListenerMatcher](listener_matcher/listener_matcher.html#slack_bolt.listener_matcher.listener_matcher.ListenerMatcher "slack_bolt.listener_matcher.listener_matcher.ListenerMatcher")]` The type of the None singleton. `var middleware : Sequence[[Middleware](middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")]` The type of the None singleton. ### Methods `def matches(self, *, req: [BoltRequest](request/request.html#slack_bolt.request.request.BoltRequest "slack_bolt.request.request.BoltRequest"), resp: [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse")) ‑> bool` Expand source code ``` def matches( self, *, req: BoltRequest, resp: BoltResponse, ) -> bool: is_matched: bool = False for matcher in self.matchers: is_matched = matcher.matches(req, resp) if not is_matched: return is_matched return is_matched ``` `def run_ack_function(self, *, request: [BoltRequest](request/request.html#slack_bolt.request.request.BoltRequest "slack_bolt.request.request.BoltRequest"), response: [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse")) ‑> [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None` Expand source code ``` @abstractmethod def run_ack_function(self, *, request: BoltRequest, response: BoltResponse) -> Optional[BoltResponse]: """Runs all the registered middleware and then run the listener function. Args: request: The incoming request response: The current response Returns: The processed response """ raise NotImplementedError() ``` Runs all the registered middleware and then run the listener function. ## Args **`request`** The incoming request **`response`** The current response ## Returns The processed response `def run_middleware(self, *, req: [BoltRequest](request/request.html#slack_bolt.request.request.BoltRequest "slack_bolt.request.request.BoltRequest"), resp: [BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse")) ‑> Tuple[[BoltResponse](response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None, bool]` Expand source code ``` def run_middleware( self, *, req: BoltRequest, resp: BoltResponse, ) -> Tuple[Optional[BoltResponse], bool]: """Runs a middleware. Args: req: The incoming request resp: The current response Returns: A tuple of the processed response and a flag indicating termination """ for m in self.middleware: middleware_state = {"next_called": False} def next_(): middleware_state["next_called"] = True resp = m.process(req=req, resp=resp, next=next_) # type: ignore[assignment] if not middleware_state["next_called"]: # next() was not called in this middleware return (resp, True) return (resp, False) ``` Runs a middleware. ## Args **`req`** The incoming request **`resp`** The current response ## Returns A tuple of the processed response and a flag indicating termination `class Respond (*, response_url: str | None, proxy: str | None = None, ssl: ssl.SSLContext | None = None)` Expand source code ``` class Respond: response_url: Optional[str] proxy: Optional[str] ssl: Optional[SSLContext] def __init__( self, *, response_url: Optional[str], proxy: Optional[str] = None, ssl: Optional[SSLContext] = None, ): self.response_url = response_url self.proxy = proxy self.ssl = ssl def __call__( self, text: Union[str, dict] = "", blocks: Optional[Sequence[Union[dict, Block]]] = None, attachments: Optional[Sequence[Union[dict, Attachment]]] = None, 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, thread_ts: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None, ) -> WebhookResponse: if self.response_url is not None: client = WebhookClient( url=self.response_url, proxy=self.proxy, ssl=self.ssl, ) text_or_whole_response: Union[str, dict] = text if isinstance(text_or_whole_response, str): text = text_or_whole_response message = _build_message( text=text, blocks=blocks, attachments=attachments, response_type=response_type, replace_original=replace_original, delete_original=delete_original, unfurl_links=unfurl_links, unfurl_media=unfurl_media, thread_ts=thread_ts, metadata=metadata, ) return client.send_dict(message) elif isinstance(text_or_whole_response, dict): message = _build_message(**text_or_whole_response) return client.send_dict(message) else: raise ValueError(f"The arg is unexpected type ({type(text_or_whole_response)})") else: raise ValueError("respond is unsupported here as there is no response_url") ``` ### Class variables `var proxy : str | None` The type of the None singleton. `var response_url : str | None` The type of the None singleton. `var ssl : ssl.SSLContext | None` The type of the None singleton. `class SaveThreadContext (thread_context_store: [AssistantThreadContextStore](context/assistant/thread_context_store/store.html#slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore "slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore"), channel_id: str, thread_ts: str)` Expand source code ``` class SaveThreadContext: thread_context_store: AssistantThreadContextStore channel_id: str thread_ts: str def __init__( self, thread_context_store: AssistantThreadContextStore, channel_id: str, thread_ts: str, ): self.thread_context_store = thread_context_store self.channel_id = channel_id self.thread_ts = thread_ts def __call__(self, new_context: Dict[str, str]) -> None: self.thread_context_store.save( channel_id=self.channel_id, thread_ts=self.thread_ts, context=new_context, ) ``` ### Class variables `var channel_id : str` The type of the None singleton. `var thread_context_store : [AssistantThreadContextStore](context/assistant/thread_context_store/store.html#slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore "slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore")` The type of the None singleton. `var thread_ts : str` The type of the None singleton. `class Say (client: slack_sdk.web.client.WebClient | None, channel: str | None, thread_ts: str | None = None, metadata: Dict | slack_sdk.models.metadata.Metadata | None = None, build_metadata: Callable[[], Dict | slack_sdk.models.metadata.Metadata | None] | None = None)` Expand source code ``` class Say: client: Optional[WebClient] channel: Optional[str] thread_ts: Optional[str] metadata: Optional[Union[Dict, Metadata]] build_metadata: Optional[Callable[[], Optional[Union[Dict, Metadata]]]] def __init__( self, client: Optional[WebClient], channel: Optional[str], thread_ts: Optional[str] = None, metadata: Optional[Union[Dict, Metadata]] = None, build_metadata: Optional[Callable[[], Optional[Union[Dict, Metadata]]]] = None, ): self.client = client self.channel = channel self.thread_ts = thread_ts self.metadata = metadata self.build_metadata = build_metadata def __call__( self, text: Union[str, dict] = "", blocks: Optional[Sequence[Union[Dict, Block]]] = None, attachments: Optional[Sequence[Union[Dict, Attachment]]] = None, channel: Optional[str] = None, as_user: Optional[bool] = None, thread_ts: Optional[str] = None, reply_broadcast: Optional[bool] = None, unfurl_links: Optional[bool] = None, unfurl_media: Optional[bool] = None, icon_emoji: Optional[str] = None, icon_url: Optional[str] = None, username: Optional[str] = None, markdown_text: Optional[str] = None, mrkdwn: Optional[bool] = None, link_names: Optional[bool] = None, parse: Optional[str] = None, # none, full metadata: Optional[Union[Dict, Metadata]] = None, **kwargs, ) -> SlackResponse: if _can_say(self, channel): text_or_whole_response: Union[str, dict] = text if isinstance(text_or_whole_response, str): text = text_or_whole_response if metadata is None: metadata = self.build_metadata() if self.build_metadata is not None else self.metadata return self.client.chat_postMessage( # type: ignore[union-attr] channel=channel or self.channel, # type: ignore[arg-type] text=text, blocks=blocks, attachments=attachments, as_user=as_user, thread_ts=thread_ts or self.thread_ts, reply_broadcast=reply_broadcast, unfurl_links=unfurl_links, unfurl_media=unfurl_media, icon_emoji=icon_emoji, icon_url=icon_url, username=username, markdown_text=markdown_text, mrkdwn=mrkdwn, link_names=link_names, parse=parse, metadata=metadata, **kwargs, ) elif isinstance(text_or_whole_response, dict): message: dict = create_copy(text_or_whole_response) if "channel" not in message: message["channel"] = channel or self.channel if "thread_ts" not in message: message["thread_ts"] = thread_ts or self.thread_ts if "metadata" not in message: metadata = self.build_metadata() if self.build_metadata is not None else self.metadata message["metadata"] = metadata return self.client.chat_postMessage(**message) # type: ignore[union-attr] else: raise ValueError(f"The arg is unexpected type ({type(text_or_whole_response)})") else: raise ValueError("say without channel_id here is unsupported") ``` ### Class variables `var build_metadata : Callable[[], Dict | slack_sdk.models.metadata.Metadata | None] | None` The type of the None singleton. `var channel : str | None` The type of the None singleton. `var client : slack_sdk.web.client.WebClient | None` The type of the None singleton. `var metadata : Dict | slack_sdk.models.metadata.Metadata | None` The type of the None singleton. `var thread_ts : str | None` The type of the None singleton. `class SayStream (*, client: slack_sdk.web.client.WebClient, channel: str | None = None, recipient_team_id: str | None = None, recipient_user_id: str | None = None, thread_ts: str | None = None)` Expand source code ``` class SayStream: client: WebClient channel: Optional[str] recipient_team_id: Optional[str] recipient_user_id: Optional[str] thread_ts: Optional[str] def __init__( self, *, client: WebClient, channel: Optional[str] = None, recipient_team_id: Optional[str] = None, recipient_user_id: Optional[str] = None, thread_ts: Optional[str] = None, ): self.client = client self.channel = channel self.recipient_team_id = recipient_team_id self.recipient_user_id = recipient_user_id self.thread_ts = thread_ts def __call__( self, *, buffer_size: Optional[int] = None, channel: Optional[str] = None, recipient_team_id: Optional[str] = None, recipient_user_id: Optional[str] = None, thread_ts: Optional[str] = None, **kwargs, ) -> ChatStream: """Starts a new chat stream with context.""" channel = channel or self.channel thread_ts = thread_ts or self.thread_ts if channel is None: raise ValueError("say_stream without channel here is unsupported") if thread_ts is None: raise ValueError("say_stream without thread_ts here is unsupported") if buffer_size is not None: return self.client.chat_stream( buffer_size=buffer_size, channel=channel, recipient_team_id=recipient_team_id or self.recipient_team_id, recipient_user_id=recipient_user_id or self.recipient_user_id, thread_ts=thread_ts, **kwargs, ) return self.client.chat_stream( channel=channel, recipient_team_id=recipient_team_id or self.recipient_team_id, recipient_user_id=recipient_user_id or self.recipient_user_id, thread_ts=thread_ts, **kwargs, ) ``` ### Class variables `var channel : str | None` The type of the None singleton. `var client : slack_sdk.web.client.WebClient` The type of the None singleton. `var recipient_team_id : str | None` The type of the None singleton. `var recipient_user_id : str | None` The type of the None singleton. `var thread_ts : str | None` The type of the None singleton. `class SetStatus (client: slack_sdk.web.client.WebClient, channel_id: str, thread_ts: str)` Expand source code ``` class SetStatus: client: WebClient channel_id: str thread_ts: str def __init__( self, client: WebClient, channel_id: str, thread_ts: str, ): self.client = client self.channel_id = channel_id self.thread_ts = thread_ts def __call__( self, status: str, loading_messages: Optional[List[str]] = None, **kwargs, ) -> SlackResponse: return self.client.assistant_threads_setStatus( channel_id=self.channel_id, thread_ts=self.thread_ts, status=status, loading_messages=loading_messages, **kwargs, ) ``` ### Class variables `var channel_id : str` The type of the None singleton. `var client : slack_sdk.web.client.WebClient` The type of the None singleton. `var thread_ts : str` The type of the None singleton. `class SetSuggestedPrompts (client: slack_sdk.web.client.WebClient, channel_id: str, thread_ts: str)` Expand source code ``` class SetSuggestedPrompts: client: WebClient channel_id: str thread_ts: str def __init__( self, client: WebClient, channel_id: str, thread_ts: str, ): self.client = client self.channel_id = channel_id self.thread_ts = thread_ts def __call__( self, prompts: Sequence[Union[str, Dict[str, str]]], title: Optional[str] = None, ) -> SlackResponse: prompts_arg: List[Dict[str, str]] = [] for prompt in prompts: if isinstance(prompt, str): prompts_arg.append({"title": prompt, "message": prompt}) else: prompts_arg.append(prompt) return self.client.assistant_threads_setSuggestedPrompts( channel_id=self.channel_id, thread_ts=self.thread_ts, prompts=prompts_arg, title=title, ) ``` ### Class variables `var channel_id : str` The type of the None singleton. `var client : slack_sdk.web.client.WebClient` The type of the None singleton. `var thread_ts : str` The type of the None singleton. `class SetTitle (client: slack_sdk.web.client.WebClient, channel_id: str, thread_ts: str)` Expand source code ``` class SetTitle: client: WebClient channel_id: str thread_ts: str def __init__( self, client: WebClient, channel_id: str, thread_ts: str, ): self.client = client self.channel_id = channel_id self.thread_ts = thread_ts def __call__(self, title: str) -> SlackResponse: return self.client.assistant_threads_setTitle( title=title, channel_id=self.channel_id, thread_ts=self.thread_ts, ) ``` ### Class variables `var channel_id : str` The type of the None singleton. `var client : slack_sdk.web.client.WebClient` The type of the None singleton. `var thread_ts : str` The type of the None singleton. --- Source: https://docs.slack.dev/tools/bolt-python/reference/adapter # Module slack_bolt.adapter Adapter modules for running Bolt apps along with Web frameworks or Socket Mode. ## Sub-modules `[slack_bolt.adapter.aiohttp](aiohttp/index.html "slack_bolt.adapter.aiohttp")` `[slack_bolt.adapter.asgi](asgi/index.html "slack_bolt.adapter.asgi")` `[slack_bolt.adapter.aws_lambda](aws_lambda/index.html "slack_bolt.adapter.aws_lambda")` `[slack_bolt.adapter.bottle](bottle/index.html "slack_bolt.adapter.bottle")` `[slack_bolt.adapter.cherrypy](cherrypy/index.html "slack_bolt.adapter.cherrypy")` `[slack_bolt.adapter.django](django/index.html "slack_bolt.adapter.django")` `[slack_bolt.adapter.falcon](falcon/index.html "slack_bolt.adapter.falcon")` `[slack_bolt.adapter.fastapi](fastapi/index.html "slack_bolt.adapter.fastapi")` `[slack_bolt.adapter.flask](flask/index.html "slack_bolt.adapter.flask")` `[slack_bolt.adapter.google_cloud_functions](google_cloud_functions/index.html "slack_bolt.adapter.google_cloud_functions")` `[slack_bolt.adapter.pyramid](pyramid/index.html "slack_bolt.adapter.pyramid")` `[slack_bolt.adapter.sanic](sanic/index.html "slack_bolt.adapter.sanic")` `[slack_bolt.adapter.socket_mode](socket_mode/index.html "slack_bolt.adapter.socket_mode")` Socket Mode adapter package provides the following implementations. If you don't have strong reasons to use 3rd party library based adapters, we … `[slack_bolt.adapter.starlette](starlette/index.html "slack_bolt.adapter.starlette")` `[slack_bolt.adapter.tornado](tornado/index.html "slack_bolt.adapter.tornado")` `[slack_bolt.adapter.wsgi](wsgi/index.html "slack_bolt.adapter.wsgi")` --- Source: https://docs.slack.dev/tools/bolt-python/reference/adapter/aiohttp # Module slack_bolt.adapter.aiohttp ## Functions `async def to_aiohttp_response(bolt_resp: [BoltResponse](../../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse")) ‑> aiohttp.web_response.Response` Expand source code ``` async def to_aiohttp_response(bolt_resp: BoltResponse) -> web.Response: content_type = bolt_resp.headers.pop( "content-type", ["application/json" if bolt_resp.body.startswith("{") else "text/plain"], )[0] content_type = re.sub(r";\s*charset=utf-8", "", content_type) resp = web.Response( status=bolt_resp.status, body=bolt_resp.body, headers=bolt_resp.first_headers_without_set_cookie(), content_type=content_type, ) for cookie in bolt_resp.cookies(): for name, c in cookie.items(): resp.set_cookie( name=name, value=c.value, max_age=c.get("max-age"), expires=c.get("expires"), path=c.get("path"), # type: ignore[arg-type] domain=c.get("domain"), secure=True, httponly=True, ) return resp ``` `async def to_bolt_request(request: aiohttp.web_request.Request) ‑> [AsyncBoltRequest](../../request/async_request.html#slack_bolt.request.async_request.AsyncBoltRequest "slack_bolt.request.async_request.AsyncBoltRequest")` Expand source code ``` async def to_bolt_request(request: web.Request) -> AsyncBoltRequest: return AsyncBoltRequest( body=await request.text(), query=request.query_string, headers=request.headers, # type: ignore[arg-type] ) ``` --- Source: https://docs.slack.dev/tools/bolt-python/reference/adapter/asgi # Module slack_bolt.adapter.asgi ## Sub-modules `[slack_bolt.adapter.asgi.aiohttp](aiohttp/index.html "slack_bolt.adapter.asgi.aiohttp")` `[slack_bolt.adapter.asgi.async_handler](async_handler.html "slack_bolt.adapter.asgi.async_handler")` `[slack_bolt.adapter.asgi.base_handler](base_handler.html "slack_bolt.adapter.asgi.base_handler")` `[slack_bolt.adapter.asgi.builtin](builtin/index.html "slack_bolt.adapter.asgi.builtin")` `[slack_bolt.adapter.asgi.http_request](http_request.html "slack_bolt.adapter.asgi.http_request")` `[slack_bolt.adapter.asgi.http_response](http_response.html "slack_bolt.adapter.asgi.http_response")` `[slack_bolt.adapter.asgi.utils](utils.html "slack_bolt.adapter.asgi.utils")` ## Classes `class SlackRequestHandler (app: [App](../../app/app.html#slack_bolt.app.app.App "slack_bolt.app.app.App"), path: str = '/slack/events')` Expand source code ``` class SlackRequestHandler(BaseSlackRequestHandler): def __init__(self, app: App, path: str = "/slack/events"): """Setup Bolt as an ASGI web framework, this will make your application compatible with ASGI web servers. This can be used for production deployment. With the default settings, `http://localhost:3000/slack/events` Run Bolt with [uvicron](https://www.uvicorn.org/) # Python app = App() api = SlackRequestHandler(app) # bash export SLACK_SIGNING_SECRET=*** export SLACK_BOT_TOKEN=xoxb-*** uvicorn app:api --port 3000 --log-level debug Args: app: Your bolt application path: The path to handle request from Slack (Default: `/slack/events`) """ self.path = path self.app = app async def dispatch(self, request: AsgiHttpRequest) -> BoltResponse: return self.app.dispatch( BoltRequest(body=await request.get_raw_body(), query=request.query_string, headers=request.get_headers()) ) async def handle_installation(self, request: AsgiHttpRequest) -> BoltResponse: return self.app.oauth_flow.handle_installation( # type: ignore[union-attr] BoltRequest(body=await request.get_raw_body(), query=request.query_string, headers=request.get_headers()) ) async def handle_callback(self, request: AsgiHttpRequest) -> BoltResponse: return self.app.oauth_flow.handle_callback( # type: ignore[union-attr] BoltRequest(body=await request.get_raw_body(), query=request.query_string, headers=request.get_headers()) ) ``` Setup Bolt as an ASGI web framework, this will make your application compatible with ASGI web servers. This can be used for production deployment. With the default settings, `http://localhost:3000/slack/events` Run Bolt with [uvicron](https://www.uvicorn.org/) ``` # Python app = App() api = SlackRequestHandler(app) # bash export SLACK_SIGNING_SECRET=*** export SLACK_BOT_TOKEN=xoxb-*** uvicorn app:api --port 3000 --log-level debug ``` ## Args **`app`** Your bolt application **`path`** The path to handle request from Slack (Default: `/slack/events`) ### Ancestors * [BaseSlackRequestHandler](base_handler.html#slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler "slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler") ### Subclasses * [AsyncSlackRequestHandler](aiohttp/index.html#slack_bolt.adapter.asgi.aiohttp.AsyncSlackRequestHandler "slack_bolt.adapter.asgi.aiohttp.AsyncSlackRequestHandler") ### Inherited members * `**[BaseSlackRequestHandler](base_handler.html#slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler "slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler")**`: * `[app](base_handler.html#slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler.app "slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler.app")` * `[dispatch](base_handler.html#slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler.dispatch "slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler.dispatch")` * `[handle_callback](base_handler.html#slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler.handle_callback "slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler.handle_callback")` * `[handle_installation](base_handler.html#slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler.handle_installation "slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler.handle_installation")` * `[path](base_handler.html#slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler.path "slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler.path")` --- Source: https://docs.slack.dev/tools/bolt-python/reference/adapter/asgi/aiohttp # Module slack_bolt.adapter.asgi.aiohttp ## Classes `class AsyncSlackRequestHandler (app: [AsyncApp](../../../app/async_app.html#slack_bolt.app.async_app.AsyncApp "slack_bolt.app.async_app.AsyncApp"), path: str = '/slack/events')` Expand source code ``` class AsyncSlackRequestHandler(SlackRequestHandler): app: AsyncApp def __init__(self, app: AsyncApp, path: str = "/slack/events"): """Setup Bolt as an ASGI web framework, this will make your application compatible with ASGI web servers. This can be used for production deployment. With the default settings, `http://localhost:3000/slack/events` Run Bolt with [uvicron](https://www.uvicorn.org/) # Python app = AsyncApp() api = SlackRequestHandler(app) # bash export SLACK_SIGNING_SECRET=*** export SLACK_BOT_TOKEN=xoxb-*** uvicorn app:api --port 3000 --log-level debug Args: app: Your bolt application path: The path to handle request from Slack (Default: `/slack/events`) """ self.path = path self.app = app async def dispatch(self, request: AsgiHttpRequest) -> BoltResponse: return await self.app.async_dispatch( AsyncBoltRequest(body=await request.get_raw_body(), query=request.query_string, headers=request.get_headers()) ) async def handle_installation(self, request: AsgiHttpRequest) -> BoltResponse: return await self.app.oauth_flow.handle_installation( # type: ignore[union-attr] AsyncBoltRequest(body=await request.get_raw_body(), query=request.query_string, headers=request.get_headers()) ) async def handle_callback(self, request: AsgiHttpRequest) -> BoltResponse: return await self.app.oauth_flow.handle_callback( # type: ignore[union-attr] AsyncBoltRequest(body=await request.get_raw_body(), query=request.query_string, headers=request.get_headers()) ) ``` Setup Bolt as an ASGI web framework, this will make your application compatible with ASGI web servers. This can be used for production deployment. With the default settings, `http://localhost:3000/slack/events` Run Bolt with [uvicron](https://www.uvicorn.org/) ``` # Python app = AsyncApp() api = SlackRequestHandler(app) # bash export SLACK_SIGNING_SECRET=*** export SLACK_BOT_TOKEN=xoxb-*** uvicorn app:api --port 3000 --log-level debug ``` ## Args **`app`** Your bolt application **`path`** The path to handle request from Slack (Default: `/slack/events`) ### Ancestors * [SlackRequestHandler](../builtin/index.html#slack_bolt.adapter.asgi.builtin.SlackRequestHandler "slack_bolt.adapter.asgi.builtin.SlackRequestHandler") * [BaseSlackRequestHandler](../base_handler.html#slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler "slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler") ### Inherited members * `**[SlackRequestHandler](../builtin/index.html#slack_bolt.adapter.asgi.builtin.SlackRequestHandler "slack_bolt.adapter.asgi.builtin.SlackRequestHandler")**`: * `[app](../base_handler.html#slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler.app "slack_bolt.adapter.asgi.builtin.SlackRequestHandler.app")` * `[dispatch](../base_handler.html#slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler.dispatch "slack_bolt.adapter.asgi.builtin.SlackRequestHandler.dispatch")` * `[handle_callback](../base_handler.html#slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler.handle_callback "slack_bolt.adapter.asgi.builtin.SlackRequestHandler.handle_callback")` * `[handle_installation](../base_handler.html#slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler.handle_installation "slack_bolt.adapter.asgi.builtin.SlackRequestHandler.handle_installation")` * `[path](../base_handler.html#slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler.path "slack_bolt.adapter.asgi.builtin.SlackRequestHandler.path")` --- Source: https://docs.slack.dev/tools/bolt-python/reference/adapter/asgi/builtin # Module slack_bolt.adapter.asgi.builtin ## Classes `class SlackRequestHandler (app: [App](../../../app/app.html#slack_bolt.app.app.App "slack_bolt.app.app.App"), path: str = '/slack/events')` Expand source code ``` class SlackRequestHandler(BaseSlackRequestHandler): def __init__(self, app: App, path: str = "/slack/events"): """Setup Bolt as an ASGI web framework, this will make your application compatible with ASGI web servers. This can be used for production deployment. With the default settings, `http://localhost:3000/slack/events` Run Bolt with [uvicron](https://www.uvicorn.org/) # Python app = App() api = SlackRequestHandler(app) # bash export SLACK_SIGNING_SECRET=*** export SLACK_BOT_TOKEN=xoxb-*** uvicorn app:api --port 3000 --log-level debug Args: app: Your bolt application path: The path to handle request from Slack (Default: `/slack/events`) """ self.path = path self.app = app async def dispatch(self, request: AsgiHttpRequest) -> BoltResponse: return self.app.dispatch( BoltRequest(body=await request.get_raw_body(), query=request.query_string, headers=request.get_headers()) ) async def handle_installation(self, request: AsgiHttpRequest) -> BoltResponse: return self.app.oauth_flow.handle_installation( # type: ignore[union-attr] BoltRequest(body=await request.get_raw_body(), query=request.query_string, headers=request.get_headers()) ) async def handle_callback(self, request: AsgiHttpRequest) -> BoltResponse: return self.app.oauth_flow.handle_callback( # type: ignore[union-attr] BoltRequest(body=await request.get_raw_body(), query=request.query_string, headers=request.get_headers()) ) ``` Setup Bolt as an ASGI web framework, this will make your application compatible with ASGI web servers. This can be used for production deployment. With the default settings, `http://localhost:3000/slack/events` Run Bolt with [uvicron](https://www.uvicorn.org/) ``` # Python app = App() api = SlackRequestHandler(app) # bash export SLACK_SIGNING_SECRET=*** export SLACK_BOT_TOKEN=xoxb-*** uvicorn app:api --port 3000 --log-level debug ``` ## Args **`app`** Your bolt application **`path`** The path to handle request from Slack (Default: `/slack/events`) ### Ancestors * [BaseSlackRequestHandler](../base_handler.html#slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler "slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler") ### Subclasses * [AsyncSlackRequestHandler](../aiohttp/index.html#slack_bolt.adapter.asgi.aiohttp.AsyncSlackRequestHandler "slack_bolt.adapter.asgi.aiohttp.AsyncSlackRequestHandler") ### Inherited members * `**[BaseSlackRequestHandler](../base_handler.html#slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler "slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler")**`: * `[app](../base_handler.html#slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler.app "slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler.app")` * `[dispatch](../base_handler.html#slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler.dispatch "slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler.dispatch")` * `[handle_callback](../base_handler.html#slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler.handle_callback "slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler.handle_callback")` * `[handle_installation](../base_handler.html#slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler.handle_installation "slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler.handle_installation")` * `[path](../base_handler.html#slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler.path "slack_bolt.adapter.asgi.base_handler.BaseSlackRequestHandler.path")` --- Source: https://docs.slack.dev/tools/bolt-python/reference/adapter/aws_lambda # Module slack_bolt.adapter.aws_lambda ## Sub-modules `[slack_bolt.adapter.aws_lambda.chalice_handler](chalice_handler.html "slack_bolt.adapter.aws_lambda.chalice_handler")` `[slack_bolt.adapter.aws_lambda.chalice_lazy_listener_runner](chalice_lazy_listener_runner.html "slack_bolt.adapter.aws_lambda.chalice_lazy_listener_runner")` `[slack_bolt.adapter.aws_lambda.handler](handler.html "slack_bolt.adapter.aws_lambda.handler")` `[slack_bolt.adapter.aws_lambda.internals](internals.html "slack_bolt.adapter.aws_lambda.internals")` `[slack_bolt.adapter.aws_lambda.lambda_s3_oauth_flow](lambda_s3_oauth_flow.html "slack_bolt.adapter.aws_lambda.lambda_s3_oauth_flow")` `[slack_bolt.adapter.aws_lambda.lazy_listener_runner](lazy_listener_runner.html "slack_bolt.adapter.aws_lambda.lazy_listener_runner")` `[slack_bolt.adapter.aws_lambda.local_lambda_client](local_lambda_client.html "slack_bolt.adapter.aws_lambda.local_lambda_client")` ## Classes `class SlackRequestHandler (app: [App](../../app/app.html#slack_bolt.app.app.App "slack_bolt.app.app.App"))` Expand source code ``` class SlackRequestHandler: def __init__(self, app: App): self.app = app self.logger = get_bolt_app_logger(app.name, SlackRequestHandler, app.logger) self.app.listener_runner.lazy_listener_runner = LambdaLazyListenerRunner(self.logger) if self.app.oauth_flow is not None: self.app.oauth_flow.settings.redirect_uri_page_renderer.install_path = "?" @classmethod def clear_all_log_handlers(cls): # https://stackoverflow.com/questions/37703609/using-python-logging-with-aws-lambda root = logging.getLogger() if root.handlers: for handler in root.handlers: root.removeHandler(handler) def handle(self, event, context): self.logger.debug(f"Incoming event: {event}, context: {context}") method = event.get("requestContext", {}).get("http", {}).get("method") if method is None: method = event.get("requestContext", {}).get("httpMethod") if method is None: return not_found() if method == "GET": if self.app.oauth_flow is not None: oauth_flow: OAuthFlow = self.app.oauth_flow bolt_req: BoltRequest = to_bolt_request(event) query = bolt_req.query is_callback = query is not None and ( (_first_value(query, "code") is not None and _first_value(query, "state") is not None) or _first_value(query, "error") is not None ) if is_callback: bolt_resp = oauth_flow.handle_callback(bolt_req) return to_aws_response(bolt_resp) else: bolt_resp = oauth_flow.handle_installation(bolt_req) return to_aws_response(bolt_resp) elif method == "POST": bolt_req = to_bolt_request(event) # https://docs.aws.amazon.com/lambda/latest/dg/python-context.html aws_lambda_function_name = context.function_name bolt_req.context["aws_lambda_function_name"] = aws_lambda_function_name bolt_req.context["aws_lambda_invoked_function_arn"] = context.invoked_function_arn bolt_req.context["lambda_request"] = event bolt_resp = self.app.dispatch(bolt_req) aws_response = to_aws_response(bolt_resp) return aws_response elif method == "NONE": bolt_req = to_bolt_request(event) bolt_resp = self.app.dispatch(bolt_req) aws_response = to_aws_response(bolt_resp) return aws_response return not_found() ``` ### Static methods `def clear_all_log_handlers()` ### Methods `def handle(self, event, context)` Expand source code ``` def handle(self, event, context): self.logger.debug(f"Incoming event: {event}, context: {context}") method = event.get("requestContext", {}).get("http", {}).get("method") if method is None: method = event.get("requestContext", {}).get("httpMethod") if method is None: return not_found() if method == "GET": if self.app.oauth_flow is not None: oauth_flow: OAuthFlow = self.app.oauth_flow bolt_req: BoltRequest = to_bolt_request(event) query = bolt_req.query is_callback = query is not None and ( (_first_value(query, "code") is not None and _first_value(query, "state") is not None) or _first_value(query, "error") is not None ) if is_callback: bolt_resp = oauth_flow.handle_callback(bolt_req) return to_aws_response(bolt_resp) else: bolt_resp = oauth_flow.handle_installation(bolt_req) return to_aws_response(bolt_resp) elif method == "POST": bolt_req = to_bolt_request(event) # https://docs.aws.amazon.com/lambda/latest/dg/python-context.html aws_lambda_function_name = context.function_name bolt_req.context["aws_lambda_function_name"] = aws_lambda_function_name bolt_req.context["aws_lambda_invoked_function_arn"] = context.invoked_function_arn bolt_req.context["lambda_request"] = event bolt_resp = self.app.dispatch(bolt_req) aws_response = to_aws_response(bolt_resp) return aws_response elif method == "NONE": bolt_req = to_bolt_request(event) bolt_resp = self.app.dispatch(bolt_req) aws_response = to_aws_response(bolt_resp) return aws_response return not_found() ``` --- Source: https://docs.slack.dev/tools/bolt-python/reference/adapter/bottle # Module slack_bolt.adapter.bottle ## Sub-modules `[slack_bolt.adapter.bottle.handler](handler.html "slack_bolt.adapter.bottle.handler")` ## Classes `class SlackRequestHandler (app: [App](../../app/app.html#slack_bolt.app.app.App "slack_bolt.app.app.App"))` Expand source code ``` class SlackRequestHandler: def __init__(self, app: App): self.app = app def handle(self, req: Request, resp: Response) -> str: if req.method == "GET": if self.app.oauth_flow is not None: oauth_flow: OAuthFlow = self.app.oauth_flow if req.path == oauth_flow.install_path: bolt_resp = oauth_flow.handle_installation(to_bolt_request(req)) set_response(bolt_resp, resp) return bolt_resp.body or "" elif req.path == oauth_flow.redirect_uri_path: bolt_resp = oauth_flow.handle_callback(to_bolt_request(req)) set_response(bolt_resp, resp) return bolt_resp.body or "" elif req.method == "POST": bolt_resp = self.app.dispatch(to_bolt_request(req)) set_response(bolt_resp, resp) return bolt_resp.body or "" resp.status = 404 return "Not Found" ``` ### Methods `def handle(self, req: bottle.BaseRequest, resp: bottle.BaseResponse) ‑> str` Expand source code ``` def handle(self, req: Request, resp: Response) -> str: if req.method == "GET": if self.app.oauth_flow is not None: oauth_flow: OAuthFlow = self.app.oauth_flow if req.path == oauth_flow.install_path: bolt_resp = oauth_flow.handle_installation(to_bolt_request(req)) set_response(bolt_resp, resp) return bolt_resp.body or "" elif req.path == oauth_flow.redirect_uri_path: bolt_resp = oauth_flow.handle_callback(to_bolt_request(req)) set_response(bolt_resp, resp) return bolt_resp.body or "" elif req.method == "POST": bolt_resp = self.app.dispatch(to_bolt_request(req)) set_response(bolt_resp, resp) return bolt_resp.body or "" resp.status = 404 return "Not Found" ``` --- Source: https://docs.slack.dev/tools/bolt-python/reference/adapter/cherrypy # Module slack_bolt.adapter.cherrypy ## Sub-modules `[slack_bolt.adapter.cherrypy.handler](handler.html "slack_bolt.adapter.cherrypy.handler")` ## Classes `class SlackRequestHandler (app: [App](../../app/app.html#slack_bolt.app.app.App "slack_bolt.app.app.App"))` Expand source code ``` class SlackRequestHandler: def __init__(self, app: App): self.app = app def handle(self) -> bytes: req = cherrypy.request if req.method == "GET": if self.app.oauth_flow is not None: oauth_flow: OAuthFlow = self.app.oauth_flow request_path = req.wsgi_environ["REQUEST_URI"].split("?")[0] if request_path == oauth_flow.install_path: bolt_resp = oauth_flow.handle_installation(build_bolt_request()) set_response_status_and_headers(bolt_resp) return (bolt_resp.body or "").encode("utf-8") elif request_path == oauth_flow.redirect_uri_path: bolt_resp = oauth_flow.handle_callback(build_bolt_request()) set_response_status_and_headers(bolt_resp) return (bolt_resp.body or "").encode("utf-8") elif req.method == "POST": bolt_resp = self.app.dispatch(build_bolt_request()) set_response_status_and_headers(bolt_resp) return (bolt_resp.body or "").encode("utf-8") cherrypy.response.status = 404 return "Not Found".encode("utf-8") ``` ### Methods `def handle(self) ‑> bytes` Expand source code ``` def handle(self) -> bytes: req = cherrypy.request if req.method == "GET": if self.app.oauth_flow is not None: oauth_flow: OAuthFlow = self.app.oauth_flow request_path = req.wsgi_environ["REQUEST_URI"].split("?")[0] if request_path == oauth_flow.install_path: bolt_resp = oauth_flow.handle_installation(build_bolt_request()) set_response_status_and_headers(bolt_resp) return (bolt_resp.body or "").encode("utf-8") elif request_path == oauth_flow.redirect_uri_path: bolt_resp = oauth_flow.handle_callback(build_bolt_request()) set_response_status_and_headers(bolt_resp) return (bolt_resp.body or "").encode("utf-8") elif req.method == "POST": bolt_resp = self.app.dispatch(build_bolt_request()) set_response_status_and_headers(bolt_resp) return (bolt_resp.body or "").encode("utf-8") cherrypy.response.status = 404 return "Not Found".encode("utf-8") ``` --- Source: https://docs.slack.dev/tools/bolt-python/reference/adapter/django # Module slack_bolt.adapter.django ## Sub-modules `[slack_bolt.adapter.django.handler](handler.html "slack_bolt.adapter.django.handler")` ## Classes `class SlackRequestHandler (app: [App](../../app/app.html#slack_bolt.app.app.App "slack_bolt.app.app.App"))` Expand source code ``` class SlackRequestHandler: def __init__(self, app: App): self.app = app listener_runner = self.app.listener_runner # This runner closes all thread-local connections in the thread when an execution completes self.app.listener_runner.lazy_listener_runner = DjangoThreadLazyListenerRunner( logger=listener_runner.logger, executor=listener_runner.listener_executor, ) if not isinstance(listener_runner, ThreadListenerRunner): raise BoltError("Custom listener_runners are not compatible with this Django adapter.") if app.process_before_response is True: # As long as the app access Django models in the same thread, # Django cleans the connections up for you. self.app.logger.debug("App.process_before_response is set to True") return current_start_handler = listener_runner.listener_start_handler if current_start_handler is not None and not isinstance(current_start_handler, DefaultListenerStartHandler): # As we run release_thread_local_connections() before listener executions, # it's okay to skip calling the same connection clean-up method at the listener completion. message = """As you've already set app.listener_runner.listener_start_handler to your own one, Bolt skipped to set it to slack_sdk.adapter.django.DjangoListenerStartHandler. If you go with your own handler here, we highly recommend having the following lines of code in your handle() method to clean up unmanaged stale/old database connections: from django.db import close_old_connections close_old_connections() """ self.app.logger.info(message) else: # for proper management of thread-local Django DB connections self.app.listener_runner.listener_start_handler = DjangoListenerStartHandler() self.app.logger.debug("DjangoListenerStartHandler has been enabled") current_completion_handler = listener_runner.listener_completion_handler if current_completion_handler is not None and not isinstance( current_completion_handler, DefaultListenerCompletionHandler ): # As we run release_thread_local_connections() before listener executions, # it's okay to skip calling the same connection clean-up method at the listener completion. message = """As you've already set app.listener_runner.listener_completion_handler to your own one, Bolt skipped to set it to slack_sdk.adapter.django.DjangoListenerCompletionHandler. """ self.app.logger.info(message) return # for proper management of thread-local Django DB connections self.app.listener_runner.listener_completion_handler = DjangoListenerCompletionHandler() self.app.logger.debug("DjangoListenerCompletionHandler has been enabled") def handle(self, req: HttpRequest) -> HttpResponse: if req.method == "GET": if self.app.oauth_flow is not None: oauth_flow: OAuthFlow = self.app.oauth_flow if req.path == oauth_flow.install_path: bolt_resp = oauth_flow.handle_installation(to_bolt_request(req)) return to_django_response(bolt_resp) elif req.path == oauth_flow.redirect_uri_path: bolt_resp = oauth_flow.handle_callback(to_bolt_request(req)) return to_django_response(bolt_resp) elif req.method == "POST": bolt_resp = self.app.dispatch(to_bolt_request(req)) return to_django_response(bolt_resp) return HttpResponse(status=404, content=b"Not Found") ``` ### Methods `def handle(self, req: django.http.request.HttpRequest) ‑> django.http.response.HttpResponse` Expand source code ``` def handle(self, req: HttpRequest) -> HttpResponse: if req.method == "GET": if self.app.oauth_flow is not None: oauth_flow: OAuthFlow = self.app.oauth_flow if req.path == oauth_flow.install_path: bolt_resp = oauth_flow.handle_installation(to_bolt_request(req)) return to_django_response(bolt_resp) elif req.path == oauth_flow.redirect_uri_path: bolt_resp = oauth_flow.handle_callback(to_bolt_request(req)) return to_django_response(bolt_resp) elif req.method == "POST": bolt_resp = self.app.dispatch(to_bolt_request(req)) return to_django_response(bolt_resp) return HttpResponse(status=404, content=b"Not Found") ``` --- Source: https://docs.slack.dev/tools/bolt-python/reference/adapter/falcon # Module slack_bolt.adapter.falcon ## Sub-modules `[slack_bolt.adapter.falcon.async_resource](async_resource.html "slack_bolt.adapter.falcon.async_resource")` `[slack_bolt.adapter.falcon.resource](resource.html "slack_bolt.adapter.falcon.resource")` ## Classes `class SlackAppResource (app: [App](../../app/app.html#slack_bolt.app.app.App "slack_bolt.app.app.App"))` Expand source code ``` class SlackAppResource: """ from slack_bolt import App app = App() import falcon api = application = falcon.API() api.add_route("/slack/events", SlackAppResource(app)) """ def __init__(self, app: App): self.app = app def on_get(self, req: Request, resp: Response): if self.app.oauth_flow is not None: oauth_flow: OAuthFlow = self.app.oauth_flow if req.path == oauth_flow.install_path: bolt_resp = oauth_flow.handle_installation(self._to_bolt_request(req)) self._write_response(bolt_resp, resp) return elif req.path == oauth_flow.redirect_uri_path: bolt_resp = oauth_flow.handle_callback(self._to_bolt_request(req)) self._write_response(bolt_resp, resp) return resp.status = "404" # Falcon 4.x w/ mypy fails to correctly infer the str type here resp.body = "The page is not found..." def on_post(self, req: Request, resp: Response): bolt_req = self._to_bolt_request(req) bolt_resp = self.app.dispatch(bolt_req) self._write_response(bolt_resp, resp) def _to_bolt_request(self, req: Request) -> BoltRequest: return BoltRequest( body=req.stream.read(req.content_length or 0).decode("utf-8"), query=req.query_string, headers={k.lower(): v for k, v in req.headers.items()}, ) def _write_response(self, bolt_resp: BoltResponse, resp: Response): if falcon_version.__version__.startswith("2."): # Falcon 4.x w/ mypy fails to correctly infer the str type here resp.body = bolt_resp.body else: resp.text = bolt_resp.body status = HTTPStatus(bolt_resp.status) resp.status = str(f"{status.value} {status.phrase}") resp.set_headers(bolt_resp.first_headers_without_set_cookie()) for cookie in bolt_resp.cookies(): for name, c in cookie.items(): expire_value = c.get("expires") expire = datetime.strptime(expire_value, "%a, %d %b %Y %H:%M:%S %Z") if expire_value else None resp.set_cookie( name=name, value=c.value, expires=expire, max_age=c.get("max-age"), domain=c.get("domain"), path=c.get("path"), secure=True, http_only=True, ) ``` from slack\_bolt import App app = App() import falcon api = application = falcon.API() api.add\_route("/slack/events", SlackAppResource(app)) ### Methods `def on_get(self, req: falcon.request.Request, resp: falcon.response.Response)` Expand source code ``` def on_get(self, req: Request, resp: Response): if self.app.oauth_flow is not None: oauth_flow: OAuthFlow = self.app.oauth_flow if req.path == oauth_flow.install_path: bolt_resp = oauth_flow.handle_installation(self._to_bolt_request(req)) self._write_response(bolt_resp, resp) return elif req.path == oauth_flow.redirect_uri_path: bolt_resp = oauth_flow.handle_callback(self._to_bolt_request(req)) self._write_response(bolt_resp, resp) return resp.status = "404" # Falcon 4.x w/ mypy fails to correctly infer the str type here resp.body = "The page is not found..." ``` `def on_post(self, req: falcon.request.Request, resp: falcon.response.Response)` Expand source code ``` def on_post(self, req: Request, resp: Response): bolt_req = self._to_bolt_request(req) bolt_resp = self.app.dispatch(bolt_req) self._write_response(bolt_resp, resp) ``` --- Source: https://docs.slack.dev/tools/bolt-python/reference/adapter/fastapi # Module slack_bolt.adapter.fastapi ## Sub-modules `[slack_bolt.adapter.fastapi.async_handler](async_handler.html "slack_bolt.adapter.fastapi.async_handler")` ## Classes `class SlackRequestHandler (app: [App](../../app/app.html#slack_bolt.app.app.App "slack_bolt.app.app.App"))` Expand source code ``` class SlackRequestHandler: def __init__(self, app: App): self.app = app async def handle(self, req: Request, addition_context_properties: Optional[Dict[str, Any]] = None) -> Response: body = await req.body() if req.method == "GET": if self.app.oauth_flow is not None: oauth_flow: OAuthFlow = self.app.oauth_flow if req.url.path == oauth_flow.install_path: bolt_resp = oauth_flow.handle_installation(to_bolt_request(req, body, addition_context_properties)) return to_starlette_response(bolt_resp) elif req.url.path == oauth_flow.redirect_uri_path: bolt_resp = oauth_flow.handle_callback(to_bolt_request(req, body, addition_context_properties)) return to_starlette_response(bolt_resp) elif req.method == "POST": bolt_resp = self.app.dispatch(to_bolt_request(req, body, addition_context_properties)) return to_starlette_response(bolt_resp) return Response( status_code=404, content="Not found", ) ``` ### Methods `async def handle(self, req: starlette.requests.Request, addition_context_properties: Dict[str, Any] | None = None) ‑> starlette.responses.Response` Expand source code ``` async def handle(self, req: Request, addition_context_properties: Optional[Dict[str, Any]] = None) -> Response: body = await req.body() if req.method == "GET": if self.app.oauth_flow is not None: oauth_flow: OAuthFlow = self.app.oauth_flow if req.url.path == oauth_flow.install_path: bolt_resp = oauth_flow.handle_installation(to_bolt_request(req, body, addition_context_properties)) return to_starlette_response(bolt_resp) elif req.url.path == oauth_flow.redirect_uri_path: bolt_resp = oauth_flow.handle_callback(to_bolt_request(req, body, addition_context_properties)) return to_starlette_response(bolt_resp) elif req.method == "POST": bolt_resp = self.app.dispatch(to_bolt_request(req, body, addition_context_properties)) return to_starlette_response(bolt_resp) return Response( status_code=404, content="Not found", ) ``` --- Source: https://docs.slack.dev/tools/bolt-python/reference/adapter/flask # Module slack_bolt.adapter.flask ## Sub-modules `[slack_bolt.adapter.flask.handler](handler.html "slack_bolt.adapter.flask.handler")` ## Classes `class SlackRequestHandler (app: [App](../../app/app.html#slack_bolt.app.app.App "slack_bolt.app.app.App"))` Expand source code ``` class SlackRequestHandler: def __init__(self, app: App): self.app = app def handle(self, req: Request) -> Response: if req.method == "GET": if self.app.oauth_flow is not None: oauth_flow: OAuthFlow = self.app.oauth_flow if req.path == oauth_flow.install_path: bolt_resp = oauth_flow.handle_installation(to_bolt_request(req)) return to_flask_response(bolt_resp) elif req.path == oauth_flow.redirect_uri_path: bolt_resp = oauth_flow.handle_callback(to_bolt_request(req)) return to_flask_response(bolt_resp) elif req.method == "POST": bolt_resp = self.app.dispatch(to_bolt_request(req)) return to_flask_response(bolt_resp) return make_response("Not Found", 404) ``` ### Methods `def handle(self, req: flask.wrappers.Request) ‑> flask.wrappers.Response` Expand source code ``` def handle(self, req: Request) -> Response: if req.method == "GET": if self.app.oauth_flow is not None: oauth_flow: OAuthFlow = self.app.oauth_flow if req.path == oauth_flow.install_path: bolt_resp = oauth_flow.handle_installation(to_bolt_request(req)) return to_flask_response(bolt_resp) elif req.path == oauth_flow.redirect_uri_path: bolt_resp = oauth_flow.handle_callback(to_bolt_request(req)) return to_flask_response(bolt_resp) elif req.method == "POST": bolt_resp = self.app.dispatch(to_bolt_request(req)) return to_flask_response(bolt_resp) return make_response("Not Found", 404) ``` --- Source: https://docs.slack.dev/tools/bolt-python/reference/adapter/google_cloud_functions # Module slack_bolt.adapter.google_cloud_functions ## Sub-modules `[slack_bolt.adapter.google_cloud_functions.handler](handler.html "slack_bolt.adapter.google_cloud_functions.handler")` ## Classes `class SlackRequestHandler (app: [App](../../app/app.html#slack_bolt.app.app.App "slack_bolt.app.app.App"))` Expand source code ``` class SlackRequestHandler: def __init__(self, app: App): self.app = app # Note that lazy listener is not supported self.app.listener_runner.lazy_listener_runner = NoopLazyListenerRunner() if self.app.oauth_flow is not None: self.app.oauth_flow.settings.redirect_uri_page_renderer.install_path = "?" def handle(self, req: Request) -> Response: if req.method == "GET" and self.app.oauth_flow is not None: bolt_req = to_bolt_request(req) if "code" in req.args or "error" in req.args or "state" in req.args: bolt_resp = self.app.oauth_flow.handle_callback(bolt_req) return to_flask_response(bolt_resp) else: bolt_resp = self.app.oauth_flow.handle_installation(bolt_req) return to_flask_response(bolt_resp) elif req.method == "POST": bolt_resp = self.app.dispatch(to_bolt_request(req)) return to_flask_response(bolt_resp) return make_response("Not Found", 404) ``` ### Methods `def handle(self, req: flask.wrappers.Request) ‑> flask.wrappers.Response` Expand source code ``` def handle(self, req: Request) -> Response: if req.method == "GET" and self.app.oauth_flow is not None: bolt_req = to_bolt_request(req) if "code" in req.args or "error" in req.args or "state" in req.args: bolt_resp = self.app.oauth_flow.handle_callback(bolt_req) return to_flask_response(bolt_resp) else: bolt_resp = self.app.oauth_flow.handle_installation(bolt_req) return to_flask_response(bolt_resp) elif req.method == "POST": bolt_resp = self.app.dispatch(to_bolt_request(req)) return to_flask_response(bolt_resp) return make_response("Not Found", 404) ``` --- Source: https://docs.slack.dev/tools/bolt-python/reference/adapter/pyramid # Module slack_bolt.adapter.pyramid ## Sub-modules `[slack_bolt.adapter.pyramid.handler](handler.html "slack_bolt.adapter.pyramid.handler")` ## Classes `class SlackRequestHandler (app: [App](../../app/app.html#slack_bolt.app.app.App "slack_bolt.app.app.App"))` Expand source code ``` class SlackRequestHandler: def __init__(self, app: App): self.app = app def handle(self, request: Request) -> Response: if request.method == "GET": if self.app.oauth_flow is not None: oauth_flow: OAuthFlow = self.app.oauth_flow if request.path == oauth_flow.install_path: bolt_req = _attach_pyramid_request_to_context(to_bolt_request(request), request) bolt_resp = oauth_flow.handle_installation(bolt_req) return to_pyramid_response(bolt_resp) elif request.path == oauth_flow.redirect_uri_path: bolt_req = _attach_pyramid_request_to_context(to_bolt_request(request), request) bolt_resp = oauth_flow.handle_callback(bolt_req) return to_pyramid_response(bolt_resp) elif request.method == "POST": bolt_req = _attach_pyramid_request_to_context(to_bolt_request(request), request) bolt_resp = self.app.dispatch(bolt_req) return to_pyramid_response(bolt_resp) return Response(status=404, body="Not found") ``` ### Methods `def handle(self, request: pyramid.request.Request) ‑> pyramid.response.Response` Expand source code ``` def handle(self, request: Request) -> Response: if request.method == "GET": if self.app.oauth_flow is not None: oauth_flow: OAuthFlow = self.app.oauth_flow if request.path == oauth_flow.install_path: bolt_req = _attach_pyramid_request_to_context(to_bolt_request(request), request) bolt_resp = oauth_flow.handle_installation(bolt_req) return to_pyramid_response(bolt_resp) elif request.path == oauth_flow.redirect_uri_path: bolt_req = _attach_pyramid_request_to_context(to_bolt_request(request), request) bolt_resp = oauth_flow.handle_callback(bolt_req) return to_pyramid_response(bolt_resp) elif request.method == "POST": bolt_req = _attach_pyramid_request_to_context(to_bolt_request(request), request) bolt_resp = self.app.dispatch(bolt_req) return to_pyramid_response(bolt_resp) return Response(status=404, body="Not found") ``` --- Source: https://docs.slack.dev/tools/bolt-python/reference/adapter/sanic # Module slack_bolt.adapter.sanic ## Sub-modules `[slack_bolt.adapter.sanic.async_handler](async_handler.html "slack_bolt.adapter.sanic.async_handler")` ## Classes `class AsyncSlackRequestHandler (app: [AsyncApp](../../app/async_app.html#slack_bolt.app.async_app.AsyncApp "slack_bolt.app.async_app.AsyncApp"))` Expand source code ``` class AsyncSlackRequestHandler: def __init__(self, app: AsyncApp): self.app = app async def handle(self, req: Request, addition_context_properties: Optional[Dict[str, Any]] = None) -> HTTPResponse: if req.method == "GET": if self.app.oauth_flow is not None: oauth_flow: AsyncOAuthFlow = self.app.oauth_flow if req.path == oauth_flow.install_path: bolt_resp = await oauth_flow.handle_installation(to_async_bolt_request(req, addition_context_properties)) return to_sanic_response(bolt_resp) elif req.path == oauth_flow.redirect_uri_path: bolt_resp = await oauth_flow.handle_callback(to_async_bolt_request(req, addition_context_properties)) return to_sanic_response(bolt_resp) elif req.method == "POST": bolt_resp = await self.app.async_dispatch(to_async_bolt_request(req, addition_context_properties)) return to_sanic_response(bolt_resp) return HTTPResponse( status=404, body="Not found", ) ``` ### Methods `async def handle(self, req: sanic.request.types.Request, addition_context_properties: Dict[str, Any] | None = None) ‑> sanic.response.types.HTTPResponse` Expand source code ``` async def handle(self, req: Request, addition_context_properties: Optional[Dict[str, Any]] = None) -> HTTPResponse: if req.method == "GET": if self.app.oauth_flow is not None: oauth_flow: AsyncOAuthFlow = self.app.oauth_flow if req.path == oauth_flow.install_path: bolt_resp = await oauth_flow.handle_installation(to_async_bolt_request(req, addition_context_properties)) return to_sanic_response(bolt_resp) elif req.path == oauth_flow.redirect_uri_path: bolt_resp = await oauth_flow.handle_callback(to_async_bolt_request(req, addition_context_properties)) return to_sanic_response(bolt_resp) elif req.method == "POST": bolt_resp = await self.app.async_dispatch(to_async_bolt_request(req, addition_context_properties)) return to_sanic_response(bolt_resp) return HTTPResponse( status=404, body="Not found", ) ``` --- Source: https://docs.slack.dev/tools/bolt-python/reference/adapter/socket_mode # Module slack_bolt.adapter.socket_mode Socket Mode adapter package provides the following implementations. If you don't have strong reasons to use 3rd party library based adapters, we recommend using the built-in client based one. * `[slack_bolt.adapter.socket_mode.builtin](builtin/index.html "slack_bolt.adapter.socket_mode.builtin")` * `[slack_bolt.adapter.socket_mode.websocket_client](websocket_client/index.html "slack_bolt.adapter.socket_mode.websocket_client")` * `[slack_bolt.adapter.socket_mode.aiohttp](aiohttp/index.html "slack_bolt.adapter.socket_mode.aiohttp")` * `[slack_bolt.adapter.socket_mode.websockets](websockets/index.html "slack_bolt.adapter.socket_mode.websockets")` ## Sub-modules `[slack_bolt.adapter.socket_mode.aiohttp](aiohttp/index.html "slack_bolt.adapter.socket_mode.aiohttp")` [`aiohttp`](https://pypi.org/project/aiohttp/) based implementation / asyncio compatible `[slack_bolt.adapter.socket_mode.async_base_handler](async_base_handler.html "slack_bolt.adapter.socket_mode.async_base_handler")` The base class of asyncio-based Socket Mode client implementation `[slack_bolt.adapter.socket_mode.async_handler](async_handler.html "slack_bolt.adapter.socket_mode.async_handler")` Default implementation is the aiohttp-based one. `[slack_bolt.adapter.socket_mode.async_internals](async_internals.html "slack_bolt.adapter.socket_mode.async_internals")` Internal functions `[slack_bolt.adapter.socket_mode.base_handler](base_handler.html "slack_bolt.adapter.socket_mode.base_handler")` The base class of Socket Mode client implementation. If you want to build asyncio-based ones, use `AsyncBaseSocketModeHandler` instead. `[slack_bolt.adapter.socket_mode.builtin](builtin/index.html "slack_bolt.adapter.socket_mode.builtin")` The built-in implementation, which does not have any external dependencies `[slack_bolt.adapter.socket_mode.internals](internals.html "slack_bolt.adapter.socket_mode.internals")` Internal functions `[slack_bolt.adapter.socket_mode.websocket_client](websocket_client/index.html "slack_bolt.adapter.socket_mode.websocket_client")` [`websocket-client`](https://pypi.org/project/websocket-client/) based implementation `[slack_bolt.adapter.socket_mode.websockets](websockets/index.html "slack_bolt.adapter.socket_mode.websockets")` [`websockets`](https://pypi.org/project/websockets/) based implementation / asyncio compatible ## Classes `class SocketModeHandler (app: [App](../../app/app.html#slack_bolt.app.app.App "slack_bolt.app.app.App"), app_token: str | None = None, logger: logging.Logger | None = None, web_client: slack_sdk.web.client.WebClient | None = None, proxy: str | None = None, proxy_headers: Dict[str, str] | None = None, auto_reconnect_enabled: bool = True, trace_enabled: bool = False, all_message_trace_enabled: bool = False, ping_pong_trace_enabled: bool = False, ping_interval: float = 10, receive_buffer_size: int = 1024, concurrency: int = 10)` Expand source code ``` class SocketModeHandler(BaseSocketModeHandler): app: App app_token: str client: SocketModeClient def __init__( self, app: App, app_token: Optional[str] = None, logger: Optional[Logger] = None, web_client: Optional[WebClient] = None, proxy: Optional[str] = None, proxy_headers: Optional[Dict[str, str]] = None, auto_reconnect_enabled: bool = True, trace_enabled: bool = False, all_message_trace_enabled: bool = False, ping_pong_trace_enabled: bool = False, ping_interval: float = 10, receive_buffer_size: int = 1024, concurrency: int = 10, ): """Socket Mode adapter for Bolt apps Args: app: The Bolt app app_token: App-level token starting with `xapp-` logger: Custom logger web_client: custom `slack_sdk.web.WebClient` instance proxy: HTTP proxy URL proxy_headers: Additional request header for proxy connections auto_reconnect_enabled: True if the auto-reconnect logic works trace_enabled: True if trace-level logging is enabled all_message_trace_enabled: True if trace-logging for all received WebSocket messages is enabled ping_pong_trace_enabled: True if trace-logging for all ping-pong communications ping_interval: The ping-pong internal (seconds) receive_buffer_size: The data length for a single socket recv operation concurrency: The size of the underlying thread pool """ self.app = app self.app_token = app_token or os.environ["SLACK_APP_TOKEN"] self.client = SocketModeClient( app_token=self.app_token, logger=logger if logger is not None else app.logger, web_client=web_client if web_client is not None else app.client, proxy=proxy if proxy is not None else app.client.proxy, proxy_headers=proxy_headers, auto_reconnect_enabled=auto_reconnect_enabled, trace_enabled=trace_enabled, all_message_trace_enabled=all_message_trace_enabled, ping_pong_trace_enabled=ping_pong_trace_enabled, ping_interval=ping_interval, receive_buffer_size=receive_buffer_size, concurrency=concurrency, ) self.client.socket_mode_request_listeners.append(self.handle) # type: ignore[arg-type] def handle(self, client: SocketModeClient, req: SocketModeRequest) -> None: # type: ignore[override] start = time() bolt_resp: BoltResponse = run_bolt_app(self.app, req) send_response(client, req, bolt_resp, start) ``` Socket Mode adapter for Bolt apps ## Args **`app`** The Bolt app **`app_token`** App-level token starting with `xapp-` **`logger`** Custom logger **`web_client`** custom `slack_sdk.web.WebClient` instance **`proxy`** HTTP proxy URL **`proxy_headers`** Additional request header for proxy connections **`auto_reconnect_enabled`** True if the auto-reconnect logic works **`trace_enabled`** True if trace-level logging is enabled **`all_message_trace_enabled`** True if trace-logging for all received WebSocket messages is enabled **`ping_pong_trace_enabled`** True if trace-logging for all ping-pong communications **`ping_interval`** The ping-pong internal (seconds) **`receive_buffer_size`** The data length for a single socket recv operation **`concurrency`** The size of the underlying thread pool ### Ancestors * [BaseSocketModeHandler](base_handler.html#slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler "slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler") ### Class variables `var app_token : str` The type of the None singleton. ### Inherited members * `**[BaseSocketModeHandler](base_handler.html#slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler "slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler")**`: * `[app](base_handler.html#slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.app "slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.app")` * `[client](base_handler.html#slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.client "slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.client")` * `[close](base_handler.html#slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.close "slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.close")` * `[connect](base_handler.html#slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.connect "slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.connect")` * `[disconnect](base_handler.html#slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.disconnect "slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.disconnect")` * `[handle](base_handler.html#slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.handle "slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.handle")` * `[start](base_handler.html#slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.start "slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.start")` --- Source: https://docs.slack.dev/tools/bolt-python/reference/adapter/socket_mode/aiohttp # Module slack_bolt.adapter.socket_mode.aiohttp [`aiohttp`](https://pypi.org/project/aiohttp/) based implementation / asyncio compatible ## Classes `class AsyncSocketModeHandler (app: [AsyncApp](../../../app/async_app.html#slack_bolt.app.async_app.AsyncApp "slack_bolt.app.async_app.AsyncApp"), app_token: str | None = None, logger: logging.Logger | None = None, web_client: slack_sdk.web.async_client.AsyncWebClient | None = None, proxy: str | None = None, ping_interval: float = 10, loop: asyncio.events.AbstractEventLoop | None = None)` Expand source code ``` class AsyncSocketModeHandler(AsyncBaseSocketModeHandler): app: AsyncApp app_token: str client: SocketModeClient def __init__( self, app: AsyncApp, app_token: Optional[str] = None, logger: Optional[Logger] = None, web_client: Optional[AsyncWebClient] = None, proxy: Optional[str] = None, ping_interval: float = 10, loop: Optional[AbstractEventLoop] = None, ): self.app = app self.app_token = app_token or os.environ["SLACK_APP_TOKEN"] self.client = SocketModeClient( app_token=self.app_token, logger=logger if logger is not None else app.logger, web_client=web_client if web_client is not None else app.client, proxy=proxy, ping_interval=ping_interval, loop=loop, ) self.client.socket_mode_request_listeners.append(self.handle) # type: ignore[arg-type] async def handle(self, client: SocketModeClient, req: SocketModeRequest) -> None: # type: ignore[override] start = time() bolt_resp: BoltResponse = await run_async_bolt_app(self.app, req) await send_async_response(client, req, bolt_resp, start) ``` ### Ancestors * [AsyncBaseSocketModeHandler](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler") ### Class variables `var app_token : str` The type of the None singleton. ### Inherited members * `**[AsyncBaseSocketModeHandler](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler")**`: * `[app](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.app "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.app")` * `[client](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.client "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.client")` * `[close_async](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.close_async "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.close_async")` * `[connect_async](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.connect_async "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.connect_async")` * `[disconnect_async](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.disconnect_async "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.disconnect_async")` * `[handle](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.handle "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.handle")` * `[start_async](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.start_async "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.start_async")` `class SocketModeHandler (app: [App](../../../app/app.html#slack_bolt.app.app.App "slack_bolt.app.app.App"), app_token: str | None = None, logger: logging.Logger | None = None, web_client: slack_sdk.web.async_client.AsyncWebClient | None = None, proxy: str | None = None, ping_interval: float = 10)` Expand source code ``` class SocketModeHandler(AsyncBaseSocketModeHandler): app: App app_token: str client: SocketModeClient def __init__( self, app: App, app_token: Optional[str] = None, logger: Optional[Logger] = None, web_client: Optional[AsyncWebClient] = None, proxy: Optional[str] = None, ping_interval: float = 10, ): """Socket Mode adapter for Bolt apps Args: app: The Bolt app app_token: App-level token starting with `xapp-` logger: Custom logger web_client: custom `slack_sdk.web.WebClient` instance proxy: HTTP proxy URL ping_interval: The ping-pong internal (seconds) """ self.app = app self.app_token = app_token or os.environ["SLACK_APP_TOKEN"] self.client = SocketModeClient( app_token=self.app_token, logger=logger if logger is not None else app.logger, web_client=web_client if web_client is not None else app.client, # type: ignore[arg-type] proxy=proxy, ping_interval=ping_interval, ) self.client.socket_mode_request_listeners.append(self.handle) # type: ignore[arg-type] async def handle(self, client: SocketModeClient, req: SocketModeRequest) -> None: # type: ignore[override] start = time() bolt_resp: BoltResponse = run_bolt_app(self.app, req) await send_async_response(client, req, bolt_resp, start) ``` Socket Mode adapter for Bolt apps ## Args **`app`** The Bolt app **`app_token`** App-level token starting with `xapp-` **`logger`** Custom logger **`web_client`** custom `slack_sdk.web.WebClient` instance **`proxy`** HTTP proxy URL **`ping_interval`** The ping-pong internal (seconds) ### Ancestors * [AsyncBaseSocketModeHandler](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler") ### Class variables `var app_token : str` The type of the None singleton. ### Inherited members * `**[AsyncBaseSocketModeHandler](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler")**`: * `[app](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.app "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.app")` * `[client](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.client "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.client")` * `[close_async](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.close_async "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.close_async")` * `[connect_async](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.connect_async "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.connect_async")` * `[disconnect_async](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.disconnect_async "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.disconnect_async")` * `[handle](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.handle "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.handle")` * `[start_async](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.start_async "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.start_async")` --- Source: https://docs.slack.dev/tools/bolt-python/reference/adapter/socket_mode/builtin # Module slack_bolt.adapter.socket_mode.builtin The built-in implementation, which does not have any external dependencies ## Classes `class SocketModeHandler (app: [App](../../../app/app.html#slack_bolt.app.app.App "slack_bolt.app.app.App"), app_token: str | None = None, logger: logging.Logger | None = None, web_client: slack_sdk.web.client.WebClient | None = None, proxy: str | None = None, proxy_headers: Dict[str, str] | None = None, auto_reconnect_enabled: bool = True, trace_enabled: bool = False, all_message_trace_enabled: bool = False, ping_pong_trace_enabled: bool = False, ping_interval: float = 10, receive_buffer_size: int = 1024, concurrency: int = 10)` Expand source code ``` class SocketModeHandler(BaseSocketModeHandler): app: App app_token: str client: SocketModeClient def __init__( self, app: App, app_token: Optional[str] = None, logger: Optional[Logger] = None, web_client: Optional[WebClient] = None, proxy: Optional[str] = None, proxy_headers: Optional[Dict[str, str]] = None, auto_reconnect_enabled: bool = True, trace_enabled: bool = False, all_message_trace_enabled: bool = False, ping_pong_trace_enabled: bool = False, ping_interval: float = 10, receive_buffer_size: int = 1024, concurrency: int = 10, ): """Socket Mode adapter for Bolt apps Args: app: The Bolt app app_token: App-level token starting with `xapp-` logger: Custom logger web_client: custom `slack_sdk.web.WebClient` instance proxy: HTTP proxy URL proxy_headers: Additional request header for proxy connections auto_reconnect_enabled: True if the auto-reconnect logic works trace_enabled: True if trace-level logging is enabled all_message_trace_enabled: True if trace-logging for all received WebSocket messages is enabled ping_pong_trace_enabled: True if trace-logging for all ping-pong communications ping_interval: The ping-pong internal (seconds) receive_buffer_size: The data length for a single socket recv operation concurrency: The size of the underlying thread pool """ self.app = app self.app_token = app_token or os.environ["SLACK_APP_TOKEN"] self.client = SocketModeClient( app_token=self.app_token, logger=logger if logger is not None else app.logger, web_client=web_client if web_client is not None else app.client, proxy=proxy if proxy is not None else app.client.proxy, proxy_headers=proxy_headers, auto_reconnect_enabled=auto_reconnect_enabled, trace_enabled=trace_enabled, all_message_trace_enabled=all_message_trace_enabled, ping_pong_trace_enabled=ping_pong_trace_enabled, ping_interval=ping_interval, receive_buffer_size=receive_buffer_size, concurrency=concurrency, ) self.client.socket_mode_request_listeners.append(self.handle) # type: ignore[arg-type] def handle(self, client: SocketModeClient, req: SocketModeRequest) -> None: # type: ignore[override] start = time() bolt_resp: BoltResponse = run_bolt_app(self.app, req) send_response(client, req, bolt_resp, start) ``` Socket Mode adapter for Bolt apps ## Args **`app`** The Bolt app **`app_token`** App-level token starting with `xapp-` **`logger`** Custom logger **`web_client`** custom `slack_sdk.web.WebClient` instance **`proxy`** HTTP proxy URL **`proxy_headers`** Additional request header for proxy connections **`auto_reconnect_enabled`** True if the auto-reconnect logic works **`trace_enabled`** True if trace-level logging is enabled **`all_message_trace_enabled`** True if trace-logging for all received WebSocket messages is enabled **`ping_pong_trace_enabled`** True if trace-logging for all ping-pong communications **`ping_interval`** The ping-pong internal (seconds) **`receive_buffer_size`** The data length for a single socket recv operation **`concurrency`** The size of the underlying thread pool ### Ancestors * [BaseSocketModeHandler](../base_handler.html#slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler "slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler") ### Class variables `var app_token : str` The type of the None singleton. ### Inherited members * `**[BaseSocketModeHandler](../base_handler.html#slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler "slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler")**`: * `[app](../base_handler.html#slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.app "slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.app")` * `[client](../base_handler.html#slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.client "slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.client")` * `[close](../base_handler.html#slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.close "slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.close")` * `[connect](../base_handler.html#slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.connect "slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.connect")` * `[disconnect](../base_handler.html#slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.disconnect "slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.disconnect")` * `[handle](../base_handler.html#slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.handle "slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.handle")` * `[start](../base_handler.html#slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.start "slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.start")` --- Source: https://docs.slack.dev/tools/bolt-python/reference/adapter/socket_mode/websocket_client # Module slack_bolt.adapter.socket_mode.websocket_client [`websocket-client`](https://pypi.org/project/websocket-client/) based implementation ## Classes `class SocketModeHandler (app: [App](../../../app/app.html#slack_bolt.app.app.App "slack_bolt.app.app.App"), app_token: str | None = None, logger: logging.Logger | None = None, web_client: slack_sdk.web.client.WebClient | None = None, ping_interval: float = 10, concurrency: int = 10, http_proxy_host: str | None = None, http_proxy_port: int | None = None, http_proxy_auth: Tuple[str, str] | None = None, proxy_type: str | None = None, trace_enabled: bool = False)` Expand source code ``` class SocketModeHandler(BaseSocketModeHandler): app: App app_token: str client: SocketModeClient def __init__( self, app: App, app_token: Optional[str] = None, logger: Optional[Logger] = None, web_client: Optional[WebClient] = None, ping_interval: float = 10, concurrency: int = 10, http_proxy_host: Optional[str] = None, http_proxy_port: Optional[int] = None, http_proxy_auth: Optional[Tuple[str, str]] = None, proxy_type: Optional[str] = None, trace_enabled: bool = False, ): """Socket Mode adapter for Bolt apps Args: app: The Bolt app app_token: App-level token starting with `xapp-` logger: Custom logger web_client: custom `slack_sdk.web.WebClient` instance ping_interval: The ping-pong internal (seconds) concurrency: The size of the underlying thread pool http_proxy_host: HTTP proxy host http_proxy_port: HTTP proxy port http_proxy_auth: HTTP proxy authentication (username, password) proxy_type: Proxy type trace_enabled: True if trace-level logging is enabled """ self.app = app self.app_token = app_token or os.environ["SLACK_APP_TOKEN"] self.client = SocketModeClient( app_token=self.app_token, logger=logger if logger is not None else app.logger, web_client=web_client if web_client is not None else app.client, ping_interval=ping_interval, concurrency=concurrency, http_proxy_host=http_proxy_host, http_proxy_port=http_proxy_port, http_proxy_auth=http_proxy_auth, proxy_type=proxy_type, trace_enabled=trace_enabled, ) self.client.socket_mode_request_listeners.append(self.handle) # type: ignore[arg-type] def handle(self, client: SocketModeClient, req: SocketModeRequest) -> None: # type: ignore[override] start = time() bolt_resp: BoltResponse = run_bolt_app(self.app, req) send_response(client, req, bolt_resp, start) ``` Socket Mode adapter for Bolt apps ## Args **`app`** The Bolt app **`app_token`** App-level token starting with `xapp-` **`logger`** Custom logger **`web_client`** custom `slack_sdk.web.WebClient` instance **`ping_interval`** The ping-pong internal (seconds) **`concurrency`** The size of the underlying thread pool **`http_proxy_host`** HTTP proxy host **`http_proxy_port`** HTTP proxy port **`http_proxy_auth`** HTTP proxy authentication (username, password) **`proxy_type`** Proxy type **`trace_enabled`** True if trace-level logging is enabled ### Ancestors * [BaseSocketModeHandler](../base_handler.html#slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler "slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler") ### Class variables `var app_token : str` The type of the None singleton. ### Inherited members * `**[BaseSocketModeHandler](../base_handler.html#slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler "slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler")**`: * `[app](../base_handler.html#slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.app "slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.app")` * `[client](../base_handler.html#slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.client "slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.client")` * `[close](../base_handler.html#slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.close "slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.close")` * `[connect](../base_handler.html#slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.connect "slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.connect")` * `[disconnect](../base_handler.html#slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.disconnect "slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.disconnect")` * `[handle](../base_handler.html#slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.handle "slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.handle")` * `[start](../base_handler.html#slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.start "slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.start")` --- Source: https://docs.slack.dev/tools/bolt-python/reference/adapter/socket_mode/websockets # Module slack_bolt.adapter.socket_mode.websockets [`websockets`](https://pypi.org/project/websockets/) based implementation / asyncio compatible ## Classes `class AsyncSocketModeHandler (app: [AsyncApp](../../../app/async_app.html#slack_bolt.app.async_app.AsyncApp "slack_bolt.app.async_app.AsyncApp"), app_token: str | None = None, logger: logging.Logger | None = None, web_client: slack_sdk.web.async_client.AsyncWebClient | None = None, ping_interval: float = 10)` Expand source code ``` class AsyncSocketModeHandler(AsyncBaseSocketModeHandler): app: AsyncApp app_token: str client: SocketModeClient def __init__( self, app: AsyncApp, app_token: Optional[str] = None, logger: Optional[Logger] = None, web_client: Optional[AsyncWebClient] = None, ping_interval: float = 10, ): self.app = app self.app_token = app_token or os.environ["SLACK_APP_TOKEN"] self.client = SocketModeClient( app_token=self.app_token, logger=logger if logger is not None else app.logger, web_client=web_client if web_client is not None else app.client, ping_interval=ping_interval, ) self.client.socket_mode_request_listeners.append(self.handle) # type: ignore[arg-type] async def handle(self, client: SocketModeClient, req: SocketModeRequest) -> None: # type: ignore[override] start = time() bolt_resp: BoltResponse = await run_async_bolt_app(self.app, req) await send_async_response(client, req, bolt_resp, start) ``` ### Ancestors * [AsyncBaseSocketModeHandler](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler") ### Class variables `var app_token : str` The type of the None singleton. ### Inherited members * `**[AsyncBaseSocketModeHandler](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler")**`: * `[app](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.app "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.app")` * `[client](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.client "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.client")` * `[close_async](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.close_async "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.close_async")` * `[connect_async](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.connect_async "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.connect_async")` * `[disconnect_async](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.disconnect_async "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.disconnect_async")` * `[handle](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.handle "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.handle")` * `[start_async](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.start_async "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.start_async")` `class SocketModeHandler (app: [App](../../../app/app.html#slack_bolt.app.app.App "slack_bolt.app.app.App"), app_token: str | None = None, logger: logging.Logger | None = None, web_client: slack_sdk.web.async_client.AsyncWebClient | None = None, ping_interval: float = 10)` Expand source code ``` class SocketModeHandler(AsyncBaseSocketModeHandler): app: App app_token: str client: SocketModeClient def __init__( self, app: App, app_token: Optional[str] = None, logger: Optional[Logger] = None, web_client: Optional[AsyncWebClient] = None, ping_interval: float = 10, ): """Socket Mode adapter for Bolt apps. Please note that this adapter does not support proxy configuration as the underlying websockets module does not support proxy-wired connections. If you use proxy, consider using one of the other Socket Mode adapters. Args: app: The Bolt app app_token: App-level token starting with `xapp-` logger: Custom logger web_client: custom `slack_sdk.web.WebClient` instance ping_interval: The ping-pong internal (seconds) """ self.app = app self.app_token = app_token or os.environ["SLACK_APP_TOKEN"] self.client = SocketModeClient( app_token=self.app_token, logger=logger if logger is not None else app.logger, web_client=web_client if web_client is not None else app.client, # type: ignore[arg-type] ping_interval=ping_interval, ) self.client.socket_mode_request_listeners.append(self.handle) # type: ignore[arg-type] async def handle(self, client: SocketModeClient, req: SocketModeRequest) -> None: # type: ignore[override] start = time() bolt_resp: BoltResponse = run_bolt_app(self.app, req) await send_async_response(client, req, bolt_resp, start) ``` Socket Mode adapter for Bolt apps. Please note that this adapter does not support proxy configuration as the underlying websockets module does not support proxy-wired connections. If you use proxy, consider using one of the other Socket Mode adapters. ## Args **`app`** The Bolt app **`app_token`** App-level token starting with `xapp-` **`logger`** Custom logger **`web_client`** custom `slack_sdk.web.WebClient` instance **`ping_interval`** The ping-pong internal (seconds) ### Ancestors * [AsyncBaseSocketModeHandler](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler") ### Class variables `var app_token : str` The type of the None singleton. ### Inherited members * `**[AsyncBaseSocketModeHandler](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler")**`: * `[app](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.app "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.app")` * `[client](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.client "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.client")` * `[close_async](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.close_async "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.close_async")` * `[connect_async](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.connect_async "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.connect_async")` * `[disconnect_async](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.disconnect_async "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.disconnect_async")` * `[handle](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.handle "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.handle")` * `[start_async](../async_base_handler.html#slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.start_async "slack_bolt.adapter.socket_mode.async_base_handler.AsyncBaseSocketModeHandler.start_async")` --- Source: https://docs.slack.dev/tools/bolt-python/reference/adapter/starlette # Module slack_bolt.adapter.starlette ## Sub-modules `[slack_bolt.adapter.starlette.async_handler](async_handler.html "slack_bolt.adapter.starlette.async_handler")` `[slack_bolt.adapter.starlette.handler](handler.html "slack_bolt.adapter.starlette.handler")` ## Classes `class SlackRequestHandler (app: [App](../../app/app.html#slack_bolt.app.app.App "slack_bolt.app.app.App"))` Expand source code ``` class SlackRequestHandler: def __init__(self, app: App): self.app = app async def handle(self, req: Request, addition_context_properties: Optional[Dict[str, Any]] = None) -> Response: body = await req.body() if req.method == "GET": if self.app.oauth_flow is not None: oauth_flow: OAuthFlow = self.app.oauth_flow if req.url.path == oauth_flow.install_path: bolt_resp = oauth_flow.handle_installation(to_bolt_request(req, body, addition_context_properties)) return to_starlette_response(bolt_resp) elif req.url.path == oauth_flow.redirect_uri_path: bolt_resp = oauth_flow.handle_callback(to_bolt_request(req, body, addition_context_properties)) return to_starlette_response(bolt_resp) elif req.method == "POST": bolt_resp = self.app.dispatch(to_bolt_request(req, body, addition_context_properties)) return to_starlette_response(bolt_resp) return Response( status_code=404, content="Not found", ) ``` ### Methods `async def handle(self, req: starlette.requests.Request, addition_context_properties: Dict[str, Any] | None = None) ‑> starlette.responses.Response` Expand source code ``` async def handle(self, req: Request, addition_context_properties: Optional[Dict[str, Any]] = None) -> Response: body = await req.body() if req.method == "GET": if self.app.oauth_flow is not None: oauth_flow: OAuthFlow = self.app.oauth_flow if req.url.path == oauth_flow.install_path: bolt_resp = oauth_flow.handle_installation(to_bolt_request(req, body, addition_context_properties)) return to_starlette_response(bolt_resp) elif req.url.path == oauth_flow.redirect_uri_path: bolt_resp = oauth_flow.handle_callback(to_bolt_request(req, body, addition_context_properties)) return to_starlette_response(bolt_resp) elif req.method == "POST": bolt_resp = self.app.dispatch(to_bolt_request(req, body, addition_context_properties)) return to_starlette_response(bolt_resp) return Response( status_code=404, content="Not found", ) ``` --- Source: https://docs.slack.dev/tools/bolt-python/reference/adapter/tornado # Module slack_bolt.adapter.tornado ## Sub-modules `[slack_bolt.adapter.tornado.async_handler](async_handler.html "slack_bolt.adapter.tornado.async_handler")` `[slack_bolt.adapter.tornado.handler](handler.html "slack_bolt.adapter.tornado.handler")` ## Classes `class SlackEventsHandler (application: Application, request: tornado.httputil.HTTPServerRequest, **kwargs: Any)` Expand source code ``` class SlackEventsHandler(RequestHandler): def initialize(self, app: App): self.app = app def post(self): bolt_resp: BoltResponse = self.app.dispatch(to_bolt_request(self.request)) set_response(self, bolt_resp) return ``` Base class for HTTP request handlers. Subclasses must define at least one of the methods defined in the "Entry points" section below. Applications should not construct `RequestHandler` objects directly and subclasses should not override `__init__` (override `~RequestHandler.initialize` instead). ### Ancestors * tornado.web.RequestHandler ### Methods `def initialize(self, app: [App](../../app/app.html#slack_bolt.app.app.App "slack_bolt.app.app.App"))` Expand source code ``` def initialize(self, app: App): self.app = app ``` `def post(self)` Expand source code ``` def post(self): bolt_resp: BoltResponse = self.app.dispatch(to_bolt_request(self.request)) set_response(self, bolt_resp) return ``` `class SlackOAuthHandler (application: Application, request: tornado.httputil.HTTPServerRequest, **kwargs: Any)` Expand source code ``` class SlackOAuthHandler(RequestHandler): def initialize(self, app: App): self.app = app def get(self): if self.app.oauth_flow is not None: oauth_flow: OAuthFlow = self.app.oauth_flow if self.request.path == oauth_flow.install_path: bolt_resp = oauth_flow.handle_installation(to_bolt_request(self.request)) set_response(self, bolt_resp) return elif self.request.path == oauth_flow.redirect_uri_path: bolt_resp = oauth_flow.handle_callback(to_bolt_request(self.request)) set_response(self, bolt_resp) return self.set_status(404) ``` Base class for HTTP request handlers. Subclasses must define at least one of the methods defined in the "Entry points" section below. Applications should not construct `RequestHandler` objects directly and subclasses should not override `__init__` (override `~RequestHandler.initialize` instead). ### Ancestors * tornado.web.RequestHandler ### Methods `def get(self)` Expand source code ``` def get(self): if self.app.oauth_flow is not None: oauth_flow: OAuthFlow = self.app.oauth_flow if self.request.path == oauth_flow.install_path: bolt_resp = oauth_flow.handle_installation(to_bolt_request(self.request)) set_response(self, bolt_resp) return elif self.request.path == oauth_flow.redirect_uri_path: bolt_resp = oauth_flow.handle_callback(to_bolt_request(self.request)) set_response(self, bolt_resp) return self.set_status(404) ``` `def initialize(self, app: [App](../../app/app.html#slack_bolt.app.app.App "slack_bolt.app.app.App"))` Expand source code ``` def initialize(self, app: App): self.app = app ``` --- Source: https://docs.slack.dev/tools/bolt-python/reference/adapter/wsgi # Module slack_bolt.adapter.wsgi ## Sub-modules `[slack_bolt.adapter.wsgi.handler](handler.html "slack_bolt.adapter.wsgi.handler")` `[slack_bolt.adapter.wsgi.http_request](http_request.html "slack_bolt.adapter.wsgi.http_request")` `[slack_bolt.adapter.wsgi.http_response](http_response.html "slack_bolt.adapter.wsgi.http_response")` `[slack_bolt.adapter.wsgi.internals](internals.html "slack_bolt.adapter.wsgi.internals")` ## Classes `class SlackRequestHandler (app: [App](../../app/app.html#slack_bolt.app.app.App "slack_bolt.app.app.App"), path: str = '/slack/events')` Expand source code ``` class SlackRequestHandler: def __init__(self, app: App, path: str = "/slack/events"): """Setup Bolt as a WSGI web framework, this will make your application compatible with WSGI web servers. This can be used for production deployments. With the default settings, `http://localhost:3000/slack/events` Run Bolt with [gunicorn](https://gunicorn.org/) # Python app = App() api = SlackRequestHandler(app) # bash export SLACK_SIGNING_SECRET=*** export SLACK_BOT_TOKEN=xoxb-*** gunicorn app:api -b 0.0.0.0:3000 --log-level debug Args: app: Your bolt application path: The path to handle request from Slack (Default: `/slack/events`) """ self.path = path self.app = app def dispatch(self, request: WsgiHttpRequest) -> BoltResponse: return self.app.dispatch( BoltRequest(body=request.get_body(), query=request.query_string, headers=request.get_headers()) ) def handle_installation(self, request: WsgiHttpRequest) -> BoltResponse: return self.app.oauth_flow.handle_installation( # type: ignore[union-attr] BoltRequest(body=request.get_body(), query=request.query_string, headers=request.get_headers()) ) def handle_callback(self, request: WsgiHttpRequest) -> BoltResponse: return self.app.oauth_flow.handle_callback( # type: ignore[union-attr] BoltRequest(body=request.get_body(), query=request.query_string, headers=request.get_headers()) ) def _get_http_response(self, request: WsgiHttpRequest) -> WsgiHttpResponse: if request.method == "GET": if self.app.oauth_flow is not None: if request.path == self.app.oauth_flow.install_path: bolt_response = self.handle_installation(request) return WsgiHttpResponse( status=bolt_response.status, headers=bolt_response.headers, body=bolt_response.body ) elif request.path == self.app.oauth_flow.redirect_uri_path: bolt_response = self.handle_callback(request) return WsgiHttpResponse( status=bolt_response.status, headers=bolt_response.headers, body=bolt_response.body ) if request.method == "POST" and request.path == self.path: bolt_response = self.dispatch(request) return WsgiHttpResponse(status=bolt_response.status, headers=bolt_response.headers, body=bolt_response.body) return WsgiHttpResponse(status=404, headers={"content-type": ["text/plain;charset=utf-8"]}, body="Not Found") def __call__( self, environ: Dict[str, Any], start_response: Callable[[str, List[Tuple[str, str]]], None], ) -> Iterable[bytes]: request = WsgiHttpRequest(environ) if "HTTP" in request.protocol: response: WsgiHttpResponse = self._get_http_response( request=request, ) start_response(response.status, response.get_headers()) return response.get_body() raise TypeError(f"Unsupported SERVER_PROTOCOL: {request.protocol}") ``` Setup Bolt as a WSGI web framework, this will make your application compatible with WSGI web servers. This can be used for production deployments. With the default settings, `http://localhost:3000/slack/events` Run Bolt with [gunicorn](https://gunicorn.org/) # Python ``` app = App() api = SlackRequestHandler(app) ``` # bash ``` export SLACK_SIGNING_SECRET=*** export SLACK_BOT_TOKEN=xoxb-*** gunicorn app:api -b 0.0.0.0:3000 --log-level debug ``` ## Args **`app`** Your bolt application **`path`** The path to handle request from Slack (Default: `/slack/events`) ### Methods `def dispatch(self, request: [WsgiHttpRequest](http_request.html#slack_bolt.adapter.wsgi.http_request.WsgiHttpRequest "slack_bolt.adapter.wsgi.http_request.WsgiHttpRequest")) ‑> [BoltResponse](../../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse")` Expand source code ``` def dispatch(self, request: WsgiHttpRequest) -> BoltResponse: return self.app.dispatch( BoltRequest(body=request.get_body(), query=request.query_string, headers=request.get_headers()) ) ``` `def handle_callback(self, request: [WsgiHttpRequest](http_request.html#slack_bolt.adapter.wsgi.http_request.WsgiHttpRequest "slack_bolt.adapter.wsgi.http_request.WsgiHttpRequest")) ‑> [BoltResponse](../../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse")` Expand source code ``` def handle_callback(self, request: WsgiHttpRequest) -> BoltResponse: return self.app.oauth_flow.handle_callback( # type: ignore[union-attr] BoltRequest(body=request.get_body(), query=request.query_string, headers=request.get_headers()) ) ``` `def handle_installation(self, request: [WsgiHttpRequest](http_request.html#slack_bolt.adapter.wsgi.http_request.WsgiHttpRequest "slack_bolt.adapter.wsgi.http_request.WsgiHttpRequest")) ‑> [BoltResponse](../../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse")` Expand source code ``` def handle_installation(self, request: WsgiHttpRequest) -> BoltResponse: return self.app.oauth_flow.handle_installation( # type: ignore[union-attr] BoltRequest(body=request.get_body(), query=request.query_string, headers=request.get_headers()) ) ``` --- Source: https://docs.slack.dev/tools/bolt-python/reference/app # Module slack_bolt.app Application interface in Bolt. For most use cases, we recommend using `[slack_bolt.app.app](app.html "slack_bolt.app.app")`. If you already have knowledge about asyncio and prefer the programming model, you can use `[slack_bolt.app.async_app](async_app.html "slack_bolt.app.async_app")` for building async apps. ## Sub-modules `[slack_bolt.app.app](app.html "slack_bolt.app.app")` `[slack_bolt.app.async_app](async_app.html "slack_bolt.app.async_app")` `[slack_bolt.app.async_server](async_server.html "slack_bolt.app.async_server")` ## Classes `class App (*, logger: logging.Logger | None = None, name: str | None = None, process_before_response: bool = False, raise_error_for_unhandled_request: bool = False, signing_secret: str | None = None, token: str | None = None, token_verification_enabled: bool = True, client: slack_sdk.web.client.WebClient | None = None, before_authorize: [Middleware](../middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware") | Callable[..., Any] | None = None, authorize: Callable[..., [AuthorizeResult](../authorization/authorize_result.html#slack_bolt.authorization.authorize_result.AuthorizeResult "slack_bolt.authorization.authorize_result.AuthorizeResult")] | None = None, user_facing_authorize_error_message: str | None = None, installation_store: slack_sdk.oauth.installation_store.installation_store.InstallationStore | None = None, installation_store_bot_only: bool | None = None, request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, ignoring_self_assistant_message_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, oauth_settings: [OAuthSettings](../oauth/oauth_settings.html#slack_bolt.oauth.oauth_settings.OAuthSettings "slack_bolt.oauth.oauth_settings.OAuthSettings") | None = None, oauth_flow: [OAuthFlow](../oauth/oauth_flow.html#slack_bolt.oauth.oauth_flow.OAuthFlow "slack_bolt.oauth.oauth_flow.OAuthFlow") | None = None, verification_token: str | None = None, listener_executor: concurrent.futures._base.Executor | None = None, assistant_thread_context_store: [AssistantThreadContextStore](../context/assistant/thread_context_store/store.html#slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore "slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore") | None = None, attaching_conversation_kwargs_enabled: bool = True)` Expand source code ``` class App: def __init__( self, *, logger: Optional[logging.Logger] = None, # Used in logger name: Optional[str] = None, # Set True when you run this app on a FaaS platform process_before_response: bool = False, # Set True if you want to handle an unhandled request as an exception raise_error_for_unhandled_request: bool = False, # Basic Information > Credentials > Signing Secret signing_secret: Optional[str] = None, # for single-workspace apps token: Optional[str] = None, token_verification_enabled: bool = True, client: Optional[WebClient] = None, # for multi-workspace apps before_authorize: Optional[Union[Middleware, Callable[..., Any]]] = None, authorize: Optional[Callable[..., AuthorizeResult]] = None, user_facing_authorize_error_message: Optional[str] = None, installation_store: Optional[InstallationStore] = None, # for either only bot scope usage or v1.0.x compatibility installation_store_bot_only: Optional[bool] = None, # for customizing the built-in middleware request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, ignoring_self_assistant_message_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, # for the OAuth flow oauth_settings: Optional[OAuthSettings] = None, oauth_flow: Optional[OAuthFlow] = None, # No need to set (the value is used only in response to ssl_check requests) verification_token: Optional[str] = None, # Set this one only when you want to customize the executor listener_executor: Optional[Executor] = None, # for AI Agents & Assistants assistant_thread_context_store: Optional[AssistantThreadContextStore] = None, attaching_conversation_kwargs_enabled: bool = True, ): """Bolt App that provides functionalities to register middleware/listeners. import os from slack_bolt import App # Initializes your app with your bot token and signing secret app = App( token=os.environ.get("SLACK_BOT_TOKEN"), signing_secret=os.environ.get("SLACK_SIGNING_SECRET") ) # Listens to incoming messages that contain "hello" @app.message("hello") def message_hello(message, say): # say() sends a message to the channel where the event was triggered say(f"Hey there <@{message['user']}>!") # Start your app if __name__ == "__main__": app.start(port=int(os.environ.get("PORT", 3000))) Refer to https://docs.slack.dev/tools/bolt-python/creating-an-app for details. If you would like to build an OAuth app for enabling the app to run with multiple workspaces, refer to https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth to learn how to configure the app. Args: logger: The custom logger that can be used in this app. name: The application name that will be used in logging. If absent, the source file name will be used. process_before_response: True if this app runs on Function as a Service. (Default: False) raise_error_for_unhandled_request: True if you want to raise exceptions for unhandled requests and use @app.error listeners instead of the built-in handler, which pints warning logs and returns 404 to Slack (Default: False) signing_secret: The Signing Secret value used for verifying requests from Slack. token: The bot/user access token required only for single-workspace app. token_verification_enabled: Verifies the validity of the given token if True. client: The singleton `slack_sdk.WebClient` instance for this app. before_authorize: A global middleware that can be executed right before authorize function authorize: The function to authorize an incoming request from Slack by checking if there is a team/user in the installation data. user_facing_authorize_error_message: The user-facing error message to display when the app is installed but the installation is not managed by this app's installation store installation_store: The module offering save/find operations of installation data installation_store_bot_only: Use `InstallationStore#find_bot()` if True (Default: False) request_verification_enabled: False if you would like to disable the built-in middleware (Default: True). `RequestVerification` is a built-in middleware that verifies the signature in HTTP Mode requests. Make sure if it's safe enough when you turn a built-in middleware off. We strongly recommend using RequestVerification for better security. If you have a proxy that verifies request signature in front of the Bolt app, it's totally fine to disable RequestVerification to avoid duplication of work. Don't turn it off just for easiness of development. ignoring_self_events_enabled: False if you would like to disable the built-in middleware (Default: True). `IgnoringSelfEvents` is a built-in middleware that enables Bolt apps to easily skip the events generated by this app's bot user (this is useful for avoiding code error causing an infinite loop). ignoring_self_assistant_message_events_enabled: False if you would like to disable the built-in middleware. `IgnoringSelfEvents` for this app's bot user message events within an assistant thread This is useful for avoiding code error causing an infinite loop; Default: True url_verification_enabled: False if you would like to disable the built-in middleware (Default: True). `UrlVerification` is a built-in middleware that handles url_verification requests that verify the endpoint for Events API in HTTP Mode requests. attaching_function_token_enabled: False if you would like to disable the built-in middleware (Default: True). `AttachingFunctionToken` is a built-in middleware that injects the just-in-time workflow-execution tokens when your app receives `function_executed` or interactivity events scoped to a custom step. ssl_check_enabled: bool = False if you would like to disable the built-in middleware (Default: True). `SslCheck` is a built-in middleware that handles ssl_check requests from Slack. oauth_settings: The settings related to Slack app installation flow (OAuth flow) oauth_flow: Instantiated `slack_bolt.oauth.OAuthFlow`. This is always prioritized over oauth_settings. verification_token: Deprecated verification mechanism. This can be used only for ssl_check requests. listener_executor: Custom executor to run background tasks. If absent, the default `ThreadPoolExecutor` will be used. assistant_thread_context_store: Custom AssistantThreadContext store (Default: the built-in implementation, which uses a parent message's metadata to store the latest context) """ if signing_secret is None: signing_secret = os.environ.get("SLACK_SIGNING_SECRET", "") token = token or os.environ.get("SLACK_BOT_TOKEN") self._name: str = name or inspect.stack()[1].filename.split(os.path.sep)[-1] self._signing_secret: str = signing_secret self._verification_token: Optional[str] = verification_token or os.environ.get("SLACK_VERIFICATION_TOKEN", None) # If a logger is explicitly passed when initializing, the logger works as the base logger. # The base logger's logging settings will be propagated to all the loggers created by bolt-python. self._base_logger = logger # The framework logger is supposed to be used for the internal logging. # Also, it's accessible via `app.logger` as the app's singleton logger. self._framework_logger = logger or get_bolt_logger(App) self._raise_error_for_unhandled_request = raise_error_for_unhandled_request self._token: Optional[str] = token if client is not None: if not isinstance(client, WebClient): raise BoltError(error_client_invalid_type()) self._client = client self._token = client.token if token is not None: self._framework_logger.warning(warning_client_prioritized_and_token_skipped()) else: self._client = create_web_client( # NOTE: the token here can be None token=token, logger=self._framework_logger, ) # -------------------------------------- # Authorize & OAuthFlow initialization # -------------------------------------- self._before_authorize: Optional[Middleware] = None if before_authorize is not None: if callable(before_authorize): self._before_authorize = CustomMiddleware( app_name=self._name, func=before_authorize, base_logger=self._framework_logger, ) elif isinstance(before_authorize, Middleware): self._before_authorize = before_authorize self._authorize: Optional[Authorize] = None if authorize is not None: if isinstance(authorize, Authorize): # As long as an advanced developer understands what they're doing, # bolt-python should not prevent customizing authorize middleware self._authorize = authorize else: if oauth_settings is not None or oauth_flow is not None: # If the given authorize is a simple function, # it does not work along with installation_store. raise BoltError(error_authorize_conflicts()) self._authorize = CallableAuthorize(logger=self._framework_logger, func=authorize) self._installation_store: Optional[InstallationStore] = installation_store if self._installation_store is not None and self._authorize is None: settings = oauth_flow.settings if oauth_flow is not None else oauth_settings self._authorize = InstallationStoreAuthorize( installation_store=self._installation_store, client_id=settings.client_id if settings is not None else None, client_secret=settings.client_secret if settings is not None else None, logger=self._framework_logger, bot_only=installation_store_bot_only or False, client=self._client, # for proxy use cases etc. user_token_resolution=(settings.user_token_resolution if settings is not None else "authed_user"), ) self._oauth_flow: Optional[OAuthFlow] = None if ( oauth_settings is None and os.environ.get("SLACK_CLIENT_ID") is not None and os.environ.get("SLACK_CLIENT_SECRET") is not None ): # initialize with the default settings oauth_settings = OAuthSettings() if oauth_flow is None and installation_store is None: # show info-level log for avoiding confusions self._framework_logger.info(info_default_oauth_settings_loaded()) if oauth_flow is not None: self._oauth_flow = oauth_flow installation_store = select_consistent_installation_store( client_id=self._oauth_flow.client_id, app_store=self._installation_store, oauth_flow_store=self._oauth_flow.settings.installation_store, logger=self._framework_logger, ) self._installation_store = installation_store if installation_store is not None: self._oauth_flow.settings.installation_store = installation_store if self._oauth_flow._client is None: self._oauth_flow._client = self._client if self._authorize is None: self._authorize = self._oauth_flow.settings.authorize elif oauth_settings is not None: installation_store = select_consistent_installation_store( client_id=oauth_settings.client_id, app_store=self._installation_store, oauth_flow_store=oauth_settings.installation_store, logger=self._framework_logger, ) self._installation_store = installation_store if installation_store is not None: oauth_settings.installation_store = installation_store self._oauth_flow = OAuthFlow(client=self.client, logger=self.logger, settings=oauth_settings) if self._authorize is None: self._authorize = self._oauth_flow.settings.authorize self._authorize.token_rotation_expiration_minutes = oauth_settings.token_rotation_expiration_minutes # type: ignore[attr-defined] # noqa: E501 if (self._installation_store is not None or self._authorize is not None) and self._token is not None: self._token = None self._framework_logger.warning(warning_token_skipped()) # after setting bot_only here, __init__ cannot replace authorize function if installation_store_bot_only is not None and self._oauth_flow is not None: app_bot_only = installation_store_bot_only or False oauth_flow_bot_only = self._oauth_flow.settings.installation_store_bot_only if app_bot_only != oauth_flow_bot_only: self.logger.warning(warning_bot_only_conflicts()) self._oauth_flow.settings.installation_store_bot_only = app_bot_only self._authorize.bot_only = app_bot_only # type: ignore[union-attr] self._tokens_revocation_listeners: Optional[TokenRevocationListeners] = None if self._installation_store is not None: self._tokens_revocation_listeners = TokenRevocationListeners(self._installation_store) # -------------------------------------- # Middleware Initialization # -------------------------------------- self._middleware_list: List[Middleware] = [] self._listeners: List[Listener] = [] if listener_executor is None: listener_executor = ThreadPoolExecutor(max_workers=5) self._assistant_thread_context_store = assistant_thread_context_store self._attaching_conversation_kwargs_enabled = attaching_conversation_kwargs_enabled self._process_before_response = process_before_response self._listener_runner = ThreadListenerRunner( logger=self._framework_logger, process_before_response=process_before_response, listener_error_handler=DefaultListenerErrorHandler(logger=self._framework_logger), listener_start_handler=DefaultListenerStartHandler(logger=self._framework_logger), listener_completion_handler=DefaultListenerCompletionHandler(logger=self._framework_logger), listener_executor=listener_executor, lazy_listener_runner=ThreadLazyListenerRunner( logger=self._framework_logger, executor=listener_executor, ), ) self._middleware_error_handler: MiddlewareErrorHandler = DefaultMiddlewareErrorHandler( logger=self._framework_logger, ) self._init_middleware_list_done = False self._init_middleware_list( token_verification_enabled=token_verification_enabled, request_verification_enabled=request_verification_enabled, ignoring_self_events_enabled=ignoring_self_events_enabled, ignoring_self_assistant_message_events_enabled=ignoring_self_assistant_message_events_enabled, ssl_check_enabled=ssl_check_enabled, url_verification_enabled=url_verification_enabled, attaching_function_token_enabled=attaching_function_token_enabled, user_facing_authorize_error_message=user_facing_authorize_error_message, ) def _init_middleware_list( self, token_verification_enabled: bool = True, request_verification_enabled: bool = True, ignoring_self_events_enabled: bool = True, ignoring_self_assistant_message_events_enabled: bool = True, ssl_check_enabled: bool = True, url_verification_enabled: bool = True, attaching_function_token_enabled: bool = True, user_facing_authorize_error_message: Optional[str] = None, ): if self._init_middleware_list_done: return if ssl_check_enabled is True: self._middleware_list.append( SslCheck( verification_token=self._verification_token, base_logger=self._base_logger, ) ) if request_verification_enabled is True: self._middleware_list.append(RequestVerification(self._signing_secret, base_logger=self._base_logger)) if self._before_authorize is not None: self._middleware_list.append(self._before_authorize) # As authorize is required for making a Bolt app function, we don't offer the flag to disable this if self._oauth_flow is None: if self._token is not None: try: auth_test_result = None if token_verification_enabled: # This API call is for eagerly validating the token auth_test_result = self._client.auth_test(token=self._token) self._middleware_list.append( SingleTeamAuthorization( auth_test_result=auth_test_result, base_logger=self._base_logger, user_facing_authorize_error_message=user_facing_authorize_error_message, ) ) except SlackApiError as err: raise BoltError(error_auth_test_failure(err.response)) elif self._authorize is not None: self._middleware_list.append( MultiTeamsAuthorization( authorize=self._authorize, base_logger=self._base_logger, user_facing_authorize_error_message=user_facing_authorize_error_message, ) ) else: raise BoltError(error_token_required()) elif self._authorize is not None: self._middleware_list.append( MultiTeamsAuthorization( authorize=self._authorize, base_logger=self._base_logger, user_token_resolution=self._oauth_flow.settings.user_token_resolution, user_facing_authorize_error_message=user_facing_authorize_error_message, ) ) else: raise BoltError(error_oauth_flow_or_authorize_required()) if ignoring_self_events_enabled is True: self._middleware_list.append( IgnoringSelfEvents( base_logger=self._base_logger, ignoring_self_assistant_message_events_enabled=ignoring_self_assistant_message_events_enabled, ) ) if url_verification_enabled is True: self._middleware_list.append(UrlVerification(base_logger=self._base_logger)) if attaching_function_token_enabled is True: self._middleware_list.append(AttachingFunctionToken()) self._init_middleware_list_done = True # ------------------------- # accessors @property def name(self) -> str: """The name of this app (default: the filename)""" return self._name @property def oauth_flow(self) -> Optional[OAuthFlow]: """Configured `OAuthFlow` object if exists.""" return self._oauth_flow @property def logger(self) -> logging.Logger: """The logger this app uses.""" return self._framework_logger @property def client(self) -> WebClient: """The singleton `slack_sdk.WebClient` instance in this app.""" return self._client @property def installation_store(self) -> Optional[InstallationStore]: """The `slack_sdk.oauth.InstallationStore` that can be used in the `authorize` middleware.""" return self._installation_store @property def listener_runner(self) -> ThreadListenerRunner: """The thread executor for asynchronously running listeners.""" return self._listener_runner @property def process_before_response(self) -> bool: return self._process_before_response or False # ------------------------- # standalone server def start( self, port: int = 3000, path: str = "/slack/events", http_server_logger_enabled: bool = True, ) -> None: """Starts a web server for local development. # With the default settings, `http://localhost:3000/slack/events` # is available for handling incoming requests from Slack app.start() This method internally starts a Web server process built with the `http.server` module. For production, consider using a production-ready WSGI server such as Gunicorn. Args: port: The port to listen on (Default: 3000) path: The path to handle request from Slack (Default: `/slack/events`) http_server_logger_enabled: The flag to enable http.server logging if True (Default: True) """ self._development_server = SlackAppDevelopmentServer( port=port, path=path, app=self, oauth_flow=self.oauth_flow, http_server_logger_enabled=http_server_logger_enabled, ) self._development_server.start() # ------------------------- # main dispatcher def dispatch(self, req: BoltRequest) -> BoltResponse: """Applies all middleware and dispatches an incoming request from Slack to the right code path. Args: req: An incoming request from Slack Returns: The response generated by this Bolt app """ starting_time = time.time() self._init_context(req) resp: Optional[BoltResponse] = BoltResponse(status=200, body="") middleware_state = {"next_called": False} def middleware_next(): middleware_state["next_called"] = True try: for middleware in self._middleware_list: middleware_state["next_called"] = False if self._framework_logger.level <= logging.DEBUG: self._framework_logger.debug(debug_applying_middleware(middleware.name)) resp = middleware.process(req=req, resp=resp, next=middleware_next) # type: ignore[arg-type] if not middleware_state["next_called"]: if resp is None: # next() method was not called without providing the response to return to Slack # This should not be an intentional handling in usual use cases. resp = BoltResponse(status=404, body={"error": "no next() calls in middleware"}) if self._raise_error_for_unhandled_request is True: try: raise BoltUnhandledRequestError( request=req, current_response=resp, last_global_middleware_name=middleware.name, ) except BoltUnhandledRequestError as e: self._listener_runner.listener_error_handler.handle( error=e, request=req, response=resp, ) return resp self._framework_logger.warning(warning_unhandled_by_global_middleware(middleware.name, req)) return resp return resp for listener in self._listeners: listener_name = get_name_for_callable(listener.ack_function) self._framework_logger.debug(debug_checking_listener(listener_name)) if listener.matches(req=req, resp=resp): # type: ignore[arg-type] # run all the middleware attached to this listener first middleware_resp, next_was_not_called = listener.run_middleware( req=req, resp=resp # type: ignore[arg-type] ) if next_was_not_called: if middleware_resp is not None: if self._framework_logger.level <= logging.DEBUG: debug_message = debug_return_listener_middleware_response( listener_name, middleware_resp.status, middleware_resp.body, starting_time, ) self._framework_logger.debug(debug_message) return middleware_resp # The last listener middleware didn't call next() method. # This means the listener is not for this incoming request. continue if middleware_resp is not None: resp = middleware_resp self._framework_logger.debug(debug_running_listener(listener_name)) listener_response: Optional[BoltResponse] = self._listener_runner.run( request=req, response=resp, # type: ignore[arg-type] listener_name=listener_name, listener=listener, ) if listener_response is not None: return listener_response if resp is None: resp = BoltResponse(status=404, body={"error": "unhandled request"}) if self._raise_error_for_unhandled_request is True: try: raise BoltUnhandledRequestError( request=req, current_response=resp, ) except BoltUnhandledRequestError as e: self._listener_runner.listener_error_handler.handle( error=e, request=req, response=resp, ) return resp return self._handle_unmatched_requests(req, resp) except Exception as error: resp = BoltResponse(status=500, body="") self._middleware_error_handler.handle( error=error, request=req, response=resp, ) return resp def _handle_unmatched_requests(self, req: BoltRequest, resp: BoltResponse) -> BoltResponse: self._framework_logger.warning(warning_unhandled_request(req)) return resp # ------------------------- # middleware def use(self, *args) -> Optional[Callable]: """Registers a new global middleware to this app. This method can be used as either a decorator or a method. Refer to `App#middleware()` method's docstring for details.""" return self.middleware(*args) def middleware(self, *args) -> Optional[Callable]: """Registers a new middleware to this app. This method can be used as either a decorator or a method. # Use this method as a decorator @app.middleware def middleware_func(logger, body, next): logger.info(f"request body: {body}") next() # Pass a function to this method app.middleware(middleware_func) Refer to https://docs.slack.dev/tools/bolt-python/concepts/global-middleware for details. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: *args: A function that works as a global middleware. """ if len(args) > 0: middleware_or_callable = args[0] if isinstance(middleware_or_callable, Middleware): middleware: Middleware = middleware_or_callable self._middleware_list.append(middleware) if isinstance(middleware, Assistant) and middleware.thread_context_store is not None: self._assistant_thread_context_store = middleware.thread_context_store elif callable(middleware_or_callable): self._middleware_list.append( CustomMiddleware( app_name=self.name, func=middleware_or_callable, base_logger=self._base_logger, ) ) return middleware_or_callable else: raise BoltError(f"Unexpected type for a middleware ({type(middleware_or_callable)})") return None # ------------------------- # AI Agents & Assistants def assistant(self, assistant: Assistant) -> Optional[Callable]: return self.middleware(assistant) # ------------------------- # Workflows: Steps from apps def step( self, callback_id: Union[str, Pattern, WorkflowStep, WorkflowStepBuilder], edit: Optional[Union[Callable[..., Optional[BoltResponse]], Listener, Sequence[Callable]]] = None, save: Optional[Union[Callable[..., Optional[BoltResponse]], Listener, Sequence[Callable]]] = None, execute: Optional[Union[Callable[..., Optional[BoltResponse]], Listener, Sequence[Callable]]] = None, ): """ Deprecated: Steps from apps for legacy workflows are now deprecated. Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new step from app listener. Unlike others, this method doesn't behave as a decorator. If you want to register a step from app by a decorator, use `WorkflowStepBuilder`'s methods. # Create a new WorkflowStep instance from slack_bolt.workflows.step import WorkflowStep ws = WorkflowStep( callback_id="add_task", edit=edit, save=save, execute=execute, ) # Pass Step to set up listeners app.step(ws) Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. For further information about WorkflowStep specific function arguments such as `configure`, `update`, `complete`, and `fail`, refer to `slack_bolt.workflows.step.utilities` API documents. Args: callback_id: The Callback ID for this step from app edit: The function for displaying a modal in the Workflow Builder save: The function for handling configuration in the Workflow Builder execute: The function for handling the step execution """ warnings.warn( ( "Steps from apps for legacy workflows are now deprecated. " "Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/" ), category=DeprecationWarning, ) step = callback_id if isinstance(callback_id, (str, Pattern)): step = WorkflowStep( callback_id=callback_id, edit=edit, # type: ignore[arg-type] save=save, # type: ignore[arg-type] execute=execute, # type: ignore[arg-type] base_logger=self._base_logger, ) elif isinstance(step, WorkflowStepBuilder): step = step.build(base_logger=self._base_logger) elif not isinstance(step, WorkflowStep): raise BoltError(f"Invalid step object ({type(step)})") self.use(WorkflowStepMiddleware(step)) # ------------------------- # global error handler def error(self, func: Callable[..., Optional[BoltResponse]]) -> Callable[..., Optional[BoltResponse]]: """Updates the global error handler. This method can be used as either a decorator or a method. # Use this method as a decorator @app.error def custom_error_handler(error, body, logger): logger.exception(f"Error: {error}") logger.info(f"Request body: {body}") # Pass a function to this method app.error(custom_error_handler) To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: func: The function that is supposed to be executed when getting an unhandled error in Bolt app. """ self._listener_runner.listener_error_handler = CustomListenerErrorHandler( logger=self._framework_logger, func=func, ) self._middleware_error_handler = CustomMiddlewareErrorHandler( logger=self._framework_logger, func=func, ) return func # ------------------------- # events def event( self, event: Union[ str, Pattern, Dict[str, Optional[Union[str, Sequence[Optional[Union[str, Pattern]]]]]], ], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new event listener. This method can be used as either a decorator or a method. # Use this method as a decorator @app.event("team_join") def ask_for_introduction(event, say): welcome_channel_id = "C12345" user_id = event["user"] text = f"Welcome to the team, <@{user_id}>! :tada: You can introduce yourself in this channel." say(text=text, channel=welcome_channel_id) # Pass a function to this method app.event("team_join")(ask_for_introduction) Refer to https://docs.slack.dev/apis/events-api/ for details of Events API. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: event: The conditions that match a request payload. If you pass a dict for this, you can have type, subtype in the constraint. matchers: A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ middleware = list(middleware) if middleware else [] def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.event(event, base_logger=self._base_logger) if self._attaching_conversation_kwargs_enabled: middleware.insert(0, AttachingConversationKwargs(self._assistant_thread_context_store)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) return __call__ def message( self, keyword: Union[str, Pattern] = "", matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new message event listener. This method can be used as either a decorator or a method. Check the `App#event` method's docstring for details. # Use this method as a decorator @app.message(":wave:") def say_hello(message, say): user = message['user'] say(f"Hi there, <@{user}>!") # Pass a function to this method app.message(":wave:")(say_hello) Refer to https://docs.slack.dev/reference/events/message/ for details of `message` events. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: keyword: The keyword to match matchers: A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ matchers = list(matchers) if matchers else [] middleware = list(middleware) if middleware else [] def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) constraints = { "type": "message", "subtype": ( # In most cases, new message events come with no subtype. None, # As of Jan 2021, most bot messages no longer have the subtype bot_message. # By contrast, messages posted using classic app's bot token still have the subtype. "bot_message", # If an end-user posts a message with "Also send to #channel" checked, # the message event comes with this subtype. "thread_broadcast", # If an end-user posts a message with attached files, # the message event comes with this subtype. "file_share", ), } primary_matcher = builtin_matchers.message_event( keyword=keyword, constraints=constraints, base_logger=self._base_logger ) if self._attaching_conversation_kwargs_enabled: middleware.insert(0, AttachingConversationKwargs(self._assistant_thread_context_store)) middleware.insert(0, MessageListenerMatches(keyword)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) return __call__ def function( self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, auto_acknowledge: bool = True, ack_timeout: int = 3, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new Function listener. This method can be used as either a decorator or a method. # Use this method as a decorator @app.function("reverse") def reverse_string(ack: Ack, inputs: dict, complete: Complete, fail: Fail): try: ack() string_to_reverse = inputs["stringToReverse"] complete(outputs={"reverseString": string_to_reverse[::-1]}) except Exception as e: fail(f"Cannot reverse string (error: {e})") raise e # Pass a function to this method app.function("reverse")(reverse_string) To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: callback_id: The callback id to identify the function matchers: A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ if auto_acknowledge is True: if ack_timeout != 3: self._framework_logger.warning(warning_ack_timeout_has_no_effect(callback_id, ack_timeout)) matchers = list(matchers) if matchers else [] middleware = list(middleware) if middleware else [] def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.function_executed(callback_id=callback_id, base_logger=self._base_logger) return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge, ack_timeout) return __call__ # ------------------------- # slash commands def command( self, command: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new slash command listener. This method can be used as either a decorator or a method. # Use this method as a decorator @app.command("/echo") def repeat_text(ack, say, command): # Acknowledge command request ack() say(f"{command['text']}") # Pass a function to this method app.command("/echo")(repeat_text) Refer to https://docs.slack.dev/interactivity/implementing-slash-commands/ for details of Slash Commands. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: command: The conditions that match a request payload matchers: A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.command(command, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ # ------------------------- # shortcut def shortcut( self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new shortcut listener. This method can be used as either a decorator or a method. # Use this method as a decorator @app.shortcut("open_modal") def open_modal(ack, body, client): # Acknowledge the command request ack() # Call views_open with the built-in client client.views_open( # Pass a valid trigger_id within 3 seconds of receiving it trigger_id=body["trigger_id"], # View payload view={ ... } ) # Pass a function to this method app.shortcut("open_modal")(open_modal) Refer to https://docs.slack.dev/interactivity/implementing-shortcuts/ for details about Shortcuts. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: constraints: The conditions that match a request payload. matchers: A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.shortcut(constraints, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ def global_shortcut( self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new global shortcut listener.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.global_shortcut(callback_id, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ def message_shortcut( self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new message shortcut listener.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.message_shortcut(callback_id, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ # ------------------------- # action def action( self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new action listener. This method can be used as either a decorator or a method. # Use this method as a decorator @app.action("approve_button") def update_message(ack): ack() # Pass a function to this method app.action("approve_button")(update_message) * Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for actions in `blocks`. * Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for actions in `attachments`. * Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for actions in dialogs. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: constraints: The conditions that match a request payload matchers: A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.action(constraints, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ def block_action( self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `block_actions` action listener. Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for details. """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.block_action(constraints, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ def attachment_action( self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `interactive_message` action listener. Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.attachment_action(callback_id, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ def dialog_submission( self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_submission` listener. Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.dialog_submission(callback_id, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ def dialog_cancellation( self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_cancellation` listener. Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.dialog_cancellation(callback_id, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ # ------------------------- # view def view( self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_submission`/`view_closed` event listener. This method can be used as either a decorator or a method. # Use this method as a decorator @app.view("view_1") def handle_submission(ack, body, client, view): # Assume there's an input block with `block_c` as the block_id and `dreamy_input` hopes_and_dreams = view["state"]["values"]["block_c"]["dreamy_input"] user = body["user"]["id"] # Validate the inputs errors = {} if hopes_and_dreams is not None and len(hopes_and_dreams) <= 5: errors["block_c"] = "The value must be longer than 5 characters" if len(errors) > 0: ack(response_action="errors", errors=errors) return # Acknowledge the view_submission event and close the modal ack() # Do whatever you want with the input data - here we're saving it to a DB # Pass a function to this method app.view("view_1")(handle_submission) Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: constraints: The conditions that match a request payload matchers: A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.view(constraints, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ def view_submission( self, constraints: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_submission` listener. Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details. """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.view_submission(constraints, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ def view_closed( self, constraints: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_closed` listener. Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_closed for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.view_closed(constraints, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ # ------------------------- # options def options( self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new options listener. This method can be used as either a decorator or a method. # Use this method as a decorator @app.options("menu_selection") def show_menu_options(ack): options = [ { "text": {"type": "plain_text", "text": "Option 1"}, "value": "1-1", }, { "text": {"type": "plain_text", "text": "Option 2"}, "value": "1-2", }, ] ack(options=options) # Pass a function to this method app.options("menu_selection")(show_menu_options) Refer to the following documents for details: * https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element#external_select * https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: matchers: A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.options(constraints, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ def block_suggestion( self, action_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `block_suggestion` listener.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.block_suggestion(action_id, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ def dialog_suggestion( self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_suggestion` listener. Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.dialog_suggestion(callback_id, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ # ------------------------- # built-in listener functions def default_tokens_revoked_event_listener( self, ) -> Callable[..., Optional[BoltResponse]]: if self._tokens_revocation_listeners is None: raise BoltError(error_installation_store_required_for_builtin_listeners()) return self._tokens_revocation_listeners.handle_tokens_revoked_events def default_app_uninstalled_event_listener( self, ) -> Callable[..., Optional[BoltResponse]]: if self._tokens_revocation_listeners is None: raise BoltError(error_installation_store_required_for_builtin_listeners()) return self._tokens_revocation_listeners.handle_app_uninstalled_events def enable_token_revocation_listeners(self) -> None: self.event("tokens_revoked")(self.default_tokens_revoked_event_listener()) self.event("app_uninstalled")(self.default_app_uninstalled_event_listener()) # ------------------------- def _init_context(self, req: BoltRequest): req.context["logger"] = get_bolt_app_logger(app_name=self.name, base_logger=self._base_logger) req.context["token"] = self._token # Prior to version 1.15, when the token is static, self._client was passed to `req.context`. # The intention was to avoid creating a new instance per request # in the interest of runtime performance/memory footprint optimization. # However, developers may want to replace the token held by req.context.client in some situations. # In this case, this behavior can result in thread-unsafe data modification on `self._client`. # (`self._client` a.k.a. `app.client` is a singleton object per an App instance) # Thus, we've changed the behavior to create a new instance per request regardless of token argument # in the App initialization starting v1.15. # The overhead brought by this change is slight so that we believe that it is ignorable in any cases. client_per_request: WebClient = WebClient( token=self._token, # this can be None, and it can be set later on base_url=self._client.base_url, timeout=self._client.timeout, ssl=self._client.ssl, proxy=self._client.proxy, headers=self._client.headers, team_id=req.context.team_id, logger=self._client.logger, retry_handlers=self._client.retry_handlers.copy() if self._client.retry_handlers is not None else None, ) req.context["client"] = client_per_request # Most apps do not need this "listener_runner" instance. # It is intended for apps that start lazy listeners from their custom global middleware. req.context["listener_runner"] = self.listener_runner @staticmethod def _to_listener_functions( kwargs: dict, ) -> Optional[Sequence[Callable[..., Optional[BoltResponse]]]]: if kwargs: functions = [kwargs["ack"]] for sub in kwargs["lazy"]: functions.append(sub) return functions return None def _register_listener( self, functions: Sequence[Callable[..., Optional[BoltResponse]]], primary_matcher: ListenerMatcher, matchers: Optional[Sequence[Callable[..., bool]]], middleware: Optional[Sequence[Union[Callable, Middleware]]], auto_acknowledgement: bool = False, ack_timeout: int = 3, ) -> Optional[Callable[..., Optional[BoltResponse]]]: value_to_return = None if not isinstance(functions, list): functions = list(functions) if len(functions) == 1: # In the case where the function is registered using decorator, # the registration should return the original function. value_to_return = functions[0] listener_matchers: List[ListenerMatcher] = [ CustomListenerMatcher(app_name=self.name, func=f, base_logger=self._base_logger) for f in (matchers or []) ] listener_matchers.insert(0, primary_matcher) listener_middleware = [] for m in middleware or []: if isinstance(m, Middleware): listener_middleware.append(m) elif callable(m): listener_middleware.append(CustomMiddleware(app_name=self.name, func=m, base_logger=self._base_logger)) else: raise ValueError(error_unexpected_listener_middleware(type(m))) self._listeners.append( CustomListener( app_name=self.name, ack_function=functions.pop(0), lazy_functions=functions, # type: ignore[arg-type] matchers=listener_matchers, middleware=listener_middleware, auto_acknowledgement=auto_acknowledgement, ack_timeout=ack_timeout, base_logger=self._base_logger, ) ) return value_to_return ``` Bolt App that provides functionalities to register middleware/listeners. ``` import os from slack_bolt import App # Initializes your app with your bot token and signing secret app = App( token=os.environ.get("SLACK_BOT_TOKEN"), signing_secret=os.environ.get("SLACK_SIGNING_SECRET") ) # Listens to incoming messages that contain "hello" @app.message("hello") def message_hello(message, say): # say() sends a message to the channel where the event was triggered say(f"Hey there <@{message['user']}>!") # Start your app if __name__ == "__main__": app.start(port=int(os.environ.get("PORT", 3000))) ``` Refer to [https://docs.slack.dev/tools/bolt-python/creating-an-app](https://docs.slack.dev/tools/bolt-python/creating-an-app) for details. If you would like to build an OAuth app for enabling the app to run with multiple workspaces, refer to [https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth](https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth) to learn how to configure the app. ## Args **`logger`** The custom logger that can be used in this app. **`name`** The application name that will be used in logging. If absent, the source file name will be used. **`process_before_response`** True if this app runs on Function as a Service. (Default: False) **`raise_error_for_unhandled_request`** True if you want to raise exceptions for unhandled requests and use @app.error listeners instead of the built-in handler, which pints warning logs and returns 404 to Slack (Default: False) **`signing_secret`** The Signing Secret value used for verifying requests from Slack. **`token`** The bot/user access token required only for single-workspace app. **`token_verification_enabled`** Verifies the validity of the given token if True. **`client`** The singleton `slack_sdk.WebClient` instance for this app. **`before_authorize`** A global middleware that can be executed right before authorize function **`authorize`** The function to authorize an incoming request from Slack by checking if there is a team/user in the installation data. **`user_facing_authorize_error_message`** The user-facing error message to display when the app is installed but the installation is not managed by this app's installation store **`installation_store`** The module offering save/find operations of installation data **`installation_store_bot_only`** Use `InstallationStore#find_bot()` if True (Default: False) **`request_verification_enabled`** False if you would like to disable the built-in middleware (Default: True). `RequestVerification` is a built-in middleware that verifies the signature in HTTP Mode requests. Make sure if it's safe enough when you turn a built-in middleware off. We strongly recommend using RequestVerification for better security. If you have a proxy that verifies request signature in front of the Bolt app, it's totally fine to disable RequestVerification to avoid duplication of work. Don't turn it off just for easiness of development. **`ignoring_self_events_enabled`** False if you would like to disable the built-in middleware (Default: True). `IgnoringSelfEvents` is a built-in middleware that enables Bolt apps to easily skip the events generated by this app's bot user (this is useful for avoiding code error causing an infinite loop). **`ignoring_self_assistant_message_events_enabled`** False if you would like to disable the built-in middleware. `IgnoringSelfEvents` for this app's bot user message events within an assistant thread This is useful for avoiding code error causing an infinite loop; Default: True **`url_verification_enabled`** False if you would like to disable the built-in middleware (Default: True). `UrlVerification` is a built-in middleware that handles url\_verification requests that verify the endpoint for Events API in HTTP Mode requests. **`attaching_function_token_enabled`** False if you would like to disable the built-in middleware (Default: True). `AttachingFunctionToken` is a built-in middleware that injects the just-in-time workflow-execution tokens when your app receives `function_executed` or interactivity events scoped to a custom step. **`ssl_check_enabled`** bool = False if you would like to disable the built-in middleware (Default: True). `SslCheck` is a built-in middleware that handles ssl\_check requests from Slack. **`oauth_settings`** The settings related to Slack app installation flow (OAuth flow) **`oauth_flow`** Instantiated `[OAuthFlow](../oauth/index.html#slack_bolt.oauth.OAuthFlow "slack_bolt.oauth.OAuthFlow")`. This is always prioritized over oauth\_settings. **`verification_token`** Deprecated verification mechanism. This can be used only for ssl\_check requests. **`listener_executor`** Custom executor to run background tasks. If absent, the default `ThreadPoolExecutor` will be used. **`assistant_thread_context_store`** Custom AssistantThreadContext store (Default: the built-in implementation, which uses a parent message's metadata to store the latest context) ### Instance variables `prop client : slack_sdk.web.client.WebClient` Expand source code ``` @property def client(self) -> WebClient: """The singleton `slack_sdk.WebClient` instance in this app.""" return self._client ``` The singleton `slack_sdk.WebClient` instance in this app. `prop installation_store : slack_sdk.oauth.installation_store.installation_store.InstallationStore | None` Expand source code ``` @property def installation_store(self) -> Optional[InstallationStore]: """The `slack_sdk.oauth.InstallationStore` that can be used in the `authorize` middleware.""" return self._installation_store ``` The `slack_sdk.oauth.InstallationStore` that can be used in the `authorize` middleware. `prop listener_runner : [ThreadListenerRunner](../listener/thread_runner.html#slack_bolt.listener.thread_runner.ThreadListenerRunner "slack_bolt.listener.thread_runner.ThreadListenerRunner")` Expand source code ``` @property def listener_runner(self) -> ThreadListenerRunner: """The thread executor for asynchronously running listeners.""" return self._listener_runner ``` The thread executor for asynchronously running listeners. `prop logger : logging.Logger` Expand source code ``` @property def logger(self) -> logging.Logger: """The logger this app uses.""" return self._framework_logger ``` The logger this app uses. `prop name : str` Expand source code ``` @property def name(self) -> str: """The name of this app (default: the filename)""" return self._name ``` The name of this app (default: the filename) `prop oauth_flow : [OAuthFlow](../oauth/oauth_flow.html#slack_bolt.oauth.oauth_flow.OAuthFlow "slack_bolt.oauth.oauth_flow.OAuthFlow") | None` Expand source code ``` @property def oauth_flow(self) -> Optional[OAuthFlow]: """Configured `OAuthFlow` object if exists.""" return self._oauth_flow ``` Configured `OAuthFlow` object if exists. `prop process_before_response : bool` Expand source code ``` @property def process_before_response(self) -> bool: return self._process_before_response or False ``` ### Methods `def action(self, constraints: str | Pattern | Dict[str, str | Pattern], matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](../middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def action( self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new action listener. This method can be used as either a decorator or a method. # Use this method as a decorator @app.action("approve_button") def update_message(ack): ack() # Pass a function to this method app.action("approve_button")(update_message) * Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for actions in `blocks`. * Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for actions in `attachments`. * Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for actions in dialogs. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: constraints: The conditions that match a request payload matchers: A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.action(constraints, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ ``` Registers a new action listener. This method can be used as either a decorator or a method. ``` # Use this method as a decorator @app.action("approve_button") def update_message(ack): ack() # Pass a function to this method app.action("approve_button")(update_message) ``` * Refer to [https://docs.slack.dev/reference/interaction-payloads/block\_actions-payload/](https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/) for actions in `blocks`. * Refer to [https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/](https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/) for actions in `attachments`. * Refer to [https://docs.slack.dev/legacy/legacy-dialogs/](https://docs.slack.dev/legacy/legacy-dialogs/) for actions in dialogs. To learn available arguments for middleware/listeners, see `[slack_bolt.kwargs_injection.args](../kwargs_injection/args.html "slack_bolt.kwargs_injection.args")`'s API document. ## Args **`constraints`** The conditions that match a request payload **`matchers`** A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. **`middleware`** A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. `def assistant(self, assistant: [Assistant](../middleware/assistant/assistant.html#slack_bolt.middleware.assistant.assistant.Assistant "slack_bolt.middleware.assistant.assistant.Assistant")) ‑> Callable | None` Expand source code ``` def assistant(self, assistant: Assistant) -> Optional[Callable]: return self.middleware(assistant) ``` `def attachment_action(self, callback_id: str | Pattern, matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](../middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def attachment_action( self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `interactive_message` action listener. Refer to https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.attachment_action(callback_id, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ ``` Registers a new `interactive_message` action listener. Refer to [https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/](https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/) for details. `def block_action(self, constraints: str | Pattern | Dict[str, str | Pattern], matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](../middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def block_action( self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `block_actions` action listener. Refer to https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/ for details. """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.block_action(constraints, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ ``` Registers a new `block_actions` action listener. Refer to [https://docs.slack.dev/reference/interaction-payloads/block\_actions-payload/](https://docs.slack.dev/reference/interaction-payloads/block_actions-payload/) for details. `def block_suggestion(self, action_id: str | Pattern, matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](../middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def block_suggestion( self, action_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `block_suggestion` listener.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.block_suggestion(action_id, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ ``` Registers a new `block_suggestion` listener. `def command(self, command: str | Pattern, matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](../middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def command( self, command: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new slash command listener. This method can be used as either a decorator or a method. # Use this method as a decorator @app.command("/echo") def repeat_text(ack, say, command): # Acknowledge command request ack() say(f"{command['text']}") # Pass a function to this method app.command("/echo")(repeat_text) Refer to https://docs.slack.dev/interactivity/implementing-slash-commands/ for details of Slash Commands. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: command: The conditions that match a request payload matchers: A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.command(command, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ ``` Registers a new slash command listener. This method can be used as either a decorator or a method. ``` # Use this method as a decorator @app.command("/echo") def repeat_text(ack, say, command): # Acknowledge command request ack() say(f"{command['text']}") # Pass a function to this method app.command("/echo")(repeat_text) ``` Refer to [https://docs.slack.dev/interactivity/implementing-slash-commands/](https://docs.slack.dev/interactivity/implementing-slash-commands/) for details of Slash Commands. To learn available arguments for middleware/listeners, see `[slack_bolt.kwargs_injection.args](../kwargs_injection/args.html "slack_bolt.kwargs_injection.args")`'s API document. ## Args **`command`** The conditions that match a request payload **`matchers`** A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. **`middleware`** A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. `def default_app_uninstalled_event_listener(self) ‑> Callable[..., [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None]` Expand source code ``` def default_app_uninstalled_event_listener( self, ) -> Callable[..., Optional[BoltResponse]]: if self._tokens_revocation_listeners is None: raise BoltError(error_installation_store_required_for_builtin_listeners()) return self._tokens_revocation_listeners.handle_app_uninstalled_events ``` `def default_tokens_revoked_event_listener(self) ‑> Callable[..., [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None]` Expand source code ``` def default_tokens_revoked_event_listener( self, ) -> Callable[..., Optional[BoltResponse]]: if self._tokens_revocation_listeners is None: raise BoltError(error_installation_store_required_for_builtin_listeners()) return self._tokens_revocation_listeners.handle_tokens_revoked_events ``` `def dialog_cancellation(self, callback_id: str | Pattern, matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](../middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def dialog_cancellation( self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_cancellation` listener. Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.dialog_cancellation(callback_id, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ ``` Registers a new `dialog_cancellation` listener. Refer to [https://docs.slack.dev/legacy/legacy-dialogs/](https://docs.slack.dev/legacy/legacy-dialogs/) for details. `def dialog_submission(self, callback_id: str | Pattern, matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](../middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def dialog_submission( self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_submission` listener. Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.dialog_submission(callback_id, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ ``` Registers a new `dialog_submission` listener. Refer to [https://docs.slack.dev/legacy/legacy-dialogs/](https://docs.slack.dev/legacy/legacy-dialogs/) for details. `def dialog_suggestion(self, callback_id: str | Pattern, matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](../middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def dialog_suggestion( self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `dialog_suggestion` listener. Refer to https://docs.slack.dev/legacy/legacy-dialogs/ for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.dialog_suggestion(callback_id, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ ``` Registers a new `dialog_suggestion` listener. Refer to [https://docs.slack.dev/legacy/legacy-dialogs/](https://docs.slack.dev/legacy/legacy-dialogs/) for details. `def dispatch(self, req: [BoltRequest](../request/request.html#slack_bolt.request.request.BoltRequest "slack_bolt.request.request.BoltRequest")) ‑> [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse")` Expand source code ``` def dispatch(self, req: BoltRequest) -> BoltResponse: """Applies all middleware and dispatches an incoming request from Slack to the right code path. Args: req: An incoming request from Slack Returns: The response generated by this Bolt app """ starting_time = time.time() self._init_context(req) resp: Optional[BoltResponse] = BoltResponse(status=200, body="") middleware_state = {"next_called": False} def middleware_next(): middleware_state["next_called"] = True try: for middleware in self._middleware_list: middleware_state["next_called"] = False if self._framework_logger.level <= logging.DEBUG: self._framework_logger.debug(debug_applying_middleware(middleware.name)) resp = middleware.process(req=req, resp=resp, next=middleware_next) # type: ignore[arg-type] if not middleware_state["next_called"]: if resp is None: # next() method was not called without providing the response to return to Slack # This should not be an intentional handling in usual use cases. resp = BoltResponse(status=404, body={"error": "no next() calls in middleware"}) if self._raise_error_for_unhandled_request is True: try: raise BoltUnhandledRequestError( request=req, current_response=resp, last_global_middleware_name=middleware.name, ) except BoltUnhandledRequestError as e: self._listener_runner.listener_error_handler.handle( error=e, request=req, response=resp, ) return resp self._framework_logger.warning(warning_unhandled_by_global_middleware(middleware.name, req)) return resp return resp for listener in self._listeners: listener_name = get_name_for_callable(listener.ack_function) self._framework_logger.debug(debug_checking_listener(listener_name)) if listener.matches(req=req, resp=resp): # type: ignore[arg-type] # run all the middleware attached to this listener first middleware_resp, next_was_not_called = listener.run_middleware( req=req, resp=resp # type: ignore[arg-type] ) if next_was_not_called: if middleware_resp is not None: if self._framework_logger.level <= logging.DEBUG: debug_message = debug_return_listener_middleware_response( listener_name, middleware_resp.status, middleware_resp.body, starting_time, ) self._framework_logger.debug(debug_message) return middleware_resp # The last listener middleware didn't call next() method. # This means the listener is not for this incoming request. continue if middleware_resp is not None: resp = middleware_resp self._framework_logger.debug(debug_running_listener(listener_name)) listener_response: Optional[BoltResponse] = self._listener_runner.run( request=req, response=resp, # type: ignore[arg-type] listener_name=listener_name, listener=listener, ) if listener_response is not None: return listener_response if resp is None: resp = BoltResponse(status=404, body={"error": "unhandled request"}) if self._raise_error_for_unhandled_request is True: try: raise BoltUnhandledRequestError( request=req, current_response=resp, ) except BoltUnhandledRequestError as e: self._listener_runner.listener_error_handler.handle( error=e, request=req, response=resp, ) return resp return self._handle_unmatched_requests(req, resp) except Exception as error: resp = BoltResponse(status=500, body="") self._middleware_error_handler.handle( error=error, request=req, response=resp, ) return resp ``` Applies all middleware and dispatches an incoming request from Slack to the right code path. ## Args **`req`** An incoming request from Slack ## Returns The response generated by this Bolt app `def enable_token_revocation_listeners(self) ‑> None` Expand source code ``` def enable_token_revocation_listeners(self) -> None: self.event("tokens_revoked")(self.default_tokens_revoked_event_listener()) self.event("app_uninstalled")(self.default_app_uninstalled_event_listener()) ``` `def error(self, func: Callable[..., [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None]) ‑> Callable[..., [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None]` Expand source code ``` def error(self, func: Callable[..., Optional[BoltResponse]]) -> Callable[..., Optional[BoltResponse]]: """Updates the global error handler. This method can be used as either a decorator or a method. # Use this method as a decorator @app.error def custom_error_handler(error, body, logger): logger.exception(f"Error: {error}") logger.info(f"Request body: {body}") # Pass a function to this method app.error(custom_error_handler) To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: func: The function that is supposed to be executed when getting an unhandled error in Bolt app. """ self._listener_runner.listener_error_handler = CustomListenerErrorHandler( logger=self._framework_logger, func=func, ) self._middleware_error_handler = CustomMiddlewareErrorHandler( logger=self._framework_logger, func=func, ) return func ``` Updates the global error handler. This method can be used as either a decorator or a method. ``` # Use this method as a decorator @app.error def custom_error_handler(error, body, logger): logger.exception(f"Error: {error}") logger.info(f"Request body: {body}") # Pass a function to this method app.error(custom_error_handler) ``` To learn available arguments for middleware/listeners, see `[slack_bolt.kwargs_injection.args](../kwargs_injection/args.html "slack_bolt.kwargs_injection.args")`'s API document. ## Args **`func`** The function that is supposed to be executed when getting an unhandled error in Bolt app. `def event(self, event: str | Pattern | Dict[str, str | Sequence[str | Pattern | None] | None], matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](../middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def event( self, event: Union[ str, Pattern, Dict[str, Optional[Union[str, Sequence[Optional[Union[str, Pattern]]]]]], ], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new event listener. This method can be used as either a decorator or a method. # Use this method as a decorator @app.event("team_join") def ask_for_introduction(event, say): welcome_channel_id = "C12345" user_id = event["user"] text = f"Welcome to the team, <@{user_id}>! :tada: You can introduce yourself in this channel." say(text=text, channel=welcome_channel_id) # Pass a function to this method app.event("team_join")(ask_for_introduction) Refer to https://docs.slack.dev/apis/events-api/ for details of Events API. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: event: The conditions that match a request payload. If you pass a dict for this, you can have type, subtype in the constraint. matchers: A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ middleware = list(middleware) if middleware else [] def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.event(event, base_logger=self._base_logger) if self._attaching_conversation_kwargs_enabled: middleware.insert(0, AttachingConversationKwargs(self._assistant_thread_context_store)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) return __call__ ``` Registers a new event listener. This method can be used as either a decorator or a method. ``` # Use this method as a decorator @app.event("team_join") def ask_for_introduction(event, say): welcome_channel_id = "C12345" user_id = event["user"] text = f"Welcome to the team, <@{user_id}>! :tada: You can introduce yourself in this channel." say(text=text, channel=welcome_channel_id) # Pass a function to this method app.event("team_join")(ask_for_introduction) ``` Refer to [https://docs.slack.dev/apis/events-api/](https://docs.slack.dev/apis/events-api/) for details of Events API. To learn available arguments for middleware/listeners, see `[slack_bolt.kwargs_injection.args](../kwargs_injection/args.html "slack_bolt.kwargs_injection.args")`'s API document. ## Args **`event`** The conditions that match a request payload. If you pass a dict for this, you can have type, subtype in the constraint. **`matchers`** A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. **`middleware`** A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. `def function(self, callback_id: str | Pattern, matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](../middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None, auto_acknowledge: bool = True, ack_timeout: int = 3) ‑> Callable[..., Callable[..., [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def function( self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, auto_acknowledge: bool = True, ack_timeout: int = 3, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new Function listener. This method can be used as either a decorator or a method. # Use this method as a decorator @app.function("reverse") def reverse_string(ack: Ack, inputs: dict, complete: Complete, fail: Fail): try: ack() string_to_reverse = inputs["stringToReverse"] complete(outputs={"reverseString": string_to_reverse[::-1]}) except Exception as e: fail(f"Cannot reverse string (error: {e})") raise e # Pass a function to this method app.function("reverse")(reverse_string) To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: callback_id: The callback id to identify the function matchers: A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ if auto_acknowledge is True: if ack_timeout != 3: self._framework_logger.warning(warning_ack_timeout_has_no_effect(callback_id, ack_timeout)) matchers = list(matchers) if matchers else [] middleware = list(middleware) if middleware else [] def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.function_executed(callback_id=callback_id, base_logger=self._base_logger) return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge, ack_timeout) return __call__ ``` Registers a new Function listener. This method can be used as either a decorator or a method. ``` # Use this method as a decorator @app.function("reverse") def reverse_string(ack: Ack, inputs: dict, complete: Complete, fail: Fail): try: ack() string_to_reverse = inputs["stringToReverse"] complete(outputs={"reverseString": string_to_reverse[::-1]}) except Exception as e: fail(f"Cannot reverse string (error: {e})") raise e # Pass a function to this method app.function("reverse")(reverse_string) ``` To learn available arguments for middleware/listeners, see `[slack_bolt.kwargs_injection.args](../kwargs_injection/args.html "slack_bolt.kwargs_injection.args")`'s API document. ## Args **`callback_id`** The callback id to identify the function **`matchers`** A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. **`middleware`** A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. `def global_shortcut(self, callback_id: str | Pattern, matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](../middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def global_shortcut( self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new global shortcut listener.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.global_shortcut(callback_id, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ ``` Registers a new global shortcut listener. `def message(self, keyword: str | Pattern = '', matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](../middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def message( self, keyword: Union[str, Pattern] = "", matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new message event listener. This method can be used as either a decorator or a method. Check the `App#event` method's docstring for details. # Use this method as a decorator @app.message(":wave:") def say_hello(message, say): user = message['user'] say(f"Hi there, <@{user}>!") # Pass a function to this method app.message(":wave:")(say_hello) Refer to https://docs.slack.dev/reference/events/message/ for details of `message` events. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: keyword: The keyword to match matchers: A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ matchers = list(matchers) if matchers else [] middleware = list(middleware) if middleware else [] def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) constraints = { "type": "message", "subtype": ( # In most cases, new message events come with no subtype. None, # As of Jan 2021, most bot messages no longer have the subtype bot_message. # By contrast, messages posted using classic app's bot token still have the subtype. "bot_message", # If an end-user posts a message with "Also send to #channel" checked, # the message event comes with this subtype. "thread_broadcast", # If an end-user posts a message with attached files, # the message event comes with this subtype. "file_share", ), } primary_matcher = builtin_matchers.message_event( keyword=keyword, constraints=constraints, base_logger=self._base_logger ) if self._attaching_conversation_kwargs_enabled: middleware.insert(0, AttachingConversationKwargs(self._assistant_thread_context_store)) middleware.insert(0, MessageListenerMatches(keyword)) return self._register_listener(list(functions), primary_matcher, matchers, middleware, True) return __call__ ``` Registers a new message event listener. This method can be used as either a decorator or a method. Check the `App#event` method's docstring for details. ``` # Use this method as a decorator @app.message(":wave:") def say_hello(message, say): user = message['user'] say(f"Hi there, <@{user}>!") # Pass a function to this method app.message(":wave:")(say_hello) ``` Refer to [https://docs.slack.dev/reference/events/message/](https://docs.slack.dev/reference/events/message/) for details of `message` events. To learn available arguments for middleware/listeners, see `[slack_bolt.kwargs_injection.args](../kwargs_injection/args.html "slack_bolt.kwargs_injection.args")`'s API document. ## Args **`keyword`** The keyword to match **`matchers`** A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. **`middleware`** A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. `def message_shortcut(self, callback_id: str | Pattern, matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](../middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def message_shortcut( self, callback_id: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new message shortcut listener.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.message_shortcut(callback_id, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ ``` Registers a new message shortcut listener. `def middleware(self, *args) ‑> Callable | None` Expand source code ``` def middleware(self, *args) -> Optional[Callable]: """Registers a new middleware to this app. This method can be used as either a decorator or a method. # Use this method as a decorator @app.middleware def middleware_func(logger, body, next): logger.info(f"request body: {body}") next() # Pass a function to this method app.middleware(middleware_func) Refer to https://docs.slack.dev/tools/bolt-python/concepts/global-middleware for details. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: *args: A function that works as a global middleware. """ if len(args) > 0: middleware_or_callable = args[0] if isinstance(middleware_or_callable, Middleware): middleware: Middleware = middleware_or_callable self._middleware_list.append(middleware) if isinstance(middleware, Assistant) and middleware.thread_context_store is not None: self._assistant_thread_context_store = middleware.thread_context_store elif callable(middleware_or_callable): self._middleware_list.append( CustomMiddleware( app_name=self.name, func=middleware_or_callable, base_logger=self._base_logger, ) ) return middleware_or_callable else: raise BoltError(f"Unexpected type for a middleware ({type(middleware_or_callable)})") return None ``` Registers a new middleware to this app. This method can be used as either a decorator or a method. ``` # Use this method as a decorator @app.middleware def middleware_func(logger, body, next): logger.info(f"request body: {body}") next() # Pass a function to this method app.middleware(middleware_func) ``` Refer to [https://docs.slack.dev/tools/bolt-python/concepts/global-middleware](https://docs.slack.dev/tools/bolt-python/concepts/global-middleware) for details. To learn available arguments for middleware/listeners, see `[slack_bolt.kwargs_injection.args](../kwargs_injection/args.html "slack_bolt.kwargs_injection.args")`'s API document. ## Args **`*args`** A function that works as a global middleware. `def options(self, constraints: str | Pattern | Dict[str, str | Pattern], matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](../middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def options( self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new options listener. This method can be used as either a decorator or a method. # Use this method as a decorator @app.options("menu_selection") def show_menu_options(ack): options = [ { "text": {"type": "plain_text", "text": "Option 1"}, "value": "1-1", }, { "text": {"type": "plain_text", "text": "Option 2"}, "value": "1-2", }, ] ack(options=options) # Pass a function to this method app.options("menu_selection")(show_menu_options) Refer to the following documents for details: * https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element#external_select * https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: matchers: A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.options(constraints, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ ``` Registers a new options listener. This method can be used as either a decorator or a method. ``` # Use this method as a decorator @app.options("menu_selection") def show_menu_options(ack): options = [ { "text": {"type": "plain_text", "text": "Option 1"}, "value": "1-1", }, { "text": {"type": "plain_text", "text": "Option 2"}, "value": "1-2", }, ] ack(options=options) # Pass a function to this method app.options("menu_selection")(show_menu_options) ``` Refer to the following documents for details: * [https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element#external\_select](https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element#external_select) * [https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external\_multi\_select](https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select) To learn available arguments for middleware/listeners, see `[slack_bolt.kwargs_injection.args](../kwargs_injection/args.html "slack_bolt.kwargs_injection.args")`'s API document. ## Args **`matchers`** A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. **`middleware`** A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. `def shortcut(self, constraints: str | Pattern | Dict[str, str | Pattern], matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](../middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def shortcut( self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new shortcut listener. This method can be used as either a decorator or a method. # Use this method as a decorator @app.shortcut("open_modal") def open_modal(ack, body, client): # Acknowledge the command request ack() # Call views_open with the built-in client client.views_open( # Pass a valid trigger_id within 3 seconds of receiving it trigger_id=body["trigger_id"], # View payload view={ ... } ) # Pass a function to this method app.shortcut("open_modal")(open_modal) Refer to https://docs.slack.dev/interactivity/implementing-shortcuts/ for details about Shortcuts. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: constraints: The conditions that match a request payload. matchers: A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.shortcut(constraints, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ ``` Registers a new shortcut listener. This method can be used as either a decorator or a method. ``` # Use this method as a decorator @app.shortcut("open_modal") def open_modal(ack, body, client): # Acknowledge the command request ack() # Call views_open with the built-in client client.views_open( # Pass a valid trigger_id within 3 seconds of receiving it trigger_id=body["trigger_id"], # View payload view={ ... } ) # Pass a function to this method app.shortcut("open_modal")(open_modal) ``` Refer to [https://docs.slack.dev/interactivity/implementing-shortcuts/](https://docs.slack.dev/interactivity/implementing-shortcuts/) for details about Shortcuts. To learn available arguments for middleware/listeners, see `[slack_bolt.kwargs_injection.args](../kwargs_injection/args.html "slack_bolt.kwargs_injection.args")`'s API document. ## Args **`constraints`** The conditions that match a request payload. **`matchers`** A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. **`middleware`** A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. `def start(self, port: int = 3000, path: str = '/slack/events', http_server_logger_enabled: bool = True) ‑> None` Expand source code ``` def start( self, port: int = 3000, path: str = "/slack/events", http_server_logger_enabled: bool = True, ) -> None: """Starts a web server for local development. # With the default settings, `http://localhost:3000/slack/events` # is available for handling incoming requests from Slack app.start() This method internally starts a Web server process built with the `http.server` module. For production, consider using a production-ready WSGI server such as Gunicorn. Args: port: The port to listen on (Default: 3000) path: The path to handle request from Slack (Default: `/slack/events`) http_server_logger_enabled: The flag to enable http.server logging if True (Default: True) """ self._development_server = SlackAppDevelopmentServer( port=port, path=path, app=self, oauth_flow=self.oauth_flow, http_server_logger_enabled=http_server_logger_enabled, ) self._development_server.start() ``` Starts a web server for local development. ``` # With the default settings, `http://localhost:3000/slack/events` # is available for handling incoming requests from Slack app.start() ``` This method internally starts a Web server process built with the `http.server` module. For production, consider using a production-ready WSGI server such as Gunicorn. ## Args **`port`** The port to listen on (Default: 3000) **`path`** The path to handle request from Slack (Default: `/slack/events`) **`http_server_logger_enabled`** The flag to enable http.server logging if True (Default: True) `def step(self, callback_id: str | Pattern | [WorkflowStep](../workflows/step/step.html#slack_bolt.workflows.step.step.WorkflowStep "slack_bolt.workflows.step.step.WorkflowStep") | [WorkflowStepBuilder](../workflows/step/step.html#slack_bolt.workflows.step.step.WorkflowStepBuilder "slack_bolt.workflows.step.step.WorkflowStepBuilder"), edit: Callable[..., [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | [Listener](../listener/listener.html#slack_bolt.listener.listener.Listener "slack_bolt.listener.listener.Listener") | Sequence[Callable] | None = None, save: Callable[..., [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | [Listener](../listener/listener.html#slack_bolt.listener.listener.Listener "slack_bolt.listener.listener.Listener") | Sequence[Callable] | None = None, execute: Callable[..., [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | [Listener](../listener/listener.html#slack_bolt.listener.listener.Listener "slack_bolt.listener.listener.Listener") | Sequence[Callable] | None = None)` Expand source code ``` def step( self, callback_id: Union[str, Pattern, WorkflowStep, WorkflowStepBuilder], edit: Optional[Union[Callable[..., Optional[BoltResponse]], Listener, Sequence[Callable]]] = None, save: Optional[Union[Callable[..., Optional[BoltResponse]], Listener, Sequence[Callable]]] = None, execute: Optional[Union[Callable[..., Optional[BoltResponse]], Listener, Sequence[Callable]]] = None, ): """ Deprecated: Steps from apps for legacy workflows are now deprecated. Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Registers a new step from app listener. Unlike others, this method doesn't behave as a decorator. If you want to register a step from app by a decorator, use `WorkflowStepBuilder`'s methods. # Create a new WorkflowStep instance from slack_bolt.workflows.step import WorkflowStep ws = WorkflowStep( callback_id="add_task", edit=edit, save=save, execute=execute, ) # Pass Step to set up listeners app.step(ws) Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details of steps from apps. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. For further information about WorkflowStep specific function arguments such as `configure`, `update`, `complete`, and `fail`, refer to `slack_bolt.workflows.step.utilities` API documents. Args: callback_id: The Callback ID for this step from app edit: The function for displaying a modal in the Workflow Builder save: The function for handling configuration in the Workflow Builder execute: The function for handling the step execution """ warnings.warn( ( "Steps from apps for legacy workflows are now deprecated. " "Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/" ), category=DeprecationWarning, ) step = callback_id if isinstance(callback_id, (str, Pattern)): step = WorkflowStep( callback_id=callback_id, edit=edit, # type: ignore[arg-type] save=save, # type: ignore[arg-type] execute=execute, # type: ignore[arg-type] base_logger=self._base_logger, ) elif isinstance(step, WorkflowStepBuilder): step = step.build(base_logger=self._base_logger) elif not isinstance(step, WorkflowStep): raise BoltError(f"Invalid step object ({type(step)})") self.use(WorkflowStepMiddleware(step)) ``` ## Deprecated Steps from apps for legacy workflows are now deprecated. Use new custom steps: [https://docs.slack.dev/workflows/workflow-steps/](https://docs.slack.dev/workflows/workflow-steps/) Registers a new step from app listener. Unlike others, this method doesn't behave as a decorator. If you want to register a step from app by a decorator, use `WorkflowStepBuilder`'s methods. ``` # Create a new WorkflowStep instance from slack_bolt.workflows.step import WorkflowStep ws = WorkflowStep( callback_id="add_task", edit=edit, save=save, execute=execute, ) # Pass Step to set up listeners app.step(ws) ``` Refer to [https://docs.slack.dev/legacy/legacy-steps-from-apps/](https://docs.slack.dev/legacy/legacy-steps-from-apps/) for details of steps from apps. To learn available arguments for middleware/listeners, see `[slack_bolt.kwargs_injection.args](../kwargs_injection/args.html "slack_bolt.kwargs_injection.args")`'s API document. For further information about WorkflowStep specific function arguments such as `configure`, `update`, `complete`, and `fail`, refer to `[slack_bolt.workflows.step.utilities](../workflows/step/utilities/index.html "slack_bolt.workflows.step.utilities")` API documents. ## Args **`callback_id`** The Callback ID for this step from app **`edit`** The function for displaying a modal in the Workflow Builder **`save`** The function for handling configuration in the Workflow Builder **`execute`** The function for handling the step execution `def use(self, *args) ‑> Callable | None` Expand source code ``` def use(self, *args) -> Optional[Callable]: """Registers a new global middleware to this app. This method can be used as either a decorator or a method. Refer to `App#middleware()` method's docstring for details.""" return self.middleware(*args) ``` Registers a new global middleware to this app. This method can be used as either a decorator or a method. Refer to `App#middleware()` method's docstring for details. `def view(self, constraints: str | Pattern | Dict[str, str | Pattern], matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](../middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def view( self, constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_submission`/`view_closed` event listener. This method can be used as either a decorator or a method. # Use this method as a decorator @app.view("view_1") def handle_submission(ack, body, client, view): # Assume there's an input block with `block_c` as the block_id and `dreamy_input` hopes_and_dreams = view["state"]["values"]["block_c"]["dreamy_input"] user = body["user"]["id"] # Validate the inputs errors = {} if hopes_and_dreams is not None and len(hopes_and_dreams) <= 5: errors["block_c"] = "The value must be longer than 5 characters" if len(errors) > 0: ack(response_action="errors", errors=errors) return # Acknowledge the view_submission event and close the modal ack() # Do whatever you want with the input data - here we're saving it to a DB # Pass a function to this method app.view("view_1")(handle_submission) Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload for details of payloads. To learn available arguments for middleware/listeners, see `slack_bolt.kwargs_injection.args`'s API document. Args: constraints: The conditions that match a request payload matchers: A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. middleware: A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.view(constraints, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ ``` Registers a new `view_submission`/`view_closed` event listener. This method can be used as either a decorator or a method. ``` # Use this method as a decorator @app.view("view_1") def handle_submission(ack, body, client, view): # Assume there's an input block with block\_c as the block_id and dreamy\_input hopes_and_dreams = view["state"]["values"]["block_c"]["dreamy_input"] user = body["user"]["id"] # Validate the inputs errors = {} if hopes_and_dreams is not None and len(hopes_and_dreams) <= 5: errors["block_c"] = "The value must be longer than 5 characters" if len(errors) > 0: ack(response_action="errors", errors=errors) return # Acknowledge the view_submission event and close the modal ack() # Do whatever you want with the input data - here we're saving it to a DB # Pass a function to this method app.view("view_1")(handle_submission) ``` Refer to [https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload](https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload) for details of payloads. To learn available arguments for middleware/listeners, see `[slack_bolt.kwargs_injection.args](../kwargs_injection/args.html "slack_bolt.kwargs_injection.args")`'s API document. ## Args **`constraints`** The conditions that match a request payload **`matchers`** A list of listener matcher functions. Only when all the matchers return True, the listener function can be invoked. **`middleware`** A list of lister middleware functions. Only when all the middleware call `next()` method, the listener function can be invoked. `def view_closed(self, constraints: str | Pattern, matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](../middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def view_closed( self, constraints: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_closed` listener. Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_closed for details.""" def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.view_closed(constraints, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ ``` Registers a new `view_closed` listener. Refer to [https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view\_closed](https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_closed) for details. `def view_submission(self, constraints: str | Pattern, matchers: Sequence[Callable[..., bool]] | None = None, middleware: Sequence[Callable | [Middleware](../middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None) ‑> Callable[..., Callable[..., [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | None]` Expand source code ``` def view_submission( self, constraints: Union[str, Pattern], matchers: Optional[Sequence[Callable[..., bool]]] = None, middleware: Optional[Sequence[Union[Callable, Middleware]]] = None, ) -> Callable[..., Optional[Callable[..., Optional[BoltResponse]]]]: """Registers a new `view_submission` listener. Refer to https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission for details. """ def __call__(*args, **kwargs): functions = self._to_listener_functions(kwargs) if kwargs else list(args) primary_matcher = builtin_matchers.view_submission(constraints, base_logger=self._base_logger) return self._register_listener(list(functions), primary_matcher, matchers, middleware) return __call__ ``` Registers a new `view_submission` listener. Refer to [https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view\_submission](https://docs.slack.dev/reference/interaction-payloads/view-interactions-payload/#view_submission) for details. --- Source: https://docs.slack.dev/tools/bolt-python/reference/authorization # Module slack_bolt.authorization Authorization is the process of determining which Slack credentials should be available while processing an incoming Slack event. Refer to [https://docs.slack.dev/tools/bolt-python/concepts/authorization](https://docs.slack.dev/tools/bolt-python/concepts/authorization) for details. ## Sub-modules `[slack_bolt.authorization.async_authorize](async_authorize.html "slack_bolt.authorization.async_authorize")` `[slack_bolt.authorization.async_authorize_args](async_authorize_args.html "slack_bolt.authorization.async_authorize_args")` `[slack_bolt.authorization.authorize](authorize.html "slack_bolt.authorization.authorize")` `[slack_bolt.authorization.authorize_args](authorize_args.html "slack_bolt.authorization.authorize_args")` `[slack_bolt.authorization.authorize_result](authorize_result.html "slack_bolt.authorization.authorize_result")` ## Classes `class AuthorizeResult (*, enterprise_id: str | None, team_id: str | None, team: str | None = None, url: str | None = None, bot_user_id: str | None = None, bot_id: str | None = None, bot_token: str | None = None, bot_scopes: str | Sequence[str] | None = None, user_id: str | None = None, user: str | None = None, user_token: str | None = None, user_scopes: str | Sequence[str] | None = None)` Expand source code ``` class AuthorizeResult(dict): """Authorize function call result""" enterprise_id: Optional[str] team_id: Optional[str] team: Optional[str] # since v1.18 url: Optional[str] # since v1.18 bot_id: Optional[str] bot_user_id: Optional[str] bot_token: Optional[str] bot_scopes: Optional[Sequence[str]] # since v1.17 user_id: Optional[str] user: Optional[str] # since v1.18 user_token: Optional[str] user_scopes: Optional[Sequence[str]] # since v1.17 def __init__( self, *, enterprise_id: Optional[str], team_id: Optional[str], team: Optional[str] = None, url: Optional[str] = None, # bot bot_user_id: Optional[str] = None, bot_id: Optional[str] = None, bot_token: Optional[str] = None, bot_scopes: Optional[Union[Sequence[str], str]] = None, # user user_id: Optional[str] = None, user: Optional[str] = None, user_token: Optional[str] = None, user_scopes: Optional[Union[Sequence[str], str]] = None, ): """ Args: enterprise_id: Organization ID (Enterprise Grid) starting with `E` team_id: Workspace ID starting with `T` team: Workspace name url: Workspace slack.com URL bot_user_id: Bot user's User ID starting with either `U` or `W` bot_id: Bot ID starting with `B` bot_token: Bot user access token starting with `xoxb-` bot_scopes: The scopes associated with the bot token user_id: The request user ID user: The request user's name user_token: User access token starting with `xoxp-` user_scopes: The scopes associated wth the user token """ self["enterprise_id"] = self.enterprise_id = enterprise_id self["team_id"] = self.team_id = team_id self["team"] = self.team = team self["url"] = self.url = url # bot self["bot_user_id"] = self.bot_user_id = bot_user_id self["bot_id"] = self.bot_id = bot_id self["bot_token"] = self.bot_token = bot_token if bot_scopes is not None and isinstance(bot_scopes, str): bot_scopes = [scope.strip() for scope in bot_scopes.split(",")] self["bot_scopes"] = self.bot_scopes = bot_scopes # user self["user_id"] = self.user_id = user_id self["user"] = self.user = user self["user_token"] = self.user_token = user_token if user_scopes is not None and isinstance(user_scopes, str): user_scopes = [scope.strip() for scope in user_scopes.split(",")] self["user_scopes"] = self.user_scopes = user_scopes @classmethod def from_auth_test_response( cls, *, bot_token: Optional[str] = None, user_token: Optional[str] = None, bot_scopes: Optional[Union[Sequence[str], str]] = None, user_scopes: Optional[Union[Sequence[str], str]] = None, auth_test_response: Union[SlackResponse, "AsyncSlackResponse"], # type: ignore[name-defined] user_auth_test_response: Optional[Union[SlackResponse, "AsyncSlackResponse"]] = None, # type: ignore[name-defined] ) -> "AuthorizeResult": bot_user_id: Optional[str] = ( auth_test_response.get("user_id") if auth_test_response.get("bot_id") is not None else None ) user_id: Optional[str] = auth_test_response.get("user_id") if auth_test_response.get("bot_id") is None else None user_name: Optional[str] = auth_test_response.get("user") if user_id is None and user_auth_test_response is not None: user_id = user_auth_test_response.get("user_id") user_name = user_auth_test_response.get("user") return AuthorizeResult( enterprise_id=auth_test_response.get("enterprise_id"), team_id=auth_test_response.get("team_id"), team=auth_test_response.get("team"), url=auth_test_response.get("url"), bot_id=auth_test_response.get("bot_id"), bot_user_id=bot_user_id, bot_scopes=bot_scopes, user_id=user_id, user=user_name, bot_token=bot_token, user_token=user_token, user_scopes=user_scopes, ) ``` Authorize function call result ## Args **`enterprise_id`** Organization ID (Enterprise Grid) starting with `E` **`team_id`** Workspace ID starting with `T` **`team`** Workspace name **`url`** Workspace slack.com URL **`bot_user_id`** Bot user's User ID starting with either `U` or `W` **`bot_id`** Bot ID starting with `B` **`bot_token`** Bot user access token starting with `xoxb-` **`bot_scopes`** The scopes associated with the bot token **`user_id`** The request user ID **`user`** The request user's name **`user_token`** User access token starting with `xoxp-` **`user_scopes`** The scopes associated wth the user token ### Ancestors * builtins.dict ### Class variables `var bot_id : str | None` The type of the None singleton. `var bot_scopes : Sequence[str] | None` The type of the None singleton. `var bot_token : str | None` The type of the None singleton. `var bot_user_id : str | None` The type of the None singleton. `var enterprise_id : str | None` The type of the None singleton. `var team : str | None` The type of the None singleton. `var team_id : str | None` The type of the None singleton. `var url : str | None` The type of the None singleton. `var user : str | None` The type of the None singleton. `var user_id : str | None` The type of the None singleton. `var user_scopes : Sequence[str] | None` The type of the None singleton. `var user_token : str | None` The type of the None singleton. ### Static methods `def from_auth_test_response(*, bot_token: str | None = None, user_token: str | None = None, bot_scopes: str | Sequence[str] | None = None, user_scopes: str | Sequence[str] | None = None, auth_test_response: slack_sdk.web.slack_response.SlackResponse | ForwardRef('AsyncSlackResponse'), user_auth_test_response: slack_sdk.web.slack_response.SlackResponse | ForwardRef('AsyncSlackResponse') | None = None)` --- Source: https://docs.slack.dev/tools/bolt-python/reference/context # Module slack_bolt.context All listeners have access to a context dictionary, which can be used to enrich events with additional information. Bolt automatically attaches information that is included in the incoming event, like `user_id`, `team_id`, `channel_id`, and `enterprise_id`. Refer to [https://docs.slack.dev/tools/bolt-python/concepts/context](https://docs.slack.dev/tools/bolt-python/concepts/context) for details. ## Sub-modules `[slack_bolt.context.ack](ack/index.html "slack_bolt.context.ack")` `[slack_bolt.context.assistant](assistant/index.html "slack_bolt.context.assistant")` `[slack_bolt.context.async_context](async_context.html "slack_bolt.context.async_context")` `[slack_bolt.context.base_context](base_context.html "slack_bolt.context.base_context")` `[slack_bolt.context.complete](complete/index.html "slack_bolt.context.complete")` `[slack_bolt.context.context](context.html "slack_bolt.context.context")` `[slack_bolt.context.fail](fail/index.html "slack_bolt.context.fail")` `[slack_bolt.context.get_thread_context](get_thread_context/index.html "slack_bolt.context.get_thread_context")` `[slack_bolt.context.respond](respond/index.html "slack_bolt.context.respond")` `[slack_bolt.context.save_thread_context](save_thread_context/index.html "slack_bolt.context.save_thread_context")` `[slack_bolt.context.say](say/index.html "slack_bolt.context.say")` `[slack_bolt.context.say_stream](say_stream/index.html "slack_bolt.context.say_stream")` `[slack_bolt.context.set_status](set_status/index.html "slack_bolt.context.set_status")` `[slack_bolt.context.set_suggested_prompts](set_suggested_prompts/index.html "slack_bolt.context.set_suggested_prompts")` `[slack_bolt.context.set_title](set_title/index.html "slack_bolt.context.set_title")` ## Classes `class BoltContext (*args, **kwargs)` Expand source code ``` class BoltContext(BaseContext): """Context object associated with a request from Slack.""" def to_copyable(self) -> "BoltContext": new_dict = {} for prop_name, prop_value in self.items(): if prop_name in self.copyable_standard_property_names: # all the standard properties are copiable new_dict[prop_name] = prop_value elif prop_name in self.non_copyable_standard_property_names: # Do nothing with this property (e.g., listener_runner) continue else: try: copied_value = create_copy(prop_value) new_dict[prop_name] = copied_value except TypeError as te: self.logger.warning( f"Skipped setting '{prop_name}' to a copied request for lazy listeners " "due to a deep-copy creation error. Consider passing the value not as part of context object " f"(error: {te})" ) return BoltContext(new_dict) # The return type is intentionally string to avoid circular imports @property def listener_runner(self) -> "ThreadListenerRunner": """The properly configured listener_runner that is available for middleware/listeners.""" return self["listener_runner"] @property def client(self) -> WebClient: """The `WebClient` instance available for this request. @app.event("app_mention") def handle_events(context): context.client.chat_postMessage( channel=context.channel_id, text="Thanks!", ) # You can access "client" this way too. @app.event("app_mention") def handle_events(client, context): client.chat_postMessage( channel=context.channel_id, text="Thanks!", ) Returns: `WebClient` instance """ if "client" not in self: self["client"] = WebClient(token=None) return self["client"] @property def ack(self) -> Ack: """`ack()` function for this request. @app.action("button") def handle_button_clicks(context): context.ack() # You can access "ack" this way too. @app.action("button") def handle_button_clicks(ack): ack() Returns: Callable `ack()` function """ if "ack" not in self: self["ack"] = Ack() return self["ack"] @property def say(self) -> Say: """`say()` function for this request. @app.action("button") def handle_button_clicks(context): context.ack() context.say("Hi!") # You can access "ack" this way too. @app.action("button") def handle_button_clicks(ack, say): ack() say("Hi!") Returns: Callable `say()` function """ if "say" not in self: self["say"] = Say(client=self.client, channel=self.channel_id) return self["say"] @property def respond(self) -> Optional[Respond]: """`respond()` function for this request. @app.action("button") def handle_button_clicks(context): context.ack() context.respond("Hi!") # You can access "ack" this way too. @app.action("button") def handle_button_clicks(ack, respond): ack() respond("Hi!") Returns: Callable `respond()` function """ if "respond" not in self: self["respond"] = Respond( response_url=self.response_url, proxy=self.client.proxy, ssl=self.client.ssl, ) return self["respond"] @property def complete(self) -> Complete: """`complete()` function for this request. Once a custom function's state is set to complete, any outputs the function returns will be passed along to the next step of its housing workflow, or complete the workflow if the function is the last step in a workflow. Additionally, any interactivity handlers associated to a function invocation will no longer be invocable. @app.function("reverse") def handle_button_clicks(ack, complete): ack() complete(outputs={"stringReverse":"olleh"}) @app.function("reverse") def handle_button_clicks(context): context.ack() context.complete(outputs={"stringReverse":"olleh"}) Returns: Callable `complete()` function """ if "complete" not in self: self["complete"] = Complete(client=self.client, function_execution_id=self.function_execution_id) return self["complete"] @property def fail(self) -> Fail: """`fail()` function for this request. Once a custom function's state is set to error, its housing workflow will be interrupted and any provided error message will be passed on to the end user through SlackBot. Additionally, any interactivity handlers associated to a function invocation will no longer be invocable. @app.function("reverse") def handle_button_clicks(ack, fail): ack() fail(error="something went wrong") @app.function("reverse") def handle_button_clicks(context): context.ack() context.fail(error="something went wrong") Returns: Callable `fail()` function """ if "fail" not in self: self["fail"] = Fail(client=self.client, function_execution_id=self.function_execution_id) return self["fail"] @property def set_title(self) -> Optional[SetTitle]: return self.get("set_title") @property def set_status(self) -> Optional[SetStatus]: return self.get("set_status") @property def set_suggested_prompts(self) -> Optional[SetSuggestedPrompts]: return self.get("set_suggested_prompts") @property def get_thread_context(self) -> Optional[GetThreadContext]: return self.get("get_thread_context") @property def say_stream(self) -> Optional[SayStream]: return self.get("say_stream") @property def save_thread_context(self) -> Optional[SaveThreadContext]: return self.get("save_thread_context") ``` Context object associated with a request from Slack. ### Ancestors * [BaseContext](base_context.html#slack_bolt.context.base_context.BaseContext "slack_bolt.context.base_context.BaseContext") * builtins.dict ### Instance variables `prop ack : [Ack](ack/ack.html#slack_bolt.context.ack.ack.Ack "slack_bolt.context.ack.ack.Ack")` Expand source code ``` @property def ack(self) -> Ack: """`ack()` function for this request. @app.action("button") def handle_button_clicks(context): context.ack() # You can access "ack" this way too. @app.action("button") def handle_button_clicks(ack): ack() Returns: Callable `ack()` function """ if "ack" not in self: self["ack"] = Ack() return self["ack"] ``` `[slack_bolt.context.ack](ack/index.html "slack_bolt.context.ack")` function for this request. ``` @app.action("button") def handle_button_clicks(context): context.ack() # You can access "ack" this way too. @app.action("button") def handle_button_clicks(ack): ack() ``` ## Returns Callable `[slack_bolt.context.ack](ack/index.html "slack_bolt.context.ack")` function `prop client : slack_sdk.web.client.WebClient` Expand source code ``` @property def client(self) -> WebClient: """The `WebClient` instance available for this request. @app.event("app_mention") def handle_events(context): context.client.chat_postMessage( channel=context.channel_id, text="Thanks!", ) # You can access "client" this way too. @app.event("app_mention") def handle_events(client, context): client.chat_postMessage( channel=context.channel_id, text="Thanks!", ) Returns: `WebClient` instance """ if "client" not in self: self["client"] = WebClient(token=None) return self["client"] ``` The `WebClient` instance available for this request. ``` @app.event("app_mention") def handle_events(context): context.client.chat_postMessage( channel=context.channel_id, text="Thanks!", ) # You can access "client" this way too. @app.event("app_mention") def handle_events(client, context): client.chat_postMessage( channel=context.channel_id, text="Thanks!", ) ``` ## Returns `WebClient` instance `prop complete : [Complete](complete/complete.html#slack_bolt.context.complete.complete.Complete "slack_bolt.context.complete.complete.Complete")` Expand source code ``` @property def complete(self) -> Complete: """`complete()` function for this request. Once a custom function's state is set to complete, any outputs the function returns will be passed along to the next step of its housing workflow, or complete the workflow if the function is the last step in a workflow. Additionally, any interactivity handlers associated to a function invocation will no longer be invocable. @app.function("reverse") def handle_button_clicks(ack, complete): ack() complete(outputs={"stringReverse":"olleh"}) @app.function("reverse") def handle_button_clicks(context): context.ack() context.complete(outputs={"stringReverse":"olleh"}) Returns: Callable `complete()` function """ if "complete" not in self: self["complete"] = Complete(client=self.client, function_execution_id=self.function_execution_id) return self["complete"] ``` `[slack_bolt.context.complete](complete/index.html "slack_bolt.context.complete")` function for this request. Once a custom function's state is set to complete, any outputs the function returns will be passed along to the next step of its housing workflow, or complete the workflow if the function is the last step in a workflow. Additionally, any interactivity handlers associated to a function invocation will no longer be invocable. ``` @app.function("reverse") def handle_button_clicks(ack, complete): ack() complete(outputs={"stringReverse":"olleh"}) @app.function("reverse") def handle_button_clicks(context): context.ack() context.complete(outputs={"stringReverse":"olleh"}) ``` ## Returns Callable `[slack_bolt.context.complete](complete/index.html "slack_bolt.context.complete")` function `prop fail : [Fail](fail/fail.html#slack_bolt.context.fail.fail.Fail "slack_bolt.context.fail.fail.Fail")` Expand source code ``` @property def fail(self) -> Fail: """`fail()` function for this request. Once a custom function's state is set to error, its housing workflow will be interrupted and any provided error message will be passed on to the end user through SlackBot. Additionally, any interactivity handlers associated to a function invocation will no longer be invocable. @app.function("reverse") def handle_button_clicks(ack, fail): ack() fail(error="something went wrong") @app.function("reverse") def handle_button_clicks(context): context.ack() context.fail(error="something went wrong") Returns: Callable `fail()` function """ if "fail" not in self: self["fail"] = Fail(client=self.client, function_execution_id=self.function_execution_id) return self["fail"] ``` `[slack_bolt.context.fail](fail/index.html "slack_bolt.context.fail")` function for this request. Once a custom function's state is set to error, its housing workflow will be interrupted and any provided error message will be passed on to the end user through SlackBot. Additionally, any interactivity handlers associated to a function invocation will no longer be invocable. ``` @app.function("reverse") def handle_button_clicks(ack, fail): ack() fail(error="something went wrong") @app.function("reverse") def handle_button_clicks(context): context.ack() context.fail(error="something went wrong") ``` ## Returns Callable `[slack_bolt.context.fail](fail/index.html "slack_bolt.context.fail")` function `prop get_thread_context : [GetThreadContext](get_thread_context/get_thread_context.html#slack_bolt.context.get_thread_context.get_thread_context.GetThreadContext "slack_bolt.context.get_thread_context.get_thread_context.GetThreadContext") | None` Expand source code ``` @property def get_thread_context(self) -> Optional[GetThreadContext]: return self.get("get_thread_context") ``` `prop listener_runner : ThreadListenerRunner` Expand source code ``` @property def listener_runner(self) -> "ThreadListenerRunner": """The properly configured listener_runner that is available for middleware/listeners.""" return self["listener_runner"] ``` The properly configured listener\_runner that is available for middleware/listeners. `prop respond : [Respond](respond/respond.html#slack_bolt.context.respond.respond.Respond "slack_bolt.context.respond.respond.Respond") | None` Expand source code ``` @property def respond(self) -> Optional[Respond]: """`respond()` function for this request. @app.action("button") def handle_button_clicks(context): context.ack() context.respond("Hi!") # You can access "ack" this way too. @app.action("button") def handle_button_clicks(ack, respond): ack() respond("Hi!") Returns: Callable `respond()` function """ if "respond" not in self: self["respond"] = Respond( response_url=self.response_url, proxy=self.client.proxy, ssl=self.client.ssl, ) return self["respond"] ``` `[slack_bolt.context.respond](respond/index.html "slack_bolt.context.respond")` function for this request. ``` @app.action("button") def handle_button_clicks(context): context.ack() context.respond("Hi!") # You can access "ack" this way too. @app.action("button") def handle_button_clicks(ack, respond): ack() respond("Hi!") ``` ## Returns Callable `[slack_bolt.context.respond](respond/index.html "slack_bolt.context.respond")` function `prop save_thread_context : [SaveThreadContext](save_thread_context/save_thread_context.html#slack_bolt.context.save_thread_context.save_thread_context.SaveThreadContext "slack_bolt.context.save_thread_context.save_thread_context.SaveThreadContext") | None` Expand source code ``` @property def save_thread_context(self) -> Optional[SaveThreadContext]: return self.get("save_thread_context") ``` `prop say : [Say](say/say.html#slack_bolt.context.say.say.Say "slack_bolt.context.say.say.Say")` Expand source code ``` @property def say(self) -> Say: """`say()` function for this request. @app.action("button") def handle_button_clicks(context): context.ack() context.say("Hi!") # You can access "ack" this way too. @app.action("button") def handle_button_clicks(ack, say): ack() say("Hi!") Returns: Callable `say()` function """ if "say" not in self: self["say"] = Say(client=self.client, channel=self.channel_id) return self["say"] ``` `[slack_bolt.context.say](say/index.html "slack_bolt.context.say")` function for this request. ``` @app.action("button") def handle_button_clicks(context): context.ack() context.say("Hi!") # You can access "ack" this way too. @app.action("button") def handle_button_clicks(ack, say): ack() say("Hi!") ``` ## Returns Callable `[slack_bolt.context.say](say/index.html "slack_bolt.context.say")` function `prop say_stream : [SayStream](say_stream/say_stream.html#slack_bolt.context.say_stream.say_stream.SayStream "slack_bolt.context.say_stream.say_stream.SayStream") | None` Expand source code ``` @property def say_stream(self) -> Optional[SayStream]: return self.get("say_stream") ``` `prop set_status : [SetStatus](set_status/set_status.html#slack_bolt.context.set_status.set_status.SetStatus "slack_bolt.context.set_status.set_status.SetStatus") | None` Expand source code ``` @property def set_status(self) -> Optional[SetStatus]: return self.get("set_status") ``` `prop set_suggested_prompts : [SetSuggestedPrompts](set_suggested_prompts/set_suggested_prompts.html#slack_bolt.context.set_suggested_prompts.set_suggested_prompts.SetSuggestedPrompts "slack_bolt.context.set_suggested_prompts.set_suggested_prompts.SetSuggestedPrompts") | None` Expand source code ``` @property def set_suggested_prompts(self) -> Optional[SetSuggestedPrompts]: return self.get("set_suggested_prompts") ``` `prop set_title : [SetTitle](set_title/set_title.html#slack_bolt.context.set_title.set_title.SetTitle "slack_bolt.context.set_title.set_title.SetTitle") | None` Expand source code ``` @property def set_title(self) -> Optional[SetTitle]: return self.get("set_title") ``` ### Methods `def to_copyable(self) ‑> [BoltContext](context.html#slack_bolt.context.context.BoltContext "slack_bolt.context.context.BoltContext")` Expand source code ``` def to_copyable(self) -> "BoltContext": new_dict = {} for prop_name, prop_value in self.items(): if prop_name in self.copyable_standard_property_names: # all the standard properties are copiable new_dict[prop_name] = prop_value elif prop_name in self.non_copyable_standard_property_names: # Do nothing with this property (e.g., listener_runner) continue else: try: copied_value = create_copy(prop_value) new_dict[prop_name] = copied_value except TypeError as te: self.logger.warning( f"Skipped setting '{prop_name}' to a copied request for lazy listeners " "due to a deep-copy creation error. Consider passing the value not as part of context object " f"(error: {te})" ) return BoltContext(new_dict) ``` ### Inherited members * `**[BaseContext](base_context.html#slack_bolt.context.base_context.BaseContext "slack_bolt.context.base_context.BaseContext")**`: * `[actor_enterprise_id](base_context.html#slack_bolt.context.base_context.BaseContext.actor_enterprise_id "slack_bolt.context.base_context.BaseContext.actor_enterprise_id")` * `[actor_team_id](base_context.html#slack_bolt.context.base_context.BaseContext.actor_team_id "slack_bolt.context.base_context.BaseContext.actor_team_id")` * `[actor_user_id](base_context.html#slack_bolt.context.base_context.BaseContext.actor_user_id "slack_bolt.context.base_context.BaseContext.actor_user_id")` * `[authorize_result](base_context.html#slack_bolt.context.base_context.BaseContext.authorize_result "slack_bolt.context.base_context.BaseContext.authorize_result")` * `[bot_id](base_context.html#slack_bolt.context.base_context.BaseContext.bot_id "slack_bolt.context.base_context.BaseContext.bot_id")` * `[bot_token](base_context.html#slack_bolt.context.base_context.BaseContext.bot_token "slack_bolt.context.base_context.BaseContext.bot_token")` * `[bot_user_id](base_context.html#slack_bolt.context.base_context.BaseContext.bot_user_id "slack_bolt.context.base_context.BaseContext.bot_user_id")` * `[channel_id](base_context.html#slack_bolt.context.base_context.BaseContext.channel_id "slack_bolt.context.base_context.BaseContext.channel_id")` * `[copyable_standard_property_names](base_context.html#slack_bolt.context.base_context.BaseContext.copyable_standard_property_names "slack_bolt.context.base_context.BaseContext.copyable_standard_property_names")` * `[enterprise_id](base_context.html#slack_bolt.context.base_context.BaseContext.enterprise_id "slack_bolt.context.base_context.BaseContext.enterprise_id")` * `[function_bot_access_token](base_context.html#slack_bolt.context.base_context.BaseContext.function_bot_access_token "slack_bolt.context.base_context.BaseContext.function_bot_access_token")` * `[function_execution_id](base_context.html#slack_bolt.context.base_context.BaseContext.function_execution_id "slack_bolt.context.base_context.BaseContext.function_execution_id")` * `[inputs](base_context.html#slack_bolt.context.base_context.BaseContext.inputs "slack_bolt.context.base_context.BaseContext.inputs")` * `[is_enterprise_install](base_context.html#slack_bolt.context.base_context.BaseContext.is_enterprise_install "slack_bolt.context.base_context.BaseContext.is_enterprise_install")` * `[logger](base_context.html#slack_bolt.context.base_context.BaseContext.logger "slack_bolt.context.base_context.BaseContext.logger")` * `[matches](base_context.html#slack_bolt.context.base_context.BaseContext.matches "slack_bolt.context.base_context.BaseContext.matches")` * `[non_copyable_standard_property_names](base_context.html#slack_bolt.context.base_context.BaseContext.non_copyable_standard_property_names "slack_bolt.context.base_context.BaseContext.non_copyable_standard_property_names")` * `[response_url](base_context.html#slack_bolt.context.base_context.BaseContext.response_url "slack_bolt.context.base_context.BaseContext.response_url")` * `[standard_property_names](base_context.html#slack_bolt.context.base_context.BaseContext.standard_property_names "slack_bolt.context.base_context.BaseContext.standard_property_names")` * `[team_id](base_context.html#slack_bolt.context.base_context.BaseContext.team_id "slack_bolt.context.base_context.BaseContext.team_id")` * `[thread_ts](base_context.html#slack_bolt.context.base_context.BaseContext.thread_ts "slack_bolt.context.base_context.BaseContext.thread_ts")` * `[token](base_context.html#slack_bolt.context.base_context.BaseContext.token "slack_bolt.context.base_context.BaseContext.token")` * `[user_id](base_context.html#slack_bolt.context.base_context.BaseContext.user_id "slack_bolt.context.base_context.BaseContext.user_id")` * `[user_token](base_context.html#slack_bolt.context.base_context.BaseContext.user_token "slack_bolt.context.base_context.BaseContext.user_token")` --- Source: https://docs.slack.dev/tools/bolt-python/reference/context/ack # Module slack_bolt.context.ack ## Sub-modules `[slack_bolt.context.ack.ack](ack.html "slack_bolt.context.ack.ack")` `[slack_bolt.context.ack.async_ack](async_ack.html "slack_bolt.context.ack.async_ack")` `[slack_bolt.context.ack.internals](internals.html "slack_bolt.context.ack.internals")` ## Classes `class Ack` Expand source code ``` class Ack: response: Optional[BoltResponse] def __init__(self): self.response: Optional[BoltResponse] = None def __call__( self, text: Union[str, dict] = "", # text: str or whole_response: dict blocks: Optional[Sequence[Union[dict, Block]]] = None, attachments: Optional[Sequence[Union[dict, Attachment]]] = None, unfurl_links: Optional[bool] = None, unfurl_media: Optional[bool] = None, response_type: Optional[str] = None, # in_channel / ephemeral # block_suggestion / dialog_suggestion options: Optional[Sequence[Union[dict, Option]]] = None, option_groups: Optional[Sequence[Union[dict, OptionGroup]]] = None, # view_submission response_action: Optional[str] = None, # errors / update / push / clear errors: Optional[Dict[str, str]] = None, view: Optional[Union[dict, View]] = None, ) -> BoltResponse: return _set_response( self, text_or_whole_response=text, blocks=blocks, attachments=attachments, unfurl_links=unfurl_links, unfurl_media=unfurl_media, response_type=response_type, options=options, option_groups=option_groups, response_action=response_action, errors=errors, view=view, ) ``` ### Class variables `var response : [BoltResponse](../../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None` The type of the None singleton. --- Source: https://docs.slack.dev/tools/bolt-python/reference/context/assistant # Module slack_bolt.context.assistant ## Sub-modules `[slack_bolt.context.assistant.assistant_utilities](assistant_utilities.html "slack_bolt.context.assistant.assistant_utilities")` `[slack_bolt.context.assistant.async_assistant_utilities](async_assistant_utilities.html "slack_bolt.context.assistant.async_assistant_utilities")` `[slack_bolt.context.assistant.internals](internals.html "slack_bolt.context.assistant.internals")` `[slack_bolt.context.assistant.thread_context](thread_context/index.html "slack_bolt.context.assistant.thread_context")` `[slack_bolt.context.assistant.thread_context_store](thread_context_store/index.html "slack_bolt.context.assistant.thread_context_store")` --- Source: https://docs.slack.dev/tools/bolt-python/reference/context/assistant/thread_context # Module slack_bolt.context.assistant.thread_context ## Classes `class AssistantThreadContext (payload: dict)` Expand source code ``` class AssistantThreadContext(dict): enterprise_id: Optional[str] team_id: Optional[str] channel_id: str def __init__(self, payload: dict): dict.__init__(self, **payload) self.enterprise_id = payload.get("enterprise_id") self.team_id = payload.get("team_id") self.channel_id = payload["channel_id"] ``` dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d\[k\] = v dict(\*\*kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2) ### Ancestors * builtins.dict ### Class variables `var channel_id : str` The type of the None singleton. `var enterprise_id : str | None` The type of the None singleton. `var team_id : str | None` The type of the None singleton. --- Source: https://docs.slack.dev/tools/bolt-python/reference/context/assistant/thread_context_store # Module slack_bolt.context.assistant.thread_context_store ## Sub-modules `[slack_bolt.context.assistant.thread_context_store.async_store](async_store.html "slack_bolt.context.assistant.thread_context_store.async_store")` `[slack_bolt.context.assistant.thread_context_store.default_async_store](default_async_store.html "slack_bolt.context.assistant.thread_context_store.default_async_store")` `[slack_bolt.context.assistant.thread_context_store.default_store](default_store.html "slack_bolt.context.assistant.thread_context_store.default_store")` `[slack_bolt.context.assistant.thread_context_store.file](file/index.html "slack_bolt.context.assistant.thread_context_store.file")` `[slack_bolt.context.assistant.thread_context_store.store](store.html "slack_bolt.context.assistant.thread_context_store.store")` --- Source: https://docs.slack.dev/tools/bolt-python/reference/context/assistant/thread_context_store/file # Module slack_bolt.context.assistant.thread_context_store.file ## Classes `class FileAssistantThreadContextStore (base_dir: str = '/Users/eden.zimbelman/.bolt-app-assistant-thread-contexts')` Expand source code ``` class FileAssistantThreadContextStore(AssistantThreadContextStore): def __init__( self, base_dir: str = str(Path.home()) + "/.bolt-app-assistant-thread-contexts", ): self.base_dir = base_dir self._mkdir(self.base_dir) def save(self, *, channel_id: str, thread_ts: str, context: Dict[str, str]) -> None: path = f"{self.base_dir}/{channel_id}-{thread_ts}.json" with open(path, "w") as f: f.write(json.dumps(context)) def find(self, *, channel_id: str, thread_ts: str) -> Optional[AssistantThreadContext]: path = f"{self.base_dir}/{channel_id}-{thread_ts}.json" try: with open(path) as f: data = json.loads(f.read()) if data.get("channel_id") is not None: return AssistantThreadContext(data) except FileNotFoundError: pass return None @staticmethod def _mkdir(path: Union[str, Path]): if isinstance(path, str): path = Path(path) path.mkdir(parents=True, exist_ok=True) ``` ### Ancestors * [AssistantThreadContextStore](../store.html#slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore "slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore") ### Methods `def find(self, *, channel_id: str, thread_ts: str) ‑> [AssistantThreadContext](../../thread_context/index.html#slack_bolt.context.assistant.thread_context.AssistantThreadContext "slack_bolt.context.assistant.thread_context.AssistantThreadContext") | None` Expand source code ``` def find(self, *, channel_id: str, thread_ts: str) -> Optional[AssistantThreadContext]: path = f"{self.base_dir}/{channel_id}-{thread_ts}.json" try: with open(path) as f: data = json.loads(f.read()) if data.get("channel_id") is not None: return AssistantThreadContext(data) except FileNotFoundError: pass return None ``` `def save(self, *, channel_id: str, thread_ts: str, context: Dict[str, str]) ‑> None` Expand source code ``` def save(self, *, channel_id: str, thread_ts: str, context: Dict[str, str]) -> None: path = f"{self.base_dir}/{channel_id}-{thread_ts}.json" with open(path, "w") as f: f.write(json.dumps(context)) ``` --- Source: https://docs.slack.dev/tools/bolt-python/reference/context/complete # Module slack_bolt.context.complete ## Sub-modules `[slack_bolt.context.complete.async_complete](async_complete.html "slack_bolt.context.complete.async_complete")` `[slack_bolt.context.complete.complete](complete.html "slack_bolt.context.complete.complete")` ## Classes `class Complete (client: slack_sdk.web.client.WebClient, function_execution_id: str | None)` Expand source code ``` class Complete: client: WebClient function_execution_id: Optional[str] _called: bool def __init__( self, client: WebClient, function_execution_id: Optional[str], ): self.client = client self.function_execution_id = function_execution_id self._called = False def __call__(self, outputs: Optional[Dict[str, Any]] = None) -> SlackResponse: """Signal the successful completion of the custom function. Kwargs: outputs: Json serializable object containing the output values Returns: SlackResponse: The response object returned from slack Raises: ValueError: If this function cannot be used. """ if self.function_execution_id is None: raise ValueError("complete is unsupported here as there is no function_execution_id") self._called = True return self.client.functions_completeSuccess(function_execution_id=self.function_execution_id, outputs=outputs or {}) def has_been_called(self) -> bool: """Check if this complete function has been called. Returns: bool: True if the complete function has been called, False otherwise. """ return self._called ``` ### Class variables `var client : slack_sdk.web.client.WebClient` The type of the None singleton. `var function_execution_id : str | None` The type of the None singleton. ### Methods `def has_been_called(self) ‑> bool` Expand source code ``` def has_been_called(self) -> bool: """Check if this complete function has been called. Returns: bool: True if the complete function has been called, False otherwise. """ return self._called ``` Check if this complete function has been called. ## Returns `bool` True if the complete function has been called, False otherwise. --- Source: https://docs.slack.dev/tools/bolt-python/reference/context/fail # Module slack_bolt.context.fail ## Sub-modules `[slack_bolt.context.fail.async_fail](async_fail.html "slack_bolt.context.fail.async_fail")` `[slack_bolt.context.fail.fail](fail.html "slack_bolt.context.fail.fail")` ## Classes `class Fail (client: slack_sdk.web.client.WebClient, function_execution_id: str | None)` Expand source code ``` class Fail: client: WebClient function_execution_id: Optional[str] _called: bool def __init__( self, client: WebClient, function_execution_id: Optional[str], ): self.client = client self.function_execution_id = function_execution_id self._called = False def __call__(self, error: str) -> SlackResponse: """Signal that the custom function failed to complete. Kwargs: error: Error message to return to slack Returns: SlackResponse: The response object returned from slack Raises: ValueError: If this function cannot be used. """ if self.function_execution_id is None: raise ValueError("fail is unsupported here as there is no function_execution_id") self._called = True return self.client.functions_completeError(function_execution_id=self.function_execution_id, error=error) def has_been_called(self) -> bool: """Check if this fail function has been called. Returns: bool: True if the fail function has been called, False otherwise. """ return self._called ``` ### Class variables `var client : slack_sdk.web.client.WebClient` The type of the None singleton. `var function_execution_id : str | None` The type of the None singleton. ### Methods `def has_been_called(self) ‑> bool` Expand source code ``` def has_been_called(self) -> bool: """Check if this fail function has been called. Returns: bool: True if the fail function has been called, False otherwise. """ return self._called ``` Check if this fail function has been called. ## Returns `bool` True if the fail function has been called, False otherwise. --- Source: https://docs.slack.dev/tools/bolt-python/reference/context/get_thread_context # Module slack_bolt.context.get_thread_context ## Sub-modules `[slack_bolt.context.get_thread_context.async_get_thread_context](async_get_thread_context.html "slack_bolt.context.get_thread_context.async_get_thread_context")` `[slack_bolt.context.get_thread_context.get_thread_context](get_thread_context.html "slack_bolt.context.get_thread_context.get_thread_context")` ## Classes `class GetThreadContext (thread_context_store: [AssistantThreadContextStore](../assistant/thread_context_store/store.html#slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore "slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore"), channel_id: str, thread_ts: str, payload: dict)` Expand source code ``` class GetThreadContext: thread_context_store: AssistantThreadContextStore payload: dict channel_id: str thread_ts: str _thread_context: Optional[AssistantThreadContext] thread_context_loaded: bool def __init__( self, thread_context_store: AssistantThreadContextStore, channel_id: str, thread_ts: str, payload: dict, ): self.thread_context_store = thread_context_store self.payload = payload self.channel_id = channel_id self.thread_ts = thread_ts self._thread_context: Optional[AssistantThreadContext] = None self.thread_context_loaded = False def __call__(self) -> Optional[AssistantThreadContext]: if self.thread_context_loaded is True: return self._thread_context thread = self.payload.get("assistant_thread") if isinstance(thread, dict) and thread.get("context", {}).get("channel_id") is not None: # assistant_thread_started self._thread_context = AssistantThreadContext(thread["context"]) # for this event, the context will never be changed self.thread_context_loaded = True elif self.payload.get("channel") is not None and self.payload.get("thread_ts") is not None: # message event self._thread_context = self.thread_context_store.find(channel_id=self.channel_id, thread_ts=self.thread_ts) return self._thread_context ``` ### Class variables `var channel_id : str` The type of the None singleton. `var payload : dict` The type of the None singleton. `var thread_context_loaded : bool` The type of the None singleton. `var thread_context_store : [AssistantThreadContextStore](../assistant/thread_context_store/store.html#slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore "slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore")` The type of the None singleton. `var thread_ts : str` The type of the None singleton. --- Source: https://docs.slack.dev/tools/bolt-python/reference/context/respond # Module slack_bolt.context.respond ## Sub-modules `[slack_bolt.context.respond.async_respond](async_respond.html "slack_bolt.context.respond.async_respond")` `[slack_bolt.context.respond.internals](internals.html "slack_bolt.context.respond.internals")` `[slack_bolt.context.respond.respond](respond.html "slack_bolt.context.respond.respond")` ## Classes `class Respond (*, response_url: str | None, proxy: str | None = None, ssl: ssl.SSLContext | None = None)` Expand source code ``` class Respond: response_url: Optional[str] proxy: Optional[str] ssl: Optional[SSLContext] def __init__( self, *, response_url: Optional[str], proxy: Optional[str] = None, ssl: Optional[SSLContext] = None, ): self.response_url = response_url self.proxy = proxy self.ssl = ssl def __call__( self, text: Union[str, dict] = "", blocks: Optional[Sequence[Union[dict, Block]]] = None, attachments: Optional[Sequence[Union[dict, Attachment]]] = None, 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, thread_ts: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None, ) -> WebhookResponse: if self.response_url is not None: client = WebhookClient( url=self.response_url, proxy=self.proxy, ssl=self.ssl, ) text_or_whole_response: Union[str, dict] = text if isinstance(text_or_whole_response, str): text = text_or_whole_response message = _build_message( text=text, blocks=blocks, attachments=attachments, response_type=response_type, replace_original=replace_original, delete_original=delete_original, unfurl_links=unfurl_links, unfurl_media=unfurl_media, thread_ts=thread_ts, metadata=metadata, ) return client.send_dict(message) elif isinstance(text_or_whole_response, dict): message = _build_message(**text_or_whole_response) return client.send_dict(message) else: raise ValueError(f"The arg is unexpected type ({type(text_or_whole_response)})") else: raise ValueError("respond is unsupported here as there is no response_url") ``` ### Class variables `var proxy : str | None` The type of the None singleton. `var response_url : str | None` The type of the None singleton. `var ssl : ssl.SSLContext | None` The type of the None singleton. --- Source: https://docs.slack.dev/tools/bolt-python/reference/context/save_thread_context # Module slack_bolt.context.save_thread_context ## Sub-modules `[slack_bolt.context.save_thread_context.async_save_thread_context](async_save_thread_context.html "slack_bolt.context.save_thread_context.async_save_thread_context")` `[slack_bolt.context.save_thread_context.save_thread_context](save_thread_context.html "slack_bolt.context.save_thread_context.save_thread_context")` ## Classes `class SaveThreadContext (thread_context_store: [AssistantThreadContextStore](../assistant/thread_context_store/store.html#slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore "slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore"), channel_id: str, thread_ts: str)` Expand source code ``` class SaveThreadContext: thread_context_store: AssistantThreadContextStore channel_id: str thread_ts: str def __init__( self, thread_context_store: AssistantThreadContextStore, channel_id: str, thread_ts: str, ): self.thread_context_store = thread_context_store self.channel_id = channel_id self.thread_ts = thread_ts def __call__(self, new_context: Dict[str, str]) -> None: self.thread_context_store.save( channel_id=self.channel_id, thread_ts=self.thread_ts, context=new_context, ) ``` ### Class variables `var channel_id : str` The type of the None singleton. `var thread_context_store : [AssistantThreadContextStore](../assistant/thread_context_store/store.html#slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore "slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore")` The type of the None singleton. `var thread_ts : str` The type of the None singleton. --- Source: https://docs.slack.dev/tools/bolt-python/reference/context/say # Module slack_bolt.context.say ## Sub-modules `[slack_bolt.context.say.async_say](async_say.html "slack_bolt.context.say.async_say")` `[slack_bolt.context.say.internals](internals.html "slack_bolt.context.say.internals")` `[slack_bolt.context.say.say](say.html "slack_bolt.context.say.say")` ## Classes `class Say (client: slack_sdk.web.client.WebClient | None, channel: str | None, thread_ts: str | None = None, metadata: Dict | slack_sdk.models.metadata.Metadata | None = None, build_metadata: Callable[[], Dict | slack_sdk.models.metadata.Metadata | None] | None = None)` Expand source code ``` class Say: client: Optional[WebClient] channel: Optional[str] thread_ts: Optional[str] metadata: Optional[Union[Dict, Metadata]] build_metadata: Optional[Callable[[], Optional[Union[Dict, Metadata]]]] def __init__( self, client: Optional[WebClient], channel: Optional[str], thread_ts: Optional[str] = None, metadata: Optional[Union[Dict, Metadata]] = None, build_metadata: Optional[Callable[[], Optional[Union[Dict, Metadata]]]] = None, ): self.client = client self.channel = channel self.thread_ts = thread_ts self.metadata = metadata self.build_metadata = build_metadata def __call__( self, text: Union[str, dict] = "", blocks: Optional[Sequence[Union[Dict, Block]]] = None, attachments: Optional[Sequence[Union[Dict, Attachment]]] = None, channel: Optional[str] = None, as_user: Optional[bool] = None, thread_ts: Optional[str] = None, reply_broadcast: Optional[bool] = None, unfurl_links: Optional[bool] = None, unfurl_media: Optional[bool] = None, icon_emoji: Optional[str] = None, icon_url: Optional[str] = None, username: Optional[str] = None, markdown_text: Optional[str] = None, mrkdwn: Optional[bool] = None, link_names: Optional[bool] = None, parse: Optional[str] = None, # none, full metadata: Optional[Union[Dict, Metadata]] = None, **kwargs, ) -> SlackResponse: if _can_say(self, channel): text_or_whole_response: Union[str, dict] = text if isinstance(text_or_whole_response, str): text = text_or_whole_response if metadata is None: metadata = self.build_metadata() if self.build_metadata is not None else self.metadata return self.client.chat_postMessage( # type: ignore[union-attr] channel=channel or self.channel, # type: ignore[arg-type] text=text, blocks=blocks, attachments=attachments, as_user=as_user, thread_ts=thread_ts or self.thread_ts, reply_broadcast=reply_broadcast, unfurl_links=unfurl_links, unfurl_media=unfurl_media, icon_emoji=icon_emoji, icon_url=icon_url, username=username, markdown_text=markdown_text, mrkdwn=mrkdwn, link_names=link_names, parse=parse, metadata=metadata, **kwargs, ) elif isinstance(text_or_whole_response, dict): message: dict = create_copy(text_or_whole_response) if "channel" not in message: message["channel"] = channel or self.channel if "thread_ts" not in message: message["thread_ts"] = thread_ts or self.thread_ts if "metadata" not in message: metadata = self.build_metadata() if self.build_metadata is not None else self.metadata message["metadata"] = metadata return self.client.chat_postMessage(**message) # type: ignore[union-attr] else: raise ValueError(f"The arg is unexpected type ({type(text_or_whole_response)})") else: raise ValueError("say without channel_id here is unsupported") ``` ### Class variables `var build_metadata : Callable[[], Dict | slack_sdk.models.metadata.Metadata | None] | None` The type of the None singleton. `var channel : str | None` The type of the None singleton. `var client : slack_sdk.web.client.WebClient | None` The type of the None singleton. `var metadata : Dict | slack_sdk.models.metadata.Metadata | None` The type of the None singleton. `var thread_ts : str | None` The type of the None singleton. --- Source: https://docs.slack.dev/tools/bolt-python/reference/context/say_stream # Module slack_bolt.context.say_stream ## Sub-modules `[slack_bolt.context.say_stream.async_say_stream](async_say_stream.html "slack_bolt.context.say_stream.async_say_stream")` `[slack_bolt.context.say_stream.say_stream](say_stream.html "slack_bolt.context.say_stream.say_stream")` ## Classes `class SayStream (*, client: slack_sdk.web.client.WebClient, channel: str | None = None, recipient_team_id: str | None = None, recipient_user_id: str | None = None, thread_ts: str | None = None)` Expand source code ``` class SayStream: client: WebClient channel: Optional[str] recipient_team_id: Optional[str] recipient_user_id: Optional[str] thread_ts: Optional[str] def __init__( self, *, client: WebClient, channel: Optional[str] = None, recipient_team_id: Optional[str] = None, recipient_user_id: Optional[str] = None, thread_ts: Optional[str] = None, ): self.client = client self.channel = channel self.recipient_team_id = recipient_team_id self.recipient_user_id = recipient_user_id self.thread_ts = thread_ts def __call__( self, *, buffer_size: Optional[int] = None, channel: Optional[str] = None, recipient_team_id: Optional[str] = None, recipient_user_id: Optional[str] = None, thread_ts: Optional[str] = None, **kwargs, ) -> ChatStream: """Starts a new chat stream with context.""" channel = channel or self.channel thread_ts = thread_ts or self.thread_ts if channel is None: raise ValueError("say_stream without channel here is unsupported") if thread_ts is None: raise ValueError("say_stream without thread_ts here is unsupported") if buffer_size is not None: return self.client.chat_stream( buffer_size=buffer_size, channel=channel, recipient_team_id=recipient_team_id or self.recipient_team_id, recipient_user_id=recipient_user_id or self.recipient_user_id, thread_ts=thread_ts, **kwargs, ) return self.client.chat_stream( channel=channel, recipient_team_id=recipient_team_id or self.recipient_team_id, recipient_user_id=recipient_user_id or self.recipient_user_id, thread_ts=thread_ts, **kwargs, ) ``` ### Class variables `var channel : str | None` The type of the None singleton. `var client : slack_sdk.web.client.WebClient` The type of the None singleton. `var recipient_team_id : str | None` The type of the None singleton. `var recipient_user_id : str | None` The type of the None singleton. `var thread_ts : str | None` The type of the None singleton. --- Source: https://docs.slack.dev/tools/bolt-python/reference/context/set_status # Module slack_bolt.context.set_status ## Sub-modules `[slack_bolt.context.set_status.async_set_status](async_set_status.html "slack_bolt.context.set_status.async_set_status")` `[slack_bolt.context.set_status.set_status](set_status.html "slack_bolt.context.set_status.set_status")` ## Classes `class SetStatus (client: slack_sdk.web.client.WebClient, channel_id: str, thread_ts: str)` Expand source code ``` class SetStatus: client: WebClient channel_id: str thread_ts: str def __init__( self, client: WebClient, channel_id: str, thread_ts: str, ): self.client = client self.channel_id = channel_id self.thread_ts = thread_ts def __call__( self, status: str, loading_messages: Optional[List[str]] = None, **kwargs, ) -> SlackResponse: return self.client.assistant_threads_setStatus( channel_id=self.channel_id, thread_ts=self.thread_ts, status=status, loading_messages=loading_messages, **kwargs, ) ``` ### Class variables `var channel_id : str` The type of the None singleton. `var client : slack_sdk.web.client.WebClient` The type of the None singleton. `var thread_ts : str` The type of the None singleton. --- Source: https://docs.slack.dev/tools/bolt-python/reference/context/set_suggested_prompts # Module slack_bolt.context.set_suggested_prompts ## Sub-modules `[slack_bolt.context.set_suggested_prompts.async_set_suggested_prompts](async_set_suggested_prompts.html "slack_bolt.context.set_suggested_prompts.async_set_suggested_prompts")` `[slack_bolt.context.set_suggested_prompts.set_suggested_prompts](set_suggested_prompts.html "slack_bolt.context.set_suggested_prompts.set_suggested_prompts")` ## Classes `class SetSuggestedPrompts (client: slack_sdk.web.client.WebClient, channel_id: str, thread_ts: str)` Expand source code ``` class SetSuggestedPrompts: client: WebClient channel_id: str thread_ts: str def __init__( self, client: WebClient, channel_id: str, thread_ts: str, ): self.client = client self.channel_id = channel_id self.thread_ts = thread_ts def __call__( self, prompts: Sequence[Union[str, Dict[str, str]]], title: Optional[str] = None, ) -> SlackResponse: prompts_arg: List[Dict[str, str]] = [] for prompt in prompts: if isinstance(prompt, str): prompts_arg.append({"title": prompt, "message": prompt}) else: prompts_arg.append(prompt) return self.client.assistant_threads_setSuggestedPrompts( channel_id=self.channel_id, thread_ts=self.thread_ts, prompts=prompts_arg, title=title, ) ``` ### Class variables `var channel_id : str` The type of the None singleton. `var client : slack_sdk.web.client.WebClient` The type of the None singleton. `var thread_ts : str` The type of the None singleton. --- Source: https://docs.slack.dev/tools/bolt-python/reference/context/set_title # Module slack_bolt.context.set_title ## Sub-modules `[slack_bolt.context.set_title.async_set_title](async_set_title.html "slack_bolt.context.set_title.async_set_title")` `[slack_bolt.context.set_title.set_title](set_title.html "slack_bolt.context.set_title.set_title")` ## Classes `class SetTitle (client: slack_sdk.web.client.WebClient, channel_id: str, thread_ts: str)` Expand source code ``` class SetTitle: client: WebClient channel_id: str thread_ts: str def __init__( self, client: WebClient, channel_id: str, thread_ts: str, ): self.client = client self.channel_id = channel_id self.thread_ts = thread_ts def __call__(self, title: str) -> SlackResponse: return self.client.assistant_threads_setTitle( title=title, channel_id=self.channel_id, thread_ts=self.thread_ts, ) ``` ### Class variables `var channel_id : str` The type of the None singleton. `var client : slack_sdk.web.client.WebClient` The type of the None singleton. `var thread_ts : str` The type of the None singleton. --- Source: https://docs.slack.dev/tools/bolt-python/reference/error # Module slack_bolt.error Bolt specific error types. ## Classes `class BoltError (*args, **kwargs)` Expand source code ``` class BoltError(Exception): """General class in a Bolt app""" ``` General class in a Bolt app ### Ancestors * builtins.Exception * builtins.BaseException ### Subclasses * [BoltUnhandledRequestError](#slack_bolt.error.BoltUnhandledRequestError "slack_bolt.error.BoltUnhandledRequestError") `class BoltUnhandledRequestError (*, request: ForwardRef('BoltRequest') | ForwardRef('AsyncBoltRequest'), current_response: ForwardRef('BoltResponse') | None, last_global_middleware_name: str | None = None)` Expand source code ``` class BoltUnhandledRequestError(BoltError): request: "BoltRequest" # type: ignore[name-defined] body: dict current_response: Optional["BoltResponse"] # type: ignore[name-defined] last_global_middleware_name: Optional[str] def __init__( self, *, request: Union["BoltRequest", "AsyncBoltRequest"], # type: ignore[name-defined] current_response: Optional["BoltResponse"], # type: ignore[name-defined] last_global_middleware_name: Optional[str] = None, ): self.request = request self.body = request.body if request is not None else {} self.current_response = current_response self.last_global_middleware_name = last_global_middleware_name def __str__(self) -> str: return "unhandled request error" ``` General class in a Bolt app ### Ancestors * [BoltError](#slack_bolt.error.BoltError "slack_bolt.error.BoltError") * builtins.Exception * builtins.BaseException ### Class variables `var body : dict` The type of the None singleton. `var current_response : BoltResponse | None` The type of the None singleton. `var last_global_middleware_name : str | None` The type of the None singleton. `var request : BoltRequest` The type of the None singleton. --- Source: https://docs.slack.dev/tools/bolt-python/reference/kwargs_injection # Module slack_bolt.kwargs_injection For middleware/listener arguments, Bolt does flexible data injection in accordance with their names. To learn the available arguments, check `[slack_bolt.kwargs_injection.args](args.html "slack_bolt.kwargs_injection.args")`'s API document. For steps from apps, checking `[slack_bolt.workflows.step.utilities](../workflows/step/utilities/index.html "slack_bolt.workflows.step.utilities")` as well should be helpful. ## Sub-modules `[slack_bolt.kwargs_injection.args](args.html "slack_bolt.kwargs_injection.args")` `[slack_bolt.kwargs_injection.async_args](async_args.html "slack_bolt.kwargs_injection.async_args")` `[slack_bolt.kwargs_injection.async_utils](async_utils.html "slack_bolt.kwargs_injection.async_utils")` `[slack_bolt.kwargs_injection.utils](utils.html "slack_bolt.kwargs_injection.utils")` ## Functions `def build_required_kwargs(*, logger: logging.Logger, required_arg_names: MutableSequence[str], request: [BoltRequest](../request/request.html#slack_bolt.request.request.BoltRequest "slack_bolt.request.request.BoltRequest"), response: [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None, next_func: Callable[[], None] | None = None, this_func: Callable | None = None, error: Exception | None = None, next_keys_required: bool = True) ‑> Dict[str, Any]` Expand source code ``` def build_required_kwargs( *, logger: logging.Logger, required_arg_names: MutableSequence[str], request: BoltRequest, response: Optional[BoltResponse], next_func: Optional[Callable[[], None]] = None, this_func: Optional[Callable] = None, error: Optional[Exception] = None, # for error handlers next_keys_required: bool = True, # False for listeners / middleware / error handlers ) -> Dict[str, Any]: all_available_args: Dict[str, Any] = { "logger": logger, "client": request.context.client, "req": request, "request": request, "resp": response, "response": response, "context": request.context, # payload "body": request.body, "options": to_options(request.body), "shortcut": to_shortcut(request.body), "action": to_action(request.body), "view": to_view(request.body), "command": to_command(request.body), "event": to_event(request.body), "message": to_message(request.body), "step": to_step(request.body), # utilities "ack": request.context.ack, "say": request.context.say, "respond": request.context.respond, "complete": request.context.complete, "fail": request.context.fail, "set_status": request.context.set_status, "set_title": request.context.set_title, "set_suggested_prompts": request.context.set_suggested_prompts, "save_thread_context": request.context.save_thread_context, "say_stream": request.context.say_stream, # middleware "next": next_func, "next_": next_func, # for the middleware using Python's built-in `next()` function # error handler "error": error, # Exception } if not next_keys_required: all_available_args.pop("next") all_available_args.pop("next_") all_available_args["payload"] = ( all_available_args["options"] or all_available_args["shortcut"] or all_available_args["action"] or all_available_args["view"] or all_available_args["command"] or all_available_args["event"] or all_available_args["message"] or all_available_args["step"] or request.body ) for k, v in request.context.items(): if k not in all_available_args: all_available_args[k] = v if len(required_arg_names) > 0: # To support instance/class methods in a class for listeners/middleware, # check if the first argument is either self or cls first_arg_name = required_arg_names[0] if first_arg_name in {"self", "cls"}: required_arg_names.pop(0) elif first_arg_name not in all_available_args.keys() and first_arg_name != "args": if this_func is None: logger.warning(warning_skip_uncommon_arg_name(first_arg_name)) required_arg_names.pop(0) elif inspect.ismethod(this_func): # We are sure that we should skip manipulating this arg required_arg_names.pop(0) kwargs: Dict[str, Any] = {k: v for k, v in all_available_args.items() if k in required_arg_names} found_arg_names = kwargs.keys() for name in required_arg_names: if name == "args": if isinstance(request, BoltRequest): kwargs[name] = Args(**all_available_args) else: logger.warning(f"Unknown Request object type detected ({type(request)})") elif name not in found_arg_names: logger.warning(f"{name} is not a valid argument") kwargs[name] = None return kwargs ``` ## Classes `class Args (*, logger: logging.Logger, client: slack_sdk.web.client.WebClient, req: [BoltRequest](../request/request.html#slack_bolt.request.request.BoltRequest "slack_bolt.request.request.BoltRequest"), resp: [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse"), context: [BoltContext](../context/context.html#slack_bolt.context.context.BoltContext "slack_bolt.context.context.BoltContext"), body: Dict[str, Any], payload: Dict[str, Any], options: Dict[str, Any] | None = None, shortcut: Dict[str, Any] | None = None, action: Dict[str, Any] | None = None, view: Dict[str, Any] | None = None, command: Dict[str, Any] | None = None, event: Dict[str, Any] | None = None, message: Dict[str, Any] | None = None, ack: [Ack](../context/ack/ack.html#slack_bolt.context.ack.ack.Ack "slack_bolt.context.ack.ack.Ack"), say: [Say](../context/say/say.html#slack_bolt.context.say.say.Say "slack_bolt.context.say.say.Say"), respond: [Respond](../context/respond/respond.html#slack_bolt.context.respond.respond.Respond "slack_bolt.context.respond.respond.Respond"), complete: [Complete](../context/complete/complete.html#slack_bolt.context.complete.complete.Complete "slack_bolt.context.complete.complete.Complete"), fail: [Fail](../context/fail/fail.html#slack_bolt.context.fail.fail.Fail "slack_bolt.context.fail.fail.Fail"), set_status: [SetStatus](../context/set_status/set_status.html#slack_bolt.context.set_status.set_status.SetStatus "slack_bolt.context.set_status.set_status.SetStatus") | None = None, set_title: [SetTitle](../context/set_title/set_title.html#slack_bolt.context.set_title.set_title.SetTitle "slack_bolt.context.set_title.set_title.SetTitle") | None = None, set_suggested_prompts: [SetSuggestedPrompts](../context/set_suggested_prompts/set_suggested_prompts.html#slack_bolt.context.set_suggested_prompts.set_suggested_prompts.SetSuggestedPrompts "slack_bolt.context.set_suggested_prompts.set_suggested_prompts.SetSuggestedPrompts") | None = None, get_thread_context: [GetThreadContext](../context/get_thread_context/get_thread_context.html#slack_bolt.context.get_thread_context.get_thread_context.GetThreadContext "slack_bolt.context.get_thread_context.get_thread_context.GetThreadContext") | None = None, save_thread_context: [SaveThreadContext](../context/save_thread_context/save_thread_context.html#slack_bolt.context.save_thread_context.save_thread_context.SaveThreadContext "slack_bolt.context.save_thread_context.save_thread_context.SaveThreadContext") | None = None, say_stream: [SayStream](../context/say_stream/say_stream.html#slack_bolt.context.say_stream.say_stream.SayStream "slack_bolt.context.say_stream.say_stream.SayStream") | None = None, next: Callable[[], None], **kwargs)` Expand source code ``` class Args: """All the arguments in this class are available in any middleware / listeners. You can inject the named variables in the argument list in arbitrary order. @app.action("link_button") def handle_buttons(ack, respond, logger, context, body, client): logger.info(f"request body: {body}") ack() if context.channel_id is not None: respond("Hi!") client.views_open( trigger_id=body["trigger_id"], view={ ... } ) Alternatively, you can include a parameter named `args` and it will be injected with an instance of this class. @app.action("link_button") def handle_buttons(args): args.logger.info(f"request body: {args.body}") args.ack() if args.context.channel_id is not None: args.respond("Hi!") args.client.views_open( trigger_id=args.body["trigger_id"], view={ ... } ) """ client: WebClient """`slack_sdk.web.WebClient` instance with a valid token""" logger: Logger """Logger instance""" req: BoltRequest """Incoming request from Slack""" resp: BoltResponse """Response representation""" request: BoltRequest """Incoming request from Slack""" response: BoltResponse """Response representation""" context: BoltContext """Context data associated with the incoming request""" body: Dict[str, Any] """Parsed request body data""" # payload payload: Dict[str, Any] """The unwrapped core data in the request body""" options: Optional[Dict[str, Any]] # payload alias """An alias for payload in an `@app.options` listener""" shortcut: Optional[Dict[str, Any]] # payload alias """An alias for payload in an `@app.shortcut` listener""" action: Optional[Dict[str, Any]] # payload alias """An alias for payload in an `@app.action` listener""" view: Optional[Dict[str, Any]] # payload alias """An alias for payload in an `@app.view` listener""" command: Optional[Dict[str, Any]] # payload alias """An alias for payload in an `@app.command` listener""" event: Optional[Dict[str, Any]] # payload alias """An alias for payload in an `@app.event` listener""" message: Optional[Dict[str, Any]] # payload alias """An alias for payload in an `@app.message` listener""" # utilities ack: Ack """`ack()` utility function, which returns acknowledgement to the Slack servers""" say: Say """`say()` utility function, which calls `chat.postMessage` API with the associated channel ID""" respond: Respond """`respond()` utility function, which utilizes the associated `response_url`""" complete: Complete """`complete()` utility function, signals a successful completion of the custom function""" fail: Fail """`fail()` utility function, signal that the custom function failed to complete""" set_status: Optional[SetStatus] """`set_status()` utility function for AI Agents & Assistants""" set_title: Optional[SetTitle] """`set_title()` utility function for AI Agents & Assistants""" set_suggested_prompts: Optional[SetSuggestedPrompts] """`set_suggested_prompts()` utility function for AI Agents & Assistants""" get_thread_context: Optional[GetThreadContext] """`get_thread_context()` utility function for AI Agents & Assistants""" save_thread_context: Optional[SaveThreadContext] """`save_thread_context()` utility function for AI Agents & Assistants""" say_stream: Optional[SayStream] """`say_stream()` utility function for conversations, AI Agents & Assistants""" # middleware next: Callable[[], None] """`next()` utility function, which tells the middleware chain that it can continue with the next one""" next_: Callable[[], None] """An alias of `next()` for avoiding the Python built-in method overrides in middleware functions""" def __init__( self, *, logger: logging.Logger, client: WebClient, req: BoltRequest, resp: BoltResponse, context: BoltContext, body: Dict[str, Any], payload: Dict[str, Any], options: Optional[Dict[str, Any]] = None, shortcut: Optional[Dict[str, Any]] = None, action: Optional[Dict[str, Any]] = None, view: Optional[Dict[str, Any]] = None, command: Optional[Dict[str, Any]] = None, event: Optional[Dict[str, Any]] = None, message: Optional[Dict[str, Any]] = None, ack: Ack, say: Say, respond: Respond, complete: Complete, fail: Fail, set_status: Optional[SetStatus] = None, set_title: Optional[SetTitle] = None, set_suggested_prompts: Optional[SetSuggestedPrompts] = None, get_thread_context: Optional[GetThreadContext] = None, save_thread_context: Optional[SaveThreadContext] = None, say_stream: Optional[SayStream] = None, # As this method is not supposed to be invoked by bolt-python users, # the naming conflict with the built-in one affects # only the internals of this method next: Callable[[], None], **kwargs, # noqa ): self.logger: logging.Logger = logger self.client: WebClient = client self.request = self.req = req self.response = self.resp = resp self.context: BoltContext = context self.body: Dict[str, Any] = body self.payload: Dict[str, Any] = payload self.options: Optional[Dict[str, Any]] = options self.shortcut: Optional[Dict[str, Any]] = shortcut self.action: Optional[Dict[str, Any]] = action self.view: Optional[Dict[str, Any]] = view self.command: Optional[Dict[str, Any]] = command self.event: Optional[Dict[str, Any]] = event self.message: Optional[Dict[str, Any]] = message self.ack: Ack = ack self.say: Say = say self.respond: Respond = respond self.complete: Complete = complete self.fail: Fail = fail self.set_status = set_status self.set_title = set_title self.set_suggested_prompts = set_suggested_prompts self.get_thread_context = get_thread_context self.save_thread_context = save_thread_context self.say_stream = say_stream self.next: Callable[[], None] = next self.next_: Callable[[], None] = next ``` All the arguments in this class are available in any middleware / listeners. You can inject the named variables in the argument list in arbitrary order. ``` @app.action("link_button") def handle_buttons(ack, respond, logger, context, body, client): logger.info(f"request body: {body}") ack() if context.channel_id is not None: respond("Hi!") client.views_open( trigger_id=body["trigger_id"], view={ ... } ) ``` Alternatively, you can include a parameter named `[slack_bolt.kwargs_injection.args](args.html "slack_bolt.kwargs_injection.args")` and it will be injected with an instance of this class. ``` @app.action("link_button") def handle_buttons(args): args.logger.info(f"request body: {args.body}") args.ack() if args.context.channel_id is not None: args.respond("Hi!") args.client.views_open( trigger_id=args.body["trigger_id"], view={ ... } ) ``` ### Class variables `var ack : [Ack](../context/ack/ack.html#slack_bolt.context.ack.ack.Ack "slack_bolt.context.ack.ack.Ack")` `ack()` utility function, which returns acknowledgement to the Slack servers `var action : Dict[str, Any] | None` An alias for payload in an `@app.action` listener `var body : Dict[str, Any]` Parsed request body data `var client : slack_sdk.web.client.WebClient` `slack_sdk.web.WebClient` instance with a valid token `var command : Dict[str, Any] | None` An alias for payload in an `@app.command` listener `var complete : [Complete](../context/complete/complete.html#slack_bolt.context.complete.complete.Complete "slack_bolt.context.complete.complete.Complete")` `complete()` utility function, signals a successful completion of the custom function `var context : [BoltContext](../context/context.html#slack_bolt.context.context.BoltContext "slack_bolt.context.context.BoltContext")` Context data associated with the incoming request `var event : Dict[str, Any] | None` An alias for payload in an `@app.event` listener `var fail : [Fail](../context/fail/fail.html#slack_bolt.context.fail.fail.Fail "slack_bolt.context.fail.fail.Fail")` `fail()` utility function, signal that the custom function failed to complete `var get_thread_context : [GetThreadContext](../context/get_thread_context/get_thread_context.html#slack_bolt.context.get_thread_context.get_thread_context.GetThreadContext "slack_bolt.context.get_thread_context.get_thread_context.GetThreadContext") | None` `get_thread_context()` utility function for AI Agents & Assistants `var logger : logging.Logger` Logger instance `var message : Dict[str, Any] | None` An alias for payload in an `@app.message` listener `var next : Callable[[], None]` `next()` utility function, which tells the middleware chain that it can continue with the next one `var next_ : Callable[[], None]` An alias of `next()` for avoiding the Python built-in method overrides in middleware functions `var options : Dict[str, Any] | None` An alias for payload in an `@app.options` listener `var payload : Dict[str, Any]` The unwrapped core data in the request body `var req : [BoltRequest](../request/request.html#slack_bolt.request.request.BoltRequest "slack_bolt.request.request.BoltRequest")` Incoming request from Slack `var request : [BoltRequest](../request/request.html#slack_bolt.request.request.BoltRequest "slack_bolt.request.request.BoltRequest")` Incoming request from Slack `var resp : [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse")` Response representation `var respond : [Respond](../context/respond/respond.html#slack_bolt.context.respond.respond.Respond "slack_bolt.context.respond.respond.Respond")` `respond()` utility function, which utilizes the associated `response_url` `var response : [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse")` Response representation `var save_thread_context : [SaveThreadContext](../context/save_thread_context/save_thread_context.html#slack_bolt.context.save_thread_context.save_thread_context.SaveThreadContext "slack_bolt.context.save_thread_context.save_thread_context.SaveThreadContext") | None` `save_thread_context()` utility function for AI Agents & Assistants `var say : [Say](../context/say/say.html#slack_bolt.context.say.say.Say "slack_bolt.context.say.say.Say")` `say()` utility function, which calls `chat.postMessage` API with the associated channel ID `var say_stream : [SayStream](../context/say_stream/say_stream.html#slack_bolt.context.say_stream.say_stream.SayStream "slack_bolt.context.say_stream.say_stream.SayStream") | None` `say_stream()` utility function for conversations, AI Agents & Assistants `var set_status : [SetStatus](../context/set_status/set_status.html#slack_bolt.context.set_status.set_status.SetStatus "slack_bolt.context.set_status.set_status.SetStatus") | None` `set_status()` utility function for AI Agents & Assistants `var set_suggested_prompts : [SetSuggestedPrompts](../context/set_suggested_prompts/set_suggested_prompts.html#slack_bolt.context.set_suggested_prompts.set_suggested_prompts.SetSuggestedPrompts "slack_bolt.context.set_suggested_prompts.set_suggested_prompts.SetSuggestedPrompts") | None` `set_suggested_prompts()` utility function for AI Agents & Assistants `var set_title : [SetTitle](../context/set_title/set_title.html#slack_bolt.context.set_title.set_title.SetTitle "slack_bolt.context.set_title.set_title.SetTitle") | None` `set_title()` utility function for AI Agents & Assistants `var shortcut : Dict[str, Any] | None` An alias for payload in an `@app.shortcut` listener `var view : Dict[str, Any] | None` An alias for payload in an `@app.view` listener --- Source: https://docs.slack.dev/tools/bolt-python/reference/lazy_listener # Module slack_bolt.lazy_listener Lazy listener runner is a beta feature for the apps running on Function-as-a-Service platforms. ``` def respond_to_slack_within_3_seconds(body, ack): text = body.get("text") if text is None or len(text) == 0: ack(f":x: Usage: /start-process (description here)") else: ack(f"Accepted! (task: {body['text']})") import time def run_long_process(respond, body): time.sleep(5) # longer than 3 seconds respond(f"Completed! (task: {body['text']})") app.command("/start-process")( # ack() is still called within 3 seconds ack=respond_to_slack_within_3_seconds, # Lazy function is responsible for processing the event lazy=[run_long_process] ) ``` Refer to [https://docs.slack.dev/tools/bolt-python/concepts/lazy-listeners](https://docs.slack.dev/tools/bolt-python/concepts/lazy-listeners) for more details. ## Sub-modules `[slack_bolt.lazy_listener.async_internals](async_internals.html "slack_bolt.lazy_listener.async_internals")` `[slack_bolt.lazy_listener.async_runner](async_runner.html "slack_bolt.lazy_listener.async_runner")` `[slack_bolt.lazy_listener.asyncio_runner](asyncio_runner.html "slack_bolt.lazy_listener.asyncio_runner")` `[slack_bolt.lazy_listener.internals](internals.html "slack_bolt.lazy_listener.internals")` `[slack_bolt.lazy_listener.runner](runner.html "slack_bolt.lazy_listener.runner")` `[slack_bolt.lazy_listener.thread_runner](thread_runner.html "slack_bolt.lazy_listener.thread_runner")` ## Classes `class LazyListenerRunner` Expand source code ``` class LazyListenerRunner(metaclass=ABCMeta): logger: Logger @abstractmethod def start(self, function: Callable[..., None], request: BoltRequest) -> None: """Starts a new lazy listener execution. Args: function: The function to run. request: The request to pass to the function. The object must be thread-safe. """ raise NotImplementedError() def run(self, function: Callable[..., None], request: BoltRequest) -> None: """Synchronously runs the function with a given request data. Args: function: The function to run. request: The request to pass to the function. The object must be thread-safe. """ build_runnable_function( func=function, logger=self.logger, request=request, )() ``` ### Subclasses * [ChaliceLazyListenerRunner](../adapter/aws_lambda/chalice_lazy_listener_runner.html#slack_bolt.adapter.aws_lambda.chalice_lazy_listener_runner.ChaliceLazyListenerRunner "slack_bolt.adapter.aws_lambda.chalice_lazy_listener_runner.ChaliceLazyListenerRunner") * [LambdaLazyListenerRunner](../adapter/aws_lambda/lazy_listener_runner.html#slack_bolt.adapter.aws_lambda.lazy_listener_runner.LambdaLazyListenerRunner "slack_bolt.adapter.aws_lambda.lazy_listener_runner.LambdaLazyListenerRunner") * [NoopLazyListenerRunner](../adapter/google_cloud_functions/handler.html#slack_bolt.adapter.google_cloud_functions.handler.NoopLazyListenerRunner "slack_bolt.adapter.google_cloud_functions.handler.NoopLazyListenerRunner") * [ThreadLazyListenerRunner](thread_runner.html#slack_bolt.lazy_listener.thread_runner.ThreadLazyListenerRunner "slack_bolt.lazy_listener.thread_runner.ThreadLazyListenerRunner") ### Class variables `var logger : logging.Logger` The type of the None singleton. ### Methods `def run(self, function: Callable[..., None], request: [BoltRequest](../request/request.html#slack_bolt.request.request.BoltRequest "slack_bolt.request.request.BoltRequest")) ‑> None` Expand source code ``` def run(self, function: Callable[..., None], request: BoltRequest) -> None: """Synchronously runs the function with a given request data. Args: function: The function to run. request: The request to pass to the function. The object must be thread-safe. """ build_runnable_function( func=function, logger=self.logger, request=request, )() ``` Synchronously runs the function with a given request data. ## Args **`function`** The function to run. **`request`** The request to pass to the function. The object must be thread-safe. `def start(self, function: Callable[..., None], request: [BoltRequest](../request/request.html#slack_bolt.request.request.BoltRequest "slack_bolt.request.request.BoltRequest")) ‑> None` Expand source code ``` @abstractmethod def start(self, function: Callable[..., None], request: BoltRequest) -> None: """Starts a new lazy listener execution. Args: function: The function to run. request: The request to pass to the function. The object must be thread-safe. """ raise NotImplementedError() ``` Starts a new lazy listener execution. ## Args **`function`** The function to run. **`request`** The request to pass to the function. The object must be thread-safe. `class ThreadLazyListenerRunner (logger: logging.Logger, executor: concurrent.futures._base.Executor)` Expand source code ``` class ThreadLazyListenerRunner(LazyListenerRunner): logger: Logger def __init__( self, logger: Logger, executor: Executor, ): self.logger = logger self.executor = executor def start(self, function: Callable[..., None], request: BoltRequest) -> None: self.executor.submit( build_runnable_function( func=function, logger=self.logger, request=request, ) ) ``` ### Ancestors * [LazyListenerRunner](runner.html#slack_bolt.lazy_listener.runner.LazyListenerRunner "slack_bolt.lazy_listener.runner.LazyListenerRunner") ### Subclasses * [DjangoThreadLazyListenerRunner](../adapter/django/handler.html#slack_bolt.adapter.django.handler.DjangoThreadLazyListenerRunner "slack_bolt.adapter.django.handler.DjangoThreadLazyListenerRunner") ### Inherited members * `**[LazyListenerRunner](runner.html#slack_bolt.lazy_listener.runner.LazyListenerRunner "slack_bolt.lazy_listener.runner.LazyListenerRunner")**`: * `[logger](runner.html#slack_bolt.lazy_listener.runner.LazyListenerRunner.logger "slack_bolt.lazy_listener.runner.LazyListenerRunner.logger")` * `[run](runner.html#slack_bolt.lazy_listener.runner.LazyListenerRunner.run "slack_bolt.lazy_listener.runner.LazyListenerRunner.run")` * `[start](runner.html#slack_bolt.lazy_listener.runner.LazyListenerRunner.start "slack_bolt.lazy_listener.runner.LazyListenerRunner.start")` --- Source: https://docs.slack.dev/tools/bolt-python/reference/listener # Module slack_bolt.listener Listeners process an incoming request from Slack if the request's type or data structure matches the predefined conditions of the listener. Typically, a listener acknowledge requests from Slack, process the request data, and may send response back to Slack. ## Sub-modules `[slack_bolt.listener.async_builtins](async_builtins.html "slack_bolt.listener.async_builtins")` `[slack_bolt.listener.async_listener](async_listener.html "slack_bolt.listener.async_listener")` `[slack_bolt.listener.async_listener_completion_handler](async_listener_completion_handler.html "slack_bolt.listener.async_listener_completion_handler")` `[slack_bolt.listener.async_listener_error_handler](async_listener_error_handler.html "slack_bolt.listener.async_listener_error_handler")` `[slack_bolt.listener.async_listener_start_handler](async_listener_start_handler.html "slack_bolt.listener.async_listener_start_handler")` `[slack_bolt.listener.asyncio_runner](asyncio_runner.html "slack_bolt.listener.asyncio_runner")` `[slack_bolt.listener.builtins](builtins.html "slack_bolt.listener.builtins")` `[slack_bolt.listener.custom_listener](custom_listener.html "slack_bolt.listener.custom_listener")` `[slack_bolt.listener.listener](listener.html "slack_bolt.listener.listener")` `[slack_bolt.listener.listener_completion_handler](listener_completion_handler.html "slack_bolt.listener.listener_completion_handler")` `[slack_bolt.listener.listener_error_handler](listener_error_handler.html "slack_bolt.listener.listener_error_handler")` `[slack_bolt.listener.listener_start_handler](listener_start_handler.html "slack_bolt.listener.listener_start_handler")` `[slack_bolt.listener.thread_runner](thread_runner.html "slack_bolt.listener.thread_runner")` ## Classes `class CustomListener (*, app_name: str, ack_function: Callable[..., [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None], lazy_functions: Sequence[Callable[..., None]], matchers: Sequence[[ListenerMatcher](../listener_matcher/listener_matcher.html#slack_bolt.listener_matcher.listener_matcher.ListenerMatcher "slack_bolt.listener_matcher.listener_matcher.ListenerMatcher")], middleware: Sequence[[Middleware](../middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")], auto_acknowledgement: bool = False, ack_timeout: int = 3, base_logger: logging.Logger | None = None)` Expand source code ``` class CustomListener(Listener): app_name: str ack_function: Callable[..., Optional[BoltResponse]] # type: ignore[assignment] lazy_functions: Sequence[Callable[..., None]] matchers: Sequence[ListenerMatcher] middleware: Sequence[Middleware] auto_acknowledgement: bool ack_timeout: int = 3 arg_names: MutableSequence[str] logger: Logger def __init__( self, *, app_name: str, ack_function: Callable[..., Optional[BoltResponse]], lazy_functions: Sequence[Callable[..., None]], matchers: Sequence[ListenerMatcher], middleware: Sequence[Middleware], auto_acknowledgement: bool = False, ack_timeout: int = 3, base_logger: Optional[Logger] = None, ): self.app_name = app_name self.ack_function = ack_function self.lazy_functions = lazy_functions self.matchers = matchers self.middleware = middleware self.auto_acknowledgement = auto_acknowledgement self.ack_timeout = ack_timeout self.arg_names = get_arg_names_of_callable(ack_function) self.logger = get_bolt_app_logger(app_name, self.ack_function, base_logger) def run_ack_function( self, *, request: BoltRequest, response: BoltResponse, ) -> Optional[BoltResponse]: return self.ack_function( **build_required_kwargs( logger=self.logger, required_arg_names=self.arg_names, request=request, response=response, this_func=self.ack_function, ) ) ``` ### Ancestors * [Listener](listener.html#slack_bolt.listener.listener.Listener "slack_bolt.listener.listener.Listener") ### Class variables `var app_name : str` The type of the None singleton. `var arg_names : MutableSequence[str]` The type of the None singleton. `var logger : logging.Logger` The type of the None singleton. ### Inherited members * `**[Listener](listener.html#slack_bolt.listener.listener.Listener "slack_bolt.listener.listener.Listener")**`: * `[ack_function](listener.html#slack_bolt.listener.listener.Listener.ack_function "slack_bolt.listener.listener.Listener.ack_function")` * `[ack_timeout](listener.html#slack_bolt.listener.listener.Listener.ack_timeout "slack_bolt.listener.listener.Listener.ack_timeout")` * `[auto_acknowledgement](listener.html#slack_bolt.listener.listener.Listener.auto_acknowledgement "slack_bolt.listener.listener.Listener.auto_acknowledgement")` * `[lazy_functions](listener.html#slack_bolt.listener.listener.Listener.lazy_functions "slack_bolt.listener.listener.Listener.lazy_functions")` * `[matchers](listener.html#slack_bolt.listener.listener.Listener.matchers "slack_bolt.listener.listener.Listener.matchers")` * `[middleware](listener.html#slack_bolt.listener.listener.Listener.middleware "slack_bolt.listener.listener.Listener.middleware")` * `[run_ack_function](listener.html#slack_bolt.listener.listener.Listener.run_ack_function "slack_bolt.listener.listener.Listener.run_ack_function")` * `[run_middleware](listener.html#slack_bolt.listener.listener.Listener.run_middleware "slack_bolt.listener.listener.Listener.run_middleware")` `class Listener` Expand source code ``` class Listener(metaclass=ABCMeta): matchers: Sequence[ListenerMatcher] middleware: Sequence[Middleware] ack_function: Callable[..., BoltResponse] lazy_functions: Sequence[Callable[..., None]] auto_acknowledgement: bool ack_timeout: int = 3 def matches( self, *, req: BoltRequest, resp: BoltResponse, ) -> bool: is_matched: bool = False for matcher in self.matchers: is_matched = matcher.matches(req, resp) if not is_matched: return is_matched return is_matched def run_middleware( self, *, req: BoltRequest, resp: BoltResponse, ) -> Tuple[Optional[BoltResponse], bool]: """Runs a middleware. Args: req: The incoming request resp: The current response Returns: A tuple of the processed response and a flag indicating termination """ for m in self.middleware: middleware_state = {"next_called": False} def next_(): middleware_state["next_called"] = True resp = m.process(req=req, resp=resp, next=next_) # type: ignore[assignment] if not middleware_state["next_called"]: # next() was not called in this middleware return (resp, True) return (resp, False) @abstractmethod def run_ack_function(self, *, request: BoltRequest, response: BoltResponse) -> Optional[BoltResponse]: """Runs all the registered middleware and then run the listener function. Args: request: The incoming request response: The current response Returns: The processed response """ raise NotImplementedError() ``` ### Subclasses * [CustomListener](custom_listener.html#slack_bolt.listener.custom_listener.CustomListener "slack_bolt.listener.custom_listener.CustomListener") ### Class variables `var ack_function : Callable[..., [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse")]` The type of the None singleton. `var ack_timeout : int` The type of the None singleton. `var auto_acknowledgement : bool` The type of the None singleton. `var lazy_functions : Sequence[Callable[..., None]]` The type of the None singleton. `var matchers : Sequence[[ListenerMatcher](../listener_matcher/listener_matcher.html#slack_bolt.listener_matcher.listener_matcher.ListenerMatcher "slack_bolt.listener_matcher.listener_matcher.ListenerMatcher")]` The type of the None singleton. `var middleware : Sequence[[Middleware](../middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")]` The type of the None singleton. ### Methods `def matches(self, *, req: [BoltRequest](../request/request.html#slack_bolt.request.request.BoltRequest "slack_bolt.request.request.BoltRequest"), resp: [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse")) ‑> bool` Expand source code ``` def matches( self, *, req: BoltRequest, resp: BoltResponse, ) -> bool: is_matched: bool = False for matcher in self.matchers: is_matched = matcher.matches(req, resp) if not is_matched: return is_matched return is_matched ``` `def run_ack_function(self, *, request: [BoltRequest](../request/request.html#slack_bolt.request.request.BoltRequest "slack_bolt.request.request.BoltRequest"), response: [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse")) ‑> [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None` Expand source code ``` @abstractmethod def run_ack_function(self, *, request: BoltRequest, response: BoltResponse) -> Optional[BoltResponse]: """Runs all the registered middleware and then run the listener function. Args: request: The incoming request response: The current response Returns: The processed response """ raise NotImplementedError() ``` Runs all the registered middleware and then run the listener function. ## Args **`request`** The incoming request **`response`** The current response ## Returns The processed response `def run_middleware(self, *, req: [BoltRequest](../request/request.html#slack_bolt.request.request.BoltRequest "slack_bolt.request.request.BoltRequest"), resp: [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse")) ‑> Tuple[[BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None, bool]` Expand source code ``` def run_middleware( self, *, req: BoltRequest, resp: BoltResponse, ) -> Tuple[Optional[BoltResponse], bool]: """Runs a middleware. Args: req: The incoming request resp: The current response Returns: A tuple of the processed response and a flag indicating termination """ for m in self.middleware: middleware_state = {"next_called": False} def next_(): middleware_state["next_called"] = True resp = m.process(req=req, resp=resp, next=next_) # type: ignore[assignment] if not middleware_state["next_called"]: # next() was not called in this middleware return (resp, True) return (resp, False) ``` Runs a middleware. ## Args **`req`** The incoming request **`resp`** The current response ## Returns A tuple of the processed response and a flag indicating termination --- Source: https://docs.slack.dev/tools/bolt-python/reference/listener_matcher # Module slack_bolt.listener_matcher A listener matcher is a simplified version of listener middleware. A listener matcher function returns bool value instead of `next()` method invocation inside. This interface enables developers to utilize simple predicate functions for additional listener conditions. ## Sub-modules `[slack_bolt.listener_matcher.async_builtins](async_builtins.html "slack_bolt.listener_matcher.async_builtins")` `[slack_bolt.listener_matcher.async_listener_matcher](async_listener_matcher.html "slack_bolt.listener_matcher.async_listener_matcher")` `[slack_bolt.listener_matcher.builtins](builtins.html "slack_bolt.listener_matcher.builtins")` `[slack_bolt.listener_matcher.custom_listener_matcher](custom_listener_matcher.html "slack_bolt.listener_matcher.custom_listener_matcher")` `[slack_bolt.listener_matcher.listener_matcher](listener_matcher.html "slack_bolt.listener_matcher.listener_matcher")` ## Classes `class CustomListenerMatcher (*, app_name: str, func: Callable[..., bool], base_logger: logging.Logger | None = None)` Expand source code ``` class CustomListenerMatcher(ListenerMatcher): app_name: str func: Callable[..., bool] arg_names: MutableSequence[str] logger: Logger def __init__(self, *, app_name: str, func: Callable[..., bool], base_logger: Optional[Logger] = None): self.app_name = app_name self.func = func self.arg_names = get_arg_names_of_callable(func) self.logger = get_bolt_app_logger(self.app_name, self.func, base_logger) def matches(self, req: BoltRequest, resp: BoltResponse) -> bool: return self.func( **build_required_kwargs( logger=self.logger, required_arg_names=self.arg_names, request=req, response=resp, this_func=self.func, ) ) ``` ### Ancestors * [ListenerMatcher](listener_matcher.html#slack_bolt.listener_matcher.listener_matcher.ListenerMatcher "slack_bolt.listener_matcher.listener_matcher.ListenerMatcher") ### Class variables `var app_name : str` The type of the None singleton. `var arg_names : MutableSequence[str]` The type of the None singleton. `var func : Callable[..., bool]` The type of the None singleton. `var logger : logging.Logger` The type of the None singleton. ### Inherited members * `**[ListenerMatcher](listener_matcher.html#slack_bolt.listener_matcher.listener_matcher.ListenerMatcher "slack_bolt.listener_matcher.listener_matcher.ListenerMatcher")**`: * `[matches](listener_matcher.html#slack_bolt.listener_matcher.listener_matcher.ListenerMatcher.matches "slack_bolt.listener_matcher.listener_matcher.ListenerMatcher.matches")` `class ListenerMatcher` Expand source code ``` class ListenerMatcher(metaclass=ABCMeta): @abstractmethod def matches(self, req: BoltRequest, resp: BoltResponse) -> bool: """Matches against the request and returns True if matched. Args: req: The request resp: The response Returns: True if matched. """ raise NotImplementedError() ``` ### Subclasses * [BuiltinListenerMatcher](builtins.html#slack_bolt.listener_matcher.builtins.BuiltinListenerMatcher "slack_bolt.listener_matcher.builtins.BuiltinListenerMatcher") * [CustomListenerMatcher](custom_listener_matcher.html#slack_bolt.listener_matcher.custom_listener_matcher.CustomListenerMatcher "slack_bolt.listener_matcher.custom_listener_matcher.CustomListenerMatcher") ### Methods `def matches(self, req: [BoltRequest](../request/request.html#slack_bolt.request.request.BoltRequest "slack_bolt.request.request.BoltRequest"), resp: [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse")) ‑> bool` Expand source code ``` @abstractmethod def matches(self, req: BoltRequest, resp: BoltResponse) -> bool: """Matches against the request and returns True if matched. Args: req: The request resp: The response Returns: True if matched. """ raise NotImplementedError() ``` Matches against the request and returns True if matched. ## Args **`req`** The request **`resp`** The response ## Returns True if matched. --- Source: https://docs.slack.dev/tools/bolt-python/reference/logger # Module slack_bolt.logger Bolt for Python relies on the standard `logging` module. ## Sub-modules `[slack_bolt.logger.messages](messages.html "slack_bolt.logger.messages")` ## Functions `def get_bolt_app_logger(app_name: str, cls: object = None, base_logger: logging.Logger | None = None) ‑> logging.Logger` Expand source code ``` def get_bolt_app_logger(app_name: str, cls: object = None, base_logger: Optional[Logger] = None) -> Logger: logger: Logger = ( logging.getLogger(f"{app_name}:{cls.__name__}") if cls and hasattr(cls, "__name__") else logging.getLogger(app_name) ) if base_logger is not None: _configure_from_base_logger(logger, base_logger) else: _configure_from_root(logger) return logger ``` `def get_bolt_logger(cls: Any, base_logger: logging.Logger | None = None) ‑> logging.Logger` Expand source code ``` def get_bolt_logger(cls: Any, base_logger: Optional[Logger] = None) -> Logger: logger = logging.getLogger(f"slack_bolt.{cls.__name__}") if base_logger is not None: _configure_from_base_logger(logger, base_logger) else: _configure_from_root(logger) return logger ``` --- Source: https://docs.slack.dev/tools/bolt-python/reference/middleware # Module slack_bolt.middleware A middleware processes request data and calls `next()` method if the execution chain should continue running the following middleware. Middleware can be used globally before all listener executions. It's also possible to run a middleware only for a particular listener. ## Sub-modules `[slack_bolt.middleware.assistant](assistant/index.html "slack_bolt.middleware.assistant")` `[slack_bolt.middleware.async_builtins](async_builtins.html "slack_bolt.middleware.async_builtins")` `[slack_bolt.middleware.async_custom_middleware](async_custom_middleware.html "slack_bolt.middleware.async_custom_middleware")` `[slack_bolt.middleware.async_middleware](async_middleware.html "slack_bolt.middleware.async_middleware")` `[slack_bolt.middleware.async_middleware_error_handler](async_middleware_error_handler.html "slack_bolt.middleware.async_middleware_error_handler")` `[slack_bolt.middleware.attaching_conversation_kwargs](attaching_conversation_kwargs/index.html "slack_bolt.middleware.attaching_conversation_kwargs")` `[slack_bolt.middleware.attaching_function_token](attaching_function_token/index.html "slack_bolt.middleware.attaching_function_token")` `[slack_bolt.middleware.authorization](authorization/index.html "slack_bolt.middleware.authorization")` `[slack_bolt.middleware.custom_middleware](custom_middleware.html "slack_bolt.middleware.custom_middleware")` `[slack_bolt.middleware.ignoring_self_events](ignoring_self_events/index.html "slack_bolt.middleware.ignoring_self_events")` `[slack_bolt.middleware.message_listener_matches](message_listener_matches/index.html "slack_bolt.middleware.message_listener_matches")` `[slack_bolt.middleware.middleware](middleware.html "slack_bolt.middleware.middleware")` `[slack_bolt.middleware.middleware_error_handler](middleware_error_handler.html "slack_bolt.middleware.middleware_error_handler")` `[slack_bolt.middleware.request_verification](request_verification/index.html "slack_bolt.middleware.request_verification")` `[slack_bolt.middleware.ssl_check](ssl_check/index.html "slack_bolt.middleware.ssl_check")` `[slack_bolt.middleware.url_verification](url_verification/index.html "slack_bolt.middleware.url_verification")` ## Classes `class AttachingConversationKwargs (thread_context_store: [AssistantThreadContextStore](../context/assistant/thread_context_store/store.html#slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore "slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore") | None = None)` Expand source code ``` class AttachingConversationKwargs(Middleware): thread_context_store: Optional[AssistantThreadContextStore] def __init__(self, thread_context_store: Optional[AssistantThreadContextStore] = None): self.thread_context_store = thread_context_store def process(self, *, req: BoltRequest, resp: BoltResponse, next: Callable[[], BoltResponse]) -> Optional[BoltResponse]: event = to_event(req.body) if event is not None: if is_assistant_event(req.body): assistant = AssistantUtilities( payload=event, context=req.context, thread_context_store=self.thread_context_store, ) req.context["say"] = assistant.say req.context["set_title"] = assistant.set_title req.context["set_suggested_prompts"] = assistant.set_suggested_prompts req.context["get_thread_context"] = assistant.get_thread_context req.context["save_thread_context"] = assistant.save_thread_context # TODO: in the future we might want to introduce a "proper" extract_ts utility thread_ts = req.context.thread_ts or event.get("ts") if req.context.channel_id and thread_ts: req.context["set_status"] = SetStatus( client=req.context.client, channel_id=req.context.channel_id, thread_ts=thread_ts, ) req.context["say_stream"] = SayStream( client=req.context.client, channel=req.context.channel_id, recipient_team_id=req.context.team_id or req.context.enterprise_id, recipient_user_id=req.context.user_id, thread_ts=thread_ts, ) return next() ``` A middleware can process request data before other middleware and listener functions. ### Ancestors * [Middleware](middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware") ### Class variables `var thread_context_store : [AssistantThreadContextStore](../context/assistant/thread_context_store/store.html#slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore "slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore") | None` The type of the None singleton. ### Inherited members * `**[Middleware](middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")**`: * `[name](middleware.html#slack_bolt.middleware.middleware.Middleware.name "slack_bolt.middleware.middleware.Middleware.name")` * `[process](middleware.html#slack_bolt.middleware.middleware.Middleware.process "slack_bolt.middleware.middleware.Middleware.process")` `class AttachingFunctionToken` Expand source code ``` class AttachingFunctionToken(Middleware): def process( self, *, req: BoltRequest, resp: BoltResponse, # This method is not supposed to be invoked by bolt-python users next: Callable[[], BoltResponse], ) -> BoltResponse: if req.context.function_bot_access_token is not None: req.context.client.token = req.context.function_bot_access_token return next() ``` A middleware can process request data before other middleware and listener functions. ### Ancestors * [Middleware](middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware") ### Inherited members * `**[Middleware](middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")**`: * `[name](middleware.html#slack_bolt.middleware.middleware.Middleware.name "slack_bolt.middleware.middleware.Middleware.name")` * `[process](middleware.html#slack_bolt.middleware.middleware.Middleware.process "slack_bolt.middleware.middleware.Middleware.process")` `class CustomMiddleware (*, app_name: str, func: Callable, base_logger: logging.Logger | None = None)` Expand source code ``` class CustomMiddleware(Middleware): app_name: str func: Callable[..., Any] arg_names: MutableSequence[str] logger: Logger def __init__(self, *, app_name: str, func: Callable, base_logger: Optional[Logger] = None): self.app_name = app_name self.func = func self.arg_names = get_arg_names_of_callable(func) self.logger = get_bolt_app_logger(self.app_name, self.func, base_logger) def process( self, *, req: BoltRequest, resp: BoltResponse, # As this method is not supposed to be invoked by bolt-python users, # the naming conflict with the built-in one affects # only the internals of this method next: Callable[[], BoltResponse], ) -> BoltResponse: return self.func( **build_required_kwargs( logger=self.logger, required_arg_names=self.arg_names, request=req, response=resp, next_func=next, # type: ignore[arg-type] this_func=self.func, ) ) @property def name(self) -> str: return f"CustomMiddleware(func={get_name_for_callable(self.func)})" ``` A middleware can process request data before other middleware and listener functions. ### Ancestors * [Middleware](middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware") ### Class variables `var app_name : str` The type of the None singleton. `var arg_names : MutableSequence[str]` The type of the None singleton. `var func : Callable[..., Any]` The type of the None singleton. `var logger : logging.Logger` The type of the None singleton. ### Inherited members * `**[Middleware](middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")**`: * `[name](middleware.html#slack_bolt.middleware.middleware.Middleware.name "slack_bolt.middleware.middleware.Middleware.name")` * `[process](middleware.html#slack_bolt.middleware.middleware.Middleware.process "slack_bolt.middleware.middleware.Middleware.process")` `class IgnoringSelfEvents (base_logger: logging.Logger | None = None, ignoring_self_assistant_message_events_enabled: bool = True)` Expand source code ``` class IgnoringSelfEvents(Middleware): def __init__( self, base_logger: Optional[logging.Logger] = None, ignoring_self_assistant_message_events_enabled: bool = True, ): """Ignores the events generated by this bot user itself.""" self.logger = get_bolt_logger(IgnoringSelfEvents, base_logger=base_logger) self.ignoring_self_assistant_message_events_enabled = ignoring_self_assistant_message_events_enabled def process( self, *, req: BoltRequest, resp: BoltResponse, next: Callable[[], BoltResponse], ) -> BoltResponse: auth_result = req.context.authorize_result # message events can have $.event.bot_id while it does not have its user_id bot_id = req.body.get("event", {}).get("bot_id") if self._is_self_event(auth_result, req.context.user_id, bot_id, req.body): # type: ignore[arg-type] if self.ignoring_self_assistant_message_events_enabled is False: if is_bot_message_event_in_assistant_thread(req.body): # Assistant#bot_message handler acknowledges this pattern return next() self._debug_log(req.body) return req.context.ack() else: return next() # ----------------------------------------- # It's an Events API event that isn't of type message, # but the user ID might match our own app. Filter these out. # However, some events still must be fired, because they can make sense. events_that_should_be_kept = ["member_joined_channel", "member_left_channel"] @classmethod def _is_self_event( cls, auth_result: AuthorizeResult, user_id: Optional[str], bot_id: Optional[str], body: Dict[str, Any], ): return ( auth_result is not None and ( (user_id is not None and user_id == auth_result.bot_user_id) or (bot_id is not None and bot_id == auth_result.bot_id) # for bot_message events ) and body.get("event") is not None and body.get("event", {}).get("type") not in cls.events_that_should_be_kept ) def _debug_log(self, body: dict): if self.logger.level <= logging.DEBUG: event = body.get("event") self.logger.debug(f"Skipped self event: {event}") ``` A middleware can process request data before other middleware and listener functions. Ignores the events generated by this bot user itself. ### Ancestors * [Middleware](middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware") ### Subclasses * [AsyncIgnoringSelfEvents](ignoring_self_events/async_ignoring_self_events.html#slack_bolt.middleware.ignoring_self_events.async_ignoring_self_events.AsyncIgnoringSelfEvents "slack_bolt.middleware.ignoring_self_events.async_ignoring_self_events.AsyncIgnoringSelfEvents") ### Class variables `var events_that_should_be_kept` The type of the None singleton. ### Inherited members * `**[Middleware](middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")**`: * `[name](middleware.html#slack_bolt.middleware.middleware.Middleware.name "slack_bolt.middleware.middleware.Middleware.name")` * `[process](middleware.html#slack_bolt.middleware.middleware.Middleware.process "slack_bolt.middleware.middleware.Middleware.process")` `class Middleware` Expand source code ``` class Middleware(metaclass=ABCMeta): """A middleware can process request data before other middleware and listener functions.""" @abstractmethod def process( self, *, req: BoltRequest, resp: BoltResponse, # As this method is not supposed to be invoked by bolt-python users, # the naming conflict with the built-in one affects # only the internals of this method next: Callable[[], BoltResponse], ) -> Optional[BoltResponse]: """Processes a request data before other middleware and listeners. A middleware calls `next()` function if the chain should continue. @app.middleware def simple_middleware(req, resp, next): # do something here next() This `process(req, resp, next)` method is supposed to be invoked only inside bolt-python. If you want to avoid the name `next()` in your middleware functions, you can use `next_()` method instead. @app.middleware def simple_middleware(req, resp, next_): # do something here next_() Args: req: The incoming request resp: The response next: The function to tell the chain that it can continue Returns: Processed response (optional) """ raise NotImplementedError() @property def name(self) -> str: """The name of this middleware""" return f"{self.__module__}.{self.__class__.__name__}" ``` A middleware can process request data before other middleware and listener functions. ### Subclasses * [Assistant](assistant/assistant.html#slack_bolt.middleware.assistant.assistant.Assistant "slack_bolt.middleware.assistant.assistant.Assistant") * [AttachingConversationKwargs](attaching_conversation_kwargs/attaching_conversation_kwargs.html#slack_bolt.middleware.attaching_conversation_kwargs.attaching_conversation_kwargs.AttachingConversationKwargs "slack_bolt.middleware.attaching_conversation_kwargs.attaching_conversation_kwargs.AttachingConversationKwargs") * [AttachingFunctionToken](attaching_function_token/attaching_function_token.html#slack_bolt.middleware.attaching_function_token.attaching_function_token.AttachingFunctionToken "slack_bolt.middleware.attaching_function_token.attaching_function_token.AttachingFunctionToken") * [Authorization](authorization/authorization.html#slack_bolt.middleware.authorization.authorization.Authorization "slack_bolt.middleware.authorization.authorization.Authorization") * [CustomMiddleware](custom_middleware.html#slack_bolt.middleware.custom_middleware.CustomMiddleware "slack_bolt.middleware.custom_middleware.CustomMiddleware") * [IgnoringSelfEvents](ignoring_self_events/ignoring_self_events.html#slack_bolt.middleware.ignoring_self_events.ignoring_self_events.IgnoringSelfEvents "slack_bolt.middleware.ignoring_self_events.ignoring_self_events.IgnoringSelfEvents") * [MessageListenerMatches](message_listener_matches/message_listener_matches.html#slack_bolt.middleware.message_listener_matches.message_listener_matches.MessageListenerMatches "slack_bolt.middleware.message_listener_matches.message_listener_matches.MessageListenerMatches") * [RequestVerification](request_verification/request_verification.html#slack_bolt.middleware.request_verification.request_verification.RequestVerification "slack_bolt.middleware.request_verification.request_verification.RequestVerification") * [SslCheck](ssl_check/ssl_check.html#slack_bolt.middleware.ssl_check.ssl_check.SslCheck "slack_bolt.middleware.ssl_check.ssl_check.SslCheck") * [UrlVerification](url_verification/url_verification.html#slack_bolt.middleware.url_verification.url_verification.UrlVerification "slack_bolt.middleware.url_verification.url_verification.UrlVerification") * [WorkflowStepMiddleware](../workflows/step/step_middleware.html#slack_bolt.workflows.step.step_middleware.WorkflowStepMiddleware "slack_bolt.workflows.step.step_middleware.WorkflowStepMiddleware") ### Instance variables `prop name : str` Expand source code ``` @property def name(self) -> str: """The name of this middleware""" return f"{self.__module__}.{self.__class__.__name__}" ``` The name of this middleware ### Methods `def process(self, *, req: [BoltRequest](../request/request.html#slack_bolt.request.request.BoltRequest "slack_bolt.request.request.BoltRequest"), resp: [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse"), next: Callable[[], [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse")]) ‑> [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None` Expand source code ``` @abstractmethod def process( self, *, req: BoltRequest, resp: BoltResponse, # As this method is not supposed to be invoked by bolt-python users, # the naming conflict with the built-in one affects # only the internals of this method next: Callable[[], BoltResponse], ) -> Optional[BoltResponse]: """Processes a request data before other middleware and listeners. A middleware calls `next()` function if the chain should continue. @app.middleware def simple_middleware(req, resp, next): # do something here next() This `process(req, resp, next)` method is supposed to be invoked only inside bolt-python. If you want to avoid the name `next()` in your middleware functions, you can use `next_()` method instead. @app.middleware def simple_middleware(req, resp, next_): # do something here next_() Args: req: The incoming request resp: The response next: The function to tell the chain that it can continue Returns: Processed response (optional) """ raise NotImplementedError() ``` Processes a request data before other middleware and listeners. A middleware calls `next()` function if the chain should continue. ``` @app.middleware def simple_middleware(req, resp, next): # do something here next() ``` This `process(req, resp, next)` method is supposed to be invoked only inside bolt-python. If you want to avoid the name `next()` in your middleware functions, you can use `next_()` method instead. ``` @app.middleware def simple_middleware(req, resp, next_): # do something here next_() ``` ## Args **`req`** The incoming request **`resp`** The response **`next`** The function to tell the chain that it can continue ## Returns Processed response (optional) `class MultiTeamsAuthorization (*, authorize: [Authorize](../authorization/authorize.html#slack_bolt.authorization.authorize.Authorize "slack_bolt.authorization.authorize.Authorize"), base_logger: logging.Logger | None = None, user_token_resolution: str = 'authed_user', user_facing_authorize_error_message: str | None = None)` Expand source code ``` class MultiTeamsAuthorization(Authorization): authorize: Authorize user_token_resolution: str def __init__( self, *, authorize: Authorize, base_logger: Optional[Logger] = None, user_token_resolution: str = "authed_user", user_facing_authorize_error_message: Optional[str] = None, ): """Multi-workspace authorization. Args: authorize: The function to authorize incoming requests from Slack. base_logger: The base logger user_token_resolution: "authed_user" or "actor" user_facing_authorize_error_message: The user-facing error message when installation is not found """ self.authorize = authorize self.logger = get_bolt_logger(MultiTeamsAuthorization, base_logger=base_logger) self.user_token_resolution = user_token_resolution self.user_facing_authorize_error_message = ( user_facing_authorize_error_message or _build_user_facing_authorize_error_message() ) def process( self, *, req: BoltRequest, resp: BoltResponse, next: Callable[[], BoltResponse], ) -> BoltResponse: if _is_no_auth_required(req): return next() if _is_no_auth_test_call_required(req): req.context.set_authorize_result( AuthorizeResult( enterprise_id=req.context.enterprise_id, team_id=req.context.team_id, user_id=req.context.user_id, ) ) return next() try: auth_result: Optional[AuthorizeResult] = None if self.user_token_resolution == "actor": auth_result = self.authorize( context=req.context, enterprise_id=req.context.enterprise_id, team_id=req.context.team_id, user_id=req.context.user_id, actor_enterprise_id=req.context.actor_enterprise_id, actor_team_id=req.context.actor_team_id, actor_user_id=req.context.actor_user_id, ) else: auth_result = self.authorize( context=req.context, enterprise_id=req.context.enterprise_id, team_id=req.context.team_id, user_id=req.context.user_id, ) if auth_result is not None: req.context.set_authorize_result(auth_result) token = auth_result.bot_token or auth_result.user_token req.context["token"] = token # As App#_init_context() generates a new WebClient for this request, # it's safe to modify this instance. req.context.client.token = token return next() else: # This situation can arise if: # * A developer installed the app from the "Install to Workspace" button in Slack app config page # * The InstallationStore failed to save or deleted the installation for this workspace self.logger.error( "Although the app should be installed into this workspace, " "the AuthorizeResult (returned value from authorize) for it was not found." ) if req.context.response_url is not None: req.context.respond(self.user_facing_authorize_error_message) # type: ignore[misc] return BoltResponse(status=200, body="") return _build_user_facing_error_response(self.user_facing_authorize_error_message) except SlackApiError as e: self.logger.error(f"Failed to authorize with the given token ({e})") return _build_user_facing_error_response(self.user_facing_authorize_error_message) ``` A middleware can process request data before other middleware and listener functions. Multi-workspace authorization. ## Args **`authorize`** The function to authorize incoming requests from Slack. **`base_logger`** The base logger **`user_token_resolution`** "authed\_user" or "actor" **`user_facing_authorize_error_message`** The user-facing error message when installation is not found ### Ancestors * [Authorization](authorization/authorization.html#slack_bolt.middleware.authorization.authorization.Authorization "slack_bolt.middleware.authorization.authorization.Authorization") * [Middleware](middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware") ### Class variables `var authorize : [Authorize](../authorization/authorize.html#slack_bolt.authorization.authorize.Authorize "slack_bolt.authorization.authorize.Authorize")` The type of the None singleton. `var user_token_resolution : str` The type of the None singleton. ### Inherited members * `**[Authorization](authorization/authorization.html#slack_bolt.middleware.authorization.authorization.Authorization "slack_bolt.middleware.authorization.authorization.Authorization")**`: * `[name](middleware.html#slack_bolt.middleware.middleware.Middleware.name "slack_bolt.middleware.authorization.authorization.Authorization.name")` * `[process](middleware.html#slack_bolt.middleware.middleware.Middleware.process "slack_bolt.middleware.authorization.authorization.Authorization.process")` `class RequestVerification (signing_secret: str, base_logger: logging.Logger | None = None)` Expand source code ``` class RequestVerification(Middleware): def __init__(self, signing_secret: str, base_logger: Optional[Logger] = None): """Verifies an incoming request by checking the validity of `x-slack-signature`, `x-slack-request-timestamp`, and its body data. Refer to https://docs.slack.dev/authentication/verifying-requests-from-slack/ for details. Args: signing_secret: The signing secret base_logger: The base logger """ self.verifier = SignatureVerifier(signing_secret=signing_secret) self.logger = get_bolt_logger(RequestVerification, base_logger=base_logger) def process( self, *, req: BoltRequest, resp: BoltResponse, # As this method is not supposed to be invoked by bolt-python users, # the naming conflict with the built-in one affects # only the internals of this method next: Callable[[], BoltResponse], ) -> BoltResponse: if self._can_skip(req.mode, req.body): return next() body = req.raw_body timestamp = req.headers.get("x-slack-request-timestamp", ["0"])[0] signature = req.headers.get("x-slack-signature", [""])[0] if self.verifier.is_valid(body, timestamp, signature): return next() else: self._debug_log_error(signature, timestamp, body) return self._build_error_response() # ----------------------------------------- @staticmethod def _can_skip(mode: str, body: Dict[str, Any]) -> bool: return mode == "socket_mode" or (body is not None and body.get("ssl_check") == "1") @staticmethod def _build_error_response() -> BoltResponse: return BoltResponse(status=401, body={"error": "invalid request"}) def _debug_log_error(self, signature, timestamp, body) -> None: self.logger.info( "Invalid request signature detected " f"(signature: {signature}, timestamp: {timestamp}, body: {body})" ) ``` A middleware can process request data before other middleware and listener functions. Verifies an incoming request by checking the validity of `x-slack-signature`, `x-slack-request-timestamp`, and its body data. Refer to [https://docs.slack.dev/authentication/verifying-requests-from-slack/](https://docs.slack.dev/authentication/verifying-requests-from-slack/) for details. ## Args **`signing_secret`** The signing secret **`base_logger`** The base logger ### Ancestors * [Middleware](middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware") ### Subclasses * [AsyncRequestVerification](request_verification/async_request_verification.html#slack_bolt.middleware.request_verification.async_request_verification.AsyncRequestVerification "slack_bolt.middleware.request_verification.async_request_verification.AsyncRequestVerification") ### Inherited members * `**[Middleware](middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")**`: * `[name](middleware.html#slack_bolt.middleware.middleware.Middleware.name "slack_bolt.middleware.middleware.Middleware.name")` * `[process](middleware.html#slack_bolt.middleware.middleware.Middleware.process "slack_bolt.middleware.middleware.Middleware.process")` `class SingleTeamAuthorization (*, auth_test_result: slack_sdk.web.slack_response.SlackResponse | None = None, base_logger: logging.Logger | None = None, user_facing_authorize_error_message: str | None = None)` Expand source code ``` class SingleTeamAuthorization(Authorization): def __init__( self, *, auth_test_result: Optional[SlackResponse] = None, base_logger: Optional[Logger] = None, user_facing_authorize_error_message: Optional[str] = None, ): """Single-workspace authorization. Args: auth_test_result: The initial `auth.test` API call result. base_logger: The base logger """ self.auth_test_result = auth_test_result self.logger = get_bolt_logger(SingleTeamAuthorization, base_logger=base_logger) self.user_facing_authorize_error_message = ( user_facing_authorize_error_message or _build_user_facing_authorize_error_message() ) def process( self, *, req: BoltRequest, resp: BoltResponse, # As this method is not supposed to be invoked by bolt-python users, # the naming conflict with the built-in one affects # only the internals of this method next: Callable[[], BoltResponse], ) -> BoltResponse: if _is_no_auth_required(req): return next() if _is_no_auth_test_call_required(req): req.context.set_authorize_result( AuthorizeResult( enterprise_id=req.context.enterprise_id, team_id=req.context.team_id, user_id=req.context.user_id, ) ) return next() try: if not self.auth_test_result: self.auth_test_result = req.context.client.auth_test() if self.auth_test_result: req.context.set_authorize_result( _to_authorize_result( auth_test_result=self.auth_test_result, token=req.context.client.token, request_user_id=req.context.user_id, ) ) return next() else: # Just in case self.logger.error("auth.test API call result is unexpectedly None") if req.context.response_url is not None: req.context.respond(self.user_facing_authorize_error_message) # type: ignore[misc] return BoltResponse(status=200, body="") return _build_user_facing_error_response(self.user_facing_authorize_error_message) except SlackApiError as e: self.logger.error(f"Failed to authorize with the given token ({e})") return _build_user_facing_error_response(self.user_facing_authorize_error_message) ``` A middleware can process request data before other middleware and listener functions. Single-workspace authorization. ## Args **`auth_test_result`** The initial `auth.test` API call result. **`base_logger`** The base logger ### Ancestors * [Authorization](authorization/authorization.html#slack_bolt.middleware.authorization.authorization.Authorization "slack_bolt.middleware.authorization.authorization.Authorization") * [Middleware](middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware") ### Inherited members * `**[Authorization](authorization/authorization.html#slack_bolt.middleware.authorization.authorization.Authorization "slack_bolt.middleware.authorization.authorization.Authorization")**`: * `[name](middleware.html#slack_bolt.middleware.middleware.Middleware.name "slack_bolt.middleware.authorization.authorization.Authorization.name")` * `[process](middleware.html#slack_bolt.middleware.middleware.Middleware.process "slack_bolt.middleware.authorization.authorization.Authorization.process")` `class SslCheck (verification_token: str | None = None, base_logger: logging.Logger | None = None)` Expand source code ``` class SslCheck(Middleware): verification_token: Optional[str] logger: Logger def __init__( self, verification_token: Optional[str] = None, base_logger: Optional[Logger] = None, ): """Handles `ssl_check` requests. Refer to https://docs.slack.dev/interactivity/implementing-slash-commands/ for details. Args: verification_token: The verification token to check (optional as it's already deprecated - https://docs.slack.dev/authentication/verifying-requests-from-slack/#deprecation) base_logger: The base logger """ # noqa: E501 self.verification_token = verification_token self.logger = get_bolt_logger(SslCheck, base_logger=base_logger) def process( self, *, req: BoltRequest, resp: BoltResponse, # As this method is not supposed to be invoked by bolt-python users, # the naming conflict with the built-in one affects # only the internals of this method next: Callable[[], BoltResponse], ) -> BoltResponse: if self._is_ssl_check_request(req.body): if self._verify_token_if_needed(req.body): return self._build_error_response() return self._build_success_response() else: return next() # ----------------------------------------- @staticmethod def _is_ssl_check_request(body: dict): return "ssl_check" in body and body["ssl_check"] == "1" def _verify_token_if_needed(self, body: dict): return self.verification_token and self.verification_token == body["token"] @staticmethod def _build_success_response() -> BoltResponse: return BoltResponse(status=200, body="") @staticmethod def _build_error_response() -> BoltResponse: return BoltResponse(status=401, body={"error": "invalid verification token"}) ``` A middleware can process request data before other middleware and listener functions. Handles `[slack_bolt.middleware.ssl_check](ssl_check/index.html "slack_bolt.middleware.ssl_check")` requests. Refer to [https://docs.slack.dev/interactivity/implementing-slash-commands/](https://docs.slack.dev/interactivity/implementing-slash-commands/) for details. ## Args **`verification_token`** The verification token to check (optional as it's already deprecated - [https://docs.slack.dev/authentication/verifying-requests-from-slack/#deprecation](https://docs.slack.dev/authentication/verifying-requests-from-slack/#deprecation)) **`base_logger`** The base logger ### Ancestors * [Middleware](middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware") ### Subclasses * [AsyncSslCheck](ssl_check/async_ssl_check.html#slack_bolt.middleware.ssl_check.async_ssl_check.AsyncSslCheck "slack_bolt.middleware.ssl_check.async_ssl_check.AsyncSslCheck") ### Class variables `var logger : logging.Logger` The type of the None singleton. `var verification_token : str | None` The type of the None singleton. ### Inherited members * `**[Middleware](middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")**`: * `[name](middleware.html#slack_bolt.middleware.middleware.Middleware.name "slack_bolt.middleware.middleware.Middleware.name")` * `[process](middleware.html#slack_bolt.middleware.middleware.Middleware.process "slack_bolt.middleware.middleware.Middleware.process")` `class UrlVerification (base_logger: logging.Logger | None = None)` Expand source code ``` class UrlVerification(Middleware): def __init__(self, base_logger: Optional[Logger] = None): """Handles url_verification requests. Refer to https://docs.slack.dev/reference/events/url_verification/ for details. Args: base_logger: The base logger """ self.logger = get_bolt_logger(UrlVerification, base_logger=base_logger) def process( self, *, req: BoltRequest, resp: BoltResponse, # As this method is not supposed to be invoked by bolt-python users, # the naming conflict with the built-in one affects # only the internals of this method next: Callable[[], BoltResponse], ) -> BoltResponse: if self._is_url_verification_request(req.body): return self._build_success_response(req.body) else: return next() # ----------------------------------------- @staticmethod def _is_url_verification_request(body: dict) -> bool: return body is not None and body.get("type") == "url_verification" @staticmethod def _build_success_response(body: dict) -> BoltResponse: return BoltResponse(status=200, body={"challenge": body.get("challenge")}) ``` A middleware can process request data before other middleware and listener functions. Handles url\_verification requests. Refer to [https://docs.slack.dev/reference/events/url\_verification/](https://docs.slack.dev/reference/events/url_verification/) for details. ## Args **`base_logger`** The base logger ### Ancestors * [Middleware](middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware") ### Subclasses * [AsyncUrlVerification](url_verification/async_url_verification.html#slack_bolt.middleware.url_verification.async_url_verification.AsyncUrlVerification "slack_bolt.middleware.url_verification.async_url_verification.AsyncUrlVerification") ### Inherited members * `**[Middleware](middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")**`: * `[name](middleware.html#slack_bolt.middleware.middleware.Middleware.name "slack_bolt.middleware.middleware.Middleware.name")` * `[process](middleware.html#slack_bolt.middleware.middleware.Middleware.process "slack_bolt.middleware.middleware.Middleware.process")` --- Source: https://docs.slack.dev/tools/bolt-python/reference/middleware/assistant # Module slack_bolt.middleware.assistant ## Sub-modules `[slack_bolt.middleware.assistant.assistant](assistant.html "slack_bolt.middleware.assistant.assistant")` `[slack_bolt.middleware.assistant.async_assistant](async_assistant.html "slack_bolt.middleware.assistant.async_assistant")` ## Classes `class Assistant (*, app_name: str = 'assistant', thread_context_store: [AssistantThreadContextStore](../../context/assistant/thread_context_store/store.html#slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore "slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore") | None = None, logger: logging.Logger | None = None)` Expand source code ``` class Assistant(Middleware): _thread_started_listeners: Optional[List[Listener]] _thread_context_changed_listeners: Optional[List[Listener]] _user_message_listeners: Optional[List[Listener]] _bot_message_listeners: Optional[List[Listener]] thread_context_store: Optional[AssistantThreadContextStore] base_logger: Optional[logging.Logger] def __init__( self, *, app_name: str = "assistant", thread_context_store: Optional[AssistantThreadContextStore] = None, logger: Optional[logging.Logger] = None, ): self.app_name = app_name self.thread_context_store = thread_context_store self.base_logger = logger self._thread_started_listeners = None self._thread_context_changed_listeners = None self._user_message_listeners = None self._bot_message_listeners = None def thread_started( self, *args, matchers: Optional[Union[Callable[..., bool], ListenerMatcher]] = None, middleware: Optional[Union[Callable, Middleware]] = None, lazy: Optional[List[Callable[..., None]]] = None, ): if self._thread_started_listeners is None: self._thread_started_listeners = [] all_matchers = self._merge_matchers(is_assistant_thread_started_event, matchers) if is_used_without_argument(args): func = args[0] self._thread_started_listeners.append( self.build_listener( listener_or_functions=func, matchers=all_matchers, middleware=middleware, # type: ignore[arg-type] ) ) return func def _inner(func): functions = [func] + (lazy if lazy is not None else []) self._thread_started_listeners.append( self.build_listener( listener_or_functions=functions, matchers=all_matchers, middleware=middleware, ) ) @wraps(func) def _wrapper(*args, **kwargs): return func(*args, **kwargs) return _wrapper return _inner def user_message( self, *args, matchers: Optional[Union[Callable[..., bool], ListenerMatcher]] = None, middleware: Optional[Union[Callable, Middleware]] = None, lazy: Optional[List[Callable[..., None]]] = None, ): if self._user_message_listeners is None: self._user_message_listeners = [] all_matchers = self._merge_matchers(is_user_message_event_in_assistant_thread, matchers) if is_used_without_argument(args): func = args[0] self._user_message_listeners.append( self.build_listener( listener_or_functions=func, matchers=all_matchers, middleware=middleware, # type: ignore[arg-type] ) ) return func def _inner(func): functions = [func] + (lazy if lazy is not None else []) self._user_message_listeners.append( self.build_listener( listener_or_functions=functions, matchers=all_matchers, middleware=middleware, ) ) @wraps(func) def _wrapper(*args, **kwargs): return func(*args, **kwargs) return _wrapper return _inner def bot_message( self, *args, matchers: Optional[Union[Callable[..., bool], ListenerMatcher]] = None, middleware: Optional[Union[Callable, Middleware]] = None, lazy: Optional[List[Callable[..., None]]] = None, ): if self._bot_message_listeners is None: self._bot_message_listeners = [] all_matchers = self._merge_matchers(is_bot_message_event_in_assistant_thread, matchers) if is_used_without_argument(args): func = args[0] self._bot_message_listeners.append( self.build_listener( listener_or_functions=func, matchers=all_matchers, middleware=middleware, # type: ignore[arg-type] ) ) return func def _inner(func): functions = [func] + (lazy if lazy is not None else []) self._bot_message_listeners.append( self.build_listener( listener_or_functions=functions, matchers=all_matchers, middleware=middleware, ) ) @wraps(func) def _wrapper(*args, **kwargs): return func(*args, **kwargs) return _wrapper return _inner def thread_context_changed( self, *args, matchers: Optional[Union[Callable[..., bool], ListenerMatcher]] = None, middleware: Optional[Union[Callable, Middleware]] = None, lazy: Optional[List[Callable[..., None]]] = None, ): if self._thread_context_changed_listeners is None: self._thread_context_changed_listeners = [] all_matchers = self._merge_matchers(is_assistant_thread_context_changed_event, matchers) if is_used_without_argument(args): func = args[0] self._thread_context_changed_listeners.append( self.build_listener( listener_or_functions=func, matchers=all_matchers, middleware=middleware, # type: ignore[arg-type] ) ) return func def _inner(func): functions = [func] + (lazy if lazy is not None else []) self._thread_context_changed_listeners.append( self.build_listener( listener_or_functions=functions, matchers=all_matchers, middleware=middleware, ) ) @wraps(func) def _wrapper(*args, **kwargs): return func(*args, **kwargs) return _wrapper return _inner def _merge_matchers( self, primary_matcher: Callable[..., bool], custom_matchers: Optional[Union[Callable[..., bool], ListenerMatcher]], ): return [CustomListenerMatcher(app_name=self.app_name, func=primary_matcher)] + ( custom_matchers or [] ) # type: ignore[operator] @staticmethod def default_thread_context_changed(save_thread_context: SaveThreadContext, payload: dict): save_thread_context(payload["assistant_thread"]["context"]) def process( # type: ignore[return] self, *, req: BoltRequest, resp: BoltResponse, next: Callable[[], BoltResponse] ) -> Optional[BoltResponse]: if self._thread_context_changed_listeners is None: self.thread_context_changed(self.default_thread_context_changed) listener_runner: ThreadListenerRunner = req.context.listener_runner for listeners in [ self._thread_started_listeners, self._thread_context_changed_listeners, self._user_message_listeners, self._bot_message_listeners, ]: if listeners is not None: for listener in listeners: if listener.matches(req=req, resp=resp): middleware_resp, next_was_not_called = listener.run_middleware(req=req, resp=resp) if next_was_not_called: if middleware_resp is not None: return middleware_resp # The listener middleware didn't call next(). # Skip this listener and try the next one. continue if middleware_resp is not None: resp = middleware_resp return listener_runner.run( request=req, response=resp, listener_name="assistant_listener", listener=listener, ) if is_other_message_sub_event_in_assistant_thread(req.body): # message_changed, message_deleted, etc. return req.context.ack() next() def build_listener( self, listener_or_functions: Union[Listener, Callable, List[Callable]], matchers: Optional[List[Union[ListenerMatcher, Callable[..., bool]]]] = None, middleware: Optional[List[Middleware]] = None, base_logger: Optional[Logger] = None, ) -> Listener: if isinstance(listener_or_functions, Callable): # type: ignore[arg-type] listener_or_functions = [listener_or_functions] # type: ignore[list-item] if isinstance(listener_or_functions, Listener): return listener_or_functions elif isinstance(listener_or_functions, list): middleware = middleware if middleware else [] middleware.insert(0, AttachingConversationKwargs(self.thread_context_store)) functions = listener_or_functions ack_function = functions.pop(0) matchers = matchers if matchers else [] listener_matchers: List[ListenerMatcher] = [] for matcher in matchers: if isinstance(matcher, ListenerMatcher): listener_matchers.append(matcher) elif isinstance(matcher, Callable): # type: ignore[arg-type] listener_matchers.append( build_listener_matcher( func=matcher, asyncio=False, base_logger=base_logger, ) ) return CustomListener( app_name=self.app_name, matchers=listener_matchers, middleware=middleware, ack_function=ack_function, lazy_functions=functions, auto_acknowledgement=True, base_logger=base_logger or self.base_logger, ) else: raise BoltError(f"Invalid listener: {type(listener_or_functions)} detected") ``` A middleware can process request data before other middleware and listener functions. ### Ancestors * [Middleware](../middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware") ### Class variables `var base_logger : logging.Logger | None` The type of the None singleton. `var thread_context_store : [AssistantThreadContextStore](../../context/assistant/thread_context_store/store.html#slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore "slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore") | None` The type of the None singleton. ### Static methods `def default_thread_context_changed(save_thread_context: [SaveThreadContext](../../context/save_thread_context/save_thread_context.html#slack_bolt.context.save_thread_context.save_thread_context.SaveThreadContext "slack_bolt.context.save_thread_context.save_thread_context.SaveThreadContext"), payload: dict)` Expand source code ``` @staticmethod def default_thread_context_changed(save_thread_context: SaveThreadContext, payload: dict): save_thread_context(payload["assistant_thread"]["context"]) ``` ### Methods `def bot_message(self, *args, matchers: Callable[..., bool] | [ListenerMatcher](../../listener_matcher/listener_matcher.html#slack_bolt.listener_matcher.listener_matcher.ListenerMatcher "slack_bolt.listener_matcher.listener_matcher.ListenerMatcher") | None = None, middleware: Callable | [Middleware](../middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware") | None = None, lazy: List[Callable[..., None]] | None = None)` Expand source code ``` def bot_message( self, *args, matchers: Optional[Union[Callable[..., bool], ListenerMatcher]] = None, middleware: Optional[Union[Callable, Middleware]] = None, lazy: Optional[List[Callable[..., None]]] = None, ): if self._bot_message_listeners is None: self._bot_message_listeners = [] all_matchers = self._merge_matchers(is_bot_message_event_in_assistant_thread, matchers) if is_used_without_argument(args): func = args[0] self._bot_message_listeners.append( self.build_listener( listener_or_functions=func, matchers=all_matchers, middleware=middleware, # type: ignore[arg-type] ) ) return func def _inner(func): functions = [func] + (lazy if lazy is not None else []) self._bot_message_listeners.append( self.build_listener( listener_or_functions=functions, matchers=all_matchers, middleware=middleware, ) ) @wraps(func) def _wrapper(*args, **kwargs): return func(*args, **kwargs) return _wrapper return _inner ``` `def build_listener(self, listener_or_functions: [Listener](../../listener/listener.html#slack_bolt.listener.listener.Listener "slack_bolt.listener.listener.Listener") | Callable | List[Callable], matchers: List[[ListenerMatcher](../../listener_matcher/listener_matcher.html#slack_bolt.listener_matcher.listener_matcher.ListenerMatcher "slack_bolt.listener_matcher.listener_matcher.ListenerMatcher") | Callable[..., bool]] | None = None, middleware: List[[Middleware](../middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None, base_logger: logging.Logger | None = None) ‑> [Listener](../../listener/listener.html#slack_bolt.listener.listener.Listener "slack_bolt.listener.listener.Listener")` Expand source code ``` def build_listener( self, listener_or_functions: Union[Listener, Callable, List[Callable]], matchers: Optional[List[Union[ListenerMatcher, Callable[..., bool]]]] = None, middleware: Optional[List[Middleware]] = None, base_logger: Optional[Logger] = None, ) -> Listener: if isinstance(listener_or_functions, Callable): # type: ignore[arg-type] listener_or_functions = [listener_or_functions] # type: ignore[list-item] if isinstance(listener_or_functions, Listener): return listener_or_functions elif isinstance(listener_or_functions, list): middleware = middleware if middleware else [] middleware.insert(0, AttachingConversationKwargs(self.thread_context_store)) functions = listener_or_functions ack_function = functions.pop(0) matchers = matchers if matchers else [] listener_matchers: List[ListenerMatcher] = [] for matcher in matchers: if isinstance(matcher, ListenerMatcher): listener_matchers.append(matcher) elif isinstance(matcher, Callable): # type: ignore[arg-type] listener_matchers.append( build_listener_matcher( func=matcher, asyncio=False, base_logger=base_logger, ) ) return CustomListener( app_name=self.app_name, matchers=listener_matchers, middleware=middleware, ack_function=ack_function, lazy_functions=functions, auto_acknowledgement=True, base_logger=base_logger or self.base_logger, ) else: raise BoltError(f"Invalid listener: {type(listener_or_functions)} detected") ``` `def thread_context_changed(self, *args, matchers: Callable[..., bool] | [ListenerMatcher](../../listener_matcher/listener_matcher.html#slack_bolt.listener_matcher.listener_matcher.ListenerMatcher "slack_bolt.listener_matcher.listener_matcher.ListenerMatcher") | None = None, middleware: Callable | [Middleware](../middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware") | None = None, lazy: List[Callable[..., None]] | None = None)` Expand source code ``` def thread_context_changed( self, *args, matchers: Optional[Union[Callable[..., bool], ListenerMatcher]] = None, middleware: Optional[Union[Callable, Middleware]] = None, lazy: Optional[List[Callable[..., None]]] = None, ): if self._thread_context_changed_listeners is None: self._thread_context_changed_listeners = [] all_matchers = self._merge_matchers(is_assistant_thread_context_changed_event, matchers) if is_used_without_argument(args): func = args[0] self._thread_context_changed_listeners.append( self.build_listener( listener_or_functions=func, matchers=all_matchers, middleware=middleware, # type: ignore[arg-type] ) ) return func def _inner(func): functions = [func] + (lazy if lazy is not None else []) self._thread_context_changed_listeners.append( self.build_listener( listener_or_functions=functions, matchers=all_matchers, middleware=middleware, ) ) @wraps(func) def _wrapper(*args, **kwargs): return func(*args, **kwargs) return _wrapper return _inner ``` `def thread_started(self, *args, matchers: Callable[..., bool] | [ListenerMatcher](../../listener_matcher/listener_matcher.html#slack_bolt.listener_matcher.listener_matcher.ListenerMatcher "slack_bolt.listener_matcher.listener_matcher.ListenerMatcher") | None = None, middleware: Callable | [Middleware](../middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware") | None = None, lazy: List[Callable[..., None]] | None = None)` Expand source code ``` def thread_started( self, *args, matchers: Optional[Union[Callable[..., bool], ListenerMatcher]] = None, middleware: Optional[Union[Callable, Middleware]] = None, lazy: Optional[List[Callable[..., None]]] = None, ): if self._thread_started_listeners is None: self._thread_started_listeners = [] all_matchers = self._merge_matchers(is_assistant_thread_started_event, matchers) if is_used_without_argument(args): func = args[0] self._thread_started_listeners.append( self.build_listener( listener_or_functions=func, matchers=all_matchers, middleware=middleware, # type: ignore[arg-type] ) ) return func def _inner(func): functions = [func] + (lazy if lazy is not None else []) self._thread_started_listeners.append( self.build_listener( listener_or_functions=functions, matchers=all_matchers, middleware=middleware, ) ) @wraps(func) def _wrapper(*args, **kwargs): return func(*args, **kwargs) return _wrapper return _inner ``` `def user_message(self, *args, matchers: Callable[..., bool] | [ListenerMatcher](../../listener_matcher/listener_matcher.html#slack_bolt.listener_matcher.listener_matcher.ListenerMatcher "slack_bolt.listener_matcher.listener_matcher.ListenerMatcher") | None = None, middleware: Callable | [Middleware](../middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware") | None = None, lazy: List[Callable[..., None]] | None = None)` Expand source code ``` def user_message( self, *args, matchers: Optional[Union[Callable[..., bool], ListenerMatcher]] = None, middleware: Optional[Union[Callable, Middleware]] = None, lazy: Optional[List[Callable[..., None]]] = None, ): if self._user_message_listeners is None: self._user_message_listeners = [] all_matchers = self._merge_matchers(is_user_message_event_in_assistant_thread, matchers) if is_used_without_argument(args): func = args[0] self._user_message_listeners.append( self.build_listener( listener_or_functions=func, matchers=all_matchers, middleware=middleware, # type: ignore[arg-type] ) ) return func def _inner(func): functions = [func] + (lazy if lazy is not None else []) self._user_message_listeners.append( self.build_listener( listener_or_functions=functions, matchers=all_matchers, middleware=middleware, ) ) @wraps(func) def _wrapper(*args, **kwargs): return func(*args, **kwargs) return _wrapper return _inner ``` ### Inherited members * `**[Middleware](../middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")**`: * `[name](../middleware.html#slack_bolt.middleware.middleware.Middleware.name "slack_bolt.middleware.middleware.Middleware.name")` * `[process](../middleware.html#slack_bolt.middleware.middleware.Middleware.process "slack_bolt.middleware.middleware.Middleware.process")` --- Source: https://docs.slack.dev/tools/bolt-python/reference/middleware/attaching_conversation_kwargs # Module slack_bolt.middleware.attaching_conversation_kwargs ## Sub-modules `[slack_bolt.middleware.attaching_conversation_kwargs.async_attaching_conversation_kwargs](async_attaching_conversation_kwargs.html "slack_bolt.middleware.attaching_conversation_kwargs.async_attaching_conversation_kwargs")` `[slack_bolt.middleware.attaching_conversation_kwargs.attaching_conversation_kwargs](attaching_conversation_kwargs.html "slack_bolt.middleware.attaching_conversation_kwargs.attaching_conversation_kwargs")` ## Classes `class AttachingConversationKwargs (thread_context_store: [AssistantThreadContextStore](../../context/assistant/thread_context_store/store.html#slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore "slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore") | None = None)` Expand source code ``` class AttachingConversationKwargs(Middleware): thread_context_store: Optional[AssistantThreadContextStore] def __init__(self, thread_context_store: Optional[AssistantThreadContextStore] = None): self.thread_context_store = thread_context_store def process(self, *, req: BoltRequest, resp: BoltResponse, next: Callable[[], BoltResponse]) -> Optional[BoltResponse]: event = to_event(req.body) if event is not None: if is_assistant_event(req.body): assistant = AssistantUtilities( payload=event, context=req.context, thread_context_store=self.thread_context_store, ) req.context["say"] = assistant.say req.context["set_title"] = assistant.set_title req.context["set_suggested_prompts"] = assistant.set_suggested_prompts req.context["get_thread_context"] = assistant.get_thread_context req.context["save_thread_context"] = assistant.save_thread_context # TODO: in the future we might want to introduce a "proper" extract_ts utility thread_ts = req.context.thread_ts or event.get("ts") if req.context.channel_id and thread_ts: req.context["set_status"] = SetStatus( client=req.context.client, channel_id=req.context.channel_id, thread_ts=thread_ts, ) req.context["say_stream"] = SayStream( client=req.context.client, channel=req.context.channel_id, recipient_team_id=req.context.team_id or req.context.enterprise_id, recipient_user_id=req.context.user_id, thread_ts=thread_ts, ) return next() ``` A middleware can process request data before other middleware and listener functions. ### Ancestors * [Middleware](../middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware") ### Class variables `var thread_context_store : [AssistantThreadContextStore](../../context/assistant/thread_context_store/store.html#slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore "slack_bolt.context.assistant.thread_context_store.store.AssistantThreadContextStore") | None` The type of the None singleton. ### Inherited members * `**[Middleware](../middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")**`: * `[name](../middleware.html#slack_bolt.middleware.middleware.Middleware.name "slack_bolt.middleware.middleware.Middleware.name")` * `[process](../middleware.html#slack_bolt.middleware.middleware.Middleware.process "slack_bolt.middleware.middleware.Middleware.process")` --- Source: https://docs.slack.dev/tools/bolt-python/reference/middleware/attaching_function_token # Module slack_bolt.middleware.attaching_function_token ## Sub-modules `[slack_bolt.middleware.attaching_function_token.async_attaching_function_token](async_attaching_function_token.html "slack_bolt.middleware.attaching_function_token.async_attaching_function_token")` `[slack_bolt.middleware.attaching_function_token.attaching_function_token](attaching_function_token.html "slack_bolt.middleware.attaching_function_token.attaching_function_token")` ## Classes `class AttachingFunctionToken` Expand source code ``` class AttachingFunctionToken(Middleware): def process( self, *, req: BoltRequest, resp: BoltResponse, # This method is not supposed to be invoked by bolt-python users next: Callable[[], BoltResponse], ) -> BoltResponse: if req.context.function_bot_access_token is not None: req.context.client.token = req.context.function_bot_access_token return next() ``` A middleware can process request data before other middleware and listener functions. ### Ancestors * [Middleware](../middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware") ### Inherited members * `**[Middleware](../middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")**`: * `[name](../middleware.html#slack_bolt.middleware.middleware.Middleware.name "slack_bolt.middleware.middleware.Middleware.name")` * `[process](../middleware.html#slack_bolt.middleware.middleware.Middleware.process "slack_bolt.middleware.middleware.Middleware.process")` --- Source: https://docs.slack.dev/tools/bolt-python/reference/middleware/authorization # Module slack_bolt.middleware.authorization ## Sub-modules `[slack_bolt.middleware.authorization.async_authorization](async_authorization.html "slack_bolt.middleware.authorization.async_authorization")` `[slack_bolt.middleware.authorization.async_internals](async_internals.html "slack_bolt.middleware.authorization.async_internals")` `[slack_bolt.middleware.authorization.async_multi_teams_authorization](async_multi_teams_authorization.html "slack_bolt.middleware.authorization.async_multi_teams_authorization")` `[slack_bolt.middleware.authorization.async_single_team_authorization](async_single_team_authorization.html "slack_bolt.middleware.authorization.async_single_team_authorization")` `[slack_bolt.middleware.authorization.authorization](authorization.html "slack_bolt.middleware.authorization.authorization")` `[slack_bolt.middleware.authorization.internals](internals.html "slack_bolt.middleware.authorization.internals")` `[slack_bolt.middleware.authorization.multi_teams_authorization](multi_teams_authorization.html "slack_bolt.middleware.authorization.multi_teams_authorization")` `[slack_bolt.middleware.authorization.single_team_authorization](single_team_authorization.html "slack_bolt.middleware.authorization.single_team_authorization")` ## Classes `class Authorization` Expand source code ``` class Authorization(Middleware): pass ``` A middleware can process request data before other middleware and listener functions. ### Ancestors * [Middleware](../middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware") ### Subclasses * [MultiTeamsAuthorization](multi_teams_authorization.html#slack_bolt.middleware.authorization.multi_teams_authorization.MultiTeamsAuthorization "slack_bolt.middleware.authorization.multi_teams_authorization.MultiTeamsAuthorization") * [SingleTeamAuthorization](single_team_authorization.html#slack_bolt.middleware.authorization.single_team_authorization.SingleTeamAuthorization "slack_bolt.middleware.authorization.single_team_authorization.SingleTeamAuthorization") ### Inherited members * `**[Middleware](../middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")**`: * `[name](../middleware.html#slack_bolt.middleware.middleware.Middleware.name "slack_bolt.middleware.middleware.Middleware.name")` * `[process](../middleware.html#slack_bolt.middleware.middleware.Middleware.process "slack_bolt.middleware.middleware.Middleware.process")` `class MultiTeamsAuthorization (*, authorize: [Authorize](../../authorization/authorize.html#slack_bolt.authorization.authorize.Authorize "slack_bolt.authorization.authorize.Authorize"), base_logger: logging.Logger | None = None, user_token_resolution: str = 'authed_user', user_facing_authorize_error_message: str | None = None)` Expand source code ``` class MultiTeamsAuthorization(Authorization): authorize: Authorize user_token_resolution: str def __init__( self, *, authorize: Authorize, base_logger: Optional[Logger] = None, user_token_resolution: str = "authed_user", user_facing_authorize_error_message: Optional[str] = None, ): """Multi-workspace authorization. Args: authorize: The function to authorize incoming requests from Slack. base_logger: The base logger user_token_resolution: "authed_user" or "actor" user_facing_authorize_error_message: The user-facing error message when installation is not found """ self.authorize = authorize self.logger = get_bolt_logger(MultiTeamsAuthorization, base_logger=base_logger) self.user_token_resolution = user_token_resolution self.user_facing_authorize_error_message = ( user_facing_authorize_error_message or _build_user_facing_authorize_error_message() ) def process( self, *, req: BoltRequest, resp: BoltResponse, next: Callable[[], BoltResponse], ) -> BoltResponse: if _is_no_auth_required(req): return next() if _is_no_auth_test_call_required(req): req.context.set_authorize_result( AuthorizeResult( enterprise_id=req.context.enterprise_id, team_id=req.context.team_id, user_id=req.context.user_id, ) ) return next() try: auth_result: Optional[AuthorizeResult] = None if self.user_token_resolution == "actor": auth_result = self.authorize( context=req.context, enterprise_id=req.context.enterprise_id, team_id=req.context.team_id, user_id=req.context.user_id, actor_enterprise_id=req.context.actor_enterprise_id, actor_team_id=req.context.actor_team_id, actor_user_id=req.context.actor_user_id, ) else: auth_result = self.authorize( context=req.context, enterprise_id=req.context.enterprise_id, team_id=req.context.team_id, user_id=req.context.user_id, ) if auth_result is not None: req.context.set_authorize_result(auth_result) token = auth_result.bot_token or auth_result.user_token req.context["token"] = token # As App#_init_context() generates a new WebClient for this request, # it's safe to modify this instance. req.context.client.token = token return next() else: # This situation can arise if: # * A developer installed the app from the "Install to Workspace" button in Slack app config page # * The InstallationStore failed to save or deleted the installation for this workspace self.logger.error( "Although the app should be installed into this workspace, " "the AuthorizeResult (returned value from authorize) for it was not found." ) if req.context.response_url is not None: req.context.respond(self.user_facing_authorize_error_message) # type: ignore[misc] return BoltResponse(status=200, body="") return _build_user_facing_error_response(self.user_facing_authorize_error_message) except SlackApiError as e: self.logger.error(f"Failed to authorize with the given token ({e})") return _build_user_facing_error_response(self.user_facing_authorize_error_message) ``` A middleware can process request data before other middleware and listener functions. Multi-workspace authorization. ## Args **`authorize`** The function to authorize incoming requests from Slack. **`base_logger`** The base logger **`user_token_resolution`** "authed\_user" or "actor" **`user_facing_authorize_error_message`** The user-facing error message when installation is not found ### Ancestors * [Authorization](authorization.html#slack_bolt.middleware.authorization.authorization.Authorization "slack_bolt.middleware.authorization.authorization.Authorization") * [Middleware](../middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware") ### Class variables `var authorize : [Authorize](../../authorization/authorize.html#slack_bolt.authorization.authorize.Authorize "slack_bolt.authorization.authorize.Authorize")` The type of the None singleton. `var user_token_resolution : str` The type of the None singleton. ### Inherited members * `**[Authorization](authorization.html#slack_bolt.middleware.authorization.authorization.Authorization "slack_bolt.middleware.authorization.authorization.Authorization")**`: * `[name](../middleware.html#slack_bolt.middleware.middleware.Middleware.name "slack_bolt.middleware.authorization.authorization.Authorization.name")` * `[process](../middleware.html#slack_bolt.middleware.middleware.Middleware.process "slack_bolt.middleware.authorization.authorization.Authorization.process")` `class SingleTeamAuthorization (*, auth_test_result: slack_sdk.web.slack_response.SlackResponse | None = None, base_logger: logging.Logger | None = None, user_facing_authorize_error_message: str | None = None)` Expand source code ``` class SingleTeamAuthorization(Authorization): def __init__( self, *, auth_test_result: Optional[SlackResponse] = None, base_logger: Optional[Logger] = None, user_facing_authorize_error_message: Optional[str] = None, ): """Single-workspace authorization. Args: auth_test_result: The initial `auth.test` API call result. base_logger: The base logger """ self.auth_test_result = auth_test_result self.logger = get_bolt_logger(SingleTeamAuthorization, base_logger=base_logger) self.user_facing_authorize_error_message = ( user_facing_authorize_error_message or _build_user_facing_authorize_error_message() ) def process( self, *, req: BoltRequest, resp: BoltResponse, # As this method is not supposed to be invoked by bolt-python users, # the naming conflict with the built-in one affects # only the internals of this method next: Callable[[], BoltResponse], ) -> BoltResponse: if _is_no_auth_required(req): return next() if _is_no_auth_test_call_required(req): req.context.set_authorize_result( AuthorizeResult( enterprise_id=req.context.enterprise_id, team_id=req.context.team_id, user_id=req.context.user_id, ) ) return next() try: if not self.auth_test_result: self.auth_test_result = req.context.client.auth_test() if self.auth_test_result: req.context.set_authorize_result( _to_authorize_result( auth_test_result=self.auth_test_result, token=req.context.client.token, request_user_id=req.context.user_id, ) ) return next() else: # Just in case self.logger.error("auth.test API call result is unexpectedly None") if req.context.response_url is not None: req.context.respond(self.user_facing_authorize_error_message) # type: ignore[misc] return BoltResponse(status=200, body="") return _build_user_facing_error_response(self.user_facing_authorize_error_message) except SlackApiError as e: self.logger.error(f"Failed to authorize with the given token ({e})") return _build_user_facing_error_response(self.user_facing_authorize_error_message) ``` A middleware can process request data before other middleware and listener functions. Single-workspace authorization. ## Args **`auth_test_result`** The initial `auth.test` API call result. **`base_logger`** The base logger ### Ancestors * [Authorization](authorization.html#slack_bolt.middleware.authorization.authorization.Authorization "slack_bolt.middleware.authorization.authorization.Authorization") * [Middleware](../middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware") ### Inherited members * `**[Authorization](authorization.html#slack_bolt.middleware.authorization.authorization.Authorization "slack_bolt.middleware.authorization.authorization.Authorization")**`: * `[name](../middleware.html#slack_bolt.middleware.middleware.Middleware.name "slack_bolt.middleware.authorization.authorization.Authorization.name")` * `[process](../middleware.html#slack_bolt.middleware.middleware.Middleware.process "slack_bolt.middleware.authorization.authorization.Authorization.process")` --- Source: https://docs.slack.dev/tools/bolt-python/reference/middleware/ignoring_self_events # Module slack_bolt.middleware.ignoring_self_events ## Sub-modules `[slack_bolt.middleware.ignoring_self_events.async_ignoring_self_events](async_ignoring_self_events.html "slack_bolt.middleware.ignoring_self_events.async_ignoring_self_events")` `[slack_bolt.middleware.ignoring_self_events.ignoring_self_events](ignoring_self_events.html "slack_bolt.middleware.ignoring_self_events.ignoring_self_events")` ## Classes `class IgnoringSelfEvents (base_logger: logging.Logger | None = None, ignoring_self_assistant_message_events_enabled: bool = True)` Expand source code ``` class IgnoringSelfEvents(Middleware): def __init__( self, base_logger: Optional[logging.Logger] = None, ignoring_self_assistant_message_events_enabled: bool = True, ): """Ignores the events generated by this bot user itself.""" self.logger = get_bolt_logger(IgnoringSelfEvents, base_logger=base_logger) self.ignoring_self_assistant_message_events_enabled = ignoring_self_assistant_message_events_enabled def process( self, *, req: BoltRequest, resp: BoltResponse, next: Callable[[], BoltResponse], ) -> BoltResponse: auth_result = req.context.authorize_result # message events can have $.event.bot_id while it does not have its user_id bot_id = req.body.get("event", {}).get("bot_id") if self._is_self_event(auth_result, req.context.user_id, bot_id, req.body): # type: ignore[arg-type] if self.ignoring_self_assistant_message_events_enabled is False: if is_bot_message_event_in_assistant_thread(req.body): # Assistant#bot_message handler acknowledges this pattern return next() self._debug_log(req.body) return req.context.ack() else: return next() # ----------------------------------------- # It's an Events API event that isn't of type message, # but the user ID might match our own app. Filter these out. # However, some events still must be fired, because they can make sense. events_that_should_be_kept = ["member_joined_channel", "member_left_channel"] @classmethod def _is_self_event( cls, auth_result: AuthorizeResult, user_id: Optional[str], bot_id: Optional[str], body: Dict[str, Any], ): return ( auth_result is not None and ( (user_id is not None and user_id == auth_result.bot_user_id) or (bot_id is not None and bot_id == auth_result.bot_id) # for bot_message events ) and body.get("event") is not None and body.get("event", {}).get("type") not in cls.events_that_should_be_kept ) def _debug_log(self, body: dict): if self.logger.level <= logging.DEBUG: event = body.get("event") self.logger.debug(f"Skipped self event: {event}") ``` A middleware can process request data before other middleware and listener functions. Ignores the events generated by this bot user itself. ### Ancestors * [Middleware](../middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware") ### Subclasses * [AsyncIgnoringSelfEvents](async_ignoring_self_events.html#slack_bolt.middleware.ignoring_self_events.async_ignoring_self_events.AsyncIgnoringSelfEvents "slack_bolt.middleware.ignoring_self_events.async_ignoring_self_events.AsyncIgnoringSelfEvents") ### Class variables `var events_that_should_be_kept` The type of the None singleton. ### Inherited members * `**[Middleware](../middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")**`: * `[name](../middleware.html#slack_bolt.middleware.middleware.Middleware.name "slack_bolt.middleware.middleware.Middleware.name")` * `[process](../middleware.html#slack_bolt.middleware.middleware.Middleware.process "slack_bolt.middleware.middleware.Middleware.process")` --- Source: https://docs.slack.dev/tools/bolt-python/reference/middleware/message_listener_matches # Module slack_bolt.middleware.message_listener_matches ## Sub-modules `[slack_bolt.middleware.message_listener_matches.async_message_listener_matches](async_message_listener_matches.html "slack_bolt.middleware.message_listener_matches.async_message_listener_matches")` `[slack_bolt.middleware.message_listener_matches.message_listener_matches](message_listener_matches.html "slack_bolt.middleware.message_listener_matches.message_listener_matches")` ## Classes `class MessageListenerMatches (keyword: str | Pattern)` Expand source code ``` class MessageListenerMatches(Middleware): def __init__(self, keyword: Union[str, Pattern]): """Captures matched keywords and saves the values in context.""" self.keyword = keyword def process( self, *, req: BoltRequest, resp: BoltResponse, # As this method is not supposed to be invoked by bolt-python users, # the naming conflict with the built-in one affects # only the internals of this method next: Callable[[], BoltResponse], ) -> BoltResponse: text = req.body.get("event", {}).get("text", "") if text: m: Optional[Union[Sequence]] = re.findall(self.keyword, text) if m is not None and m != []: if type(m[0]) is not tuple: m = tuple(m) else: m = m[0] req.context["matches"] = m # tuple or list return next() # As the text doesn't match, skip running the listener return resp ``` A middleware can process request data before other middleware and listener functions. Captures matched keywords and saves the values in context. ### Ancestors * [Middleware](../middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware") ### Inherited members * `**[Middleware](../middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")**`: * `[name](../middleware.html#slack_bolt.middleware.middleware.Middleware.name "slack_bolt.middleware.middleware.Middleware.name")` * `[process](../middleware.html#slack_bolt.middleware.middleware.Middleware.process "slack_bolt.middleware.middleware.Middleware.process")` --- Source: https://docs.slack.dev/tools/bolt-python/reference/middleware/request_verification # Module slack_bolt.middleware.request_verification ## Sub-modules `[slack_bolt.middleware.request_verification.async_request_verification](async_request_verification.html "slack_bolt.middleware.request_verification.async_request_verification")` `[slack_bolt.middleware.request_verification.request_verification](request_verification.html "slack_bolt.middleware.request_verification.request_verification")` ## Classes `class RequestVerification (signing_secret: str, base_logger: logging.Logger | None = None)` Expand source code ``` class RequestVerification(Middleware): def __init__(self, signing_secret: str, base_logger: Optional[Logger] = None): """Verifies an incoming request by checking the validity of `x-slack-signature`, `x-slack-request-timestamp`, and its body data. Refer to https://docs.slack.dev/authentication/verifying-requests-from-slack/ for details. Args: signing_secret: The signing secret base_logger: The base logger """ self.verifier = SignatureVerifier(signing_secret=signing_secret) self.logger = get_bolt_logger(RequestVerification, base_logger=base_logger) def process( self, *, req: BoltRequest, resp: BoltResponse, # As this method is not supposed to be invoked by bolt-python users, # the naming conflict with the built-in one affects # only the internals of this method next: Callable[[], BoltResponse], ) -> BoltResponse: if self._can_skip(req.mode, req.body): return next() body = req.raw_body timestamp = req.headers.get("x-slack-request-timestamp", ["0"])[0] signature = req.headers.get("x-slack-signature", [""])[0] if self.verifier.is_valid(body, timestamp, signature): return next() else: self._debug_log_error(signature, timestamp, body) return self._build_error_response() # ----------------------------------------- @staticmethod def _can_skip(mode: str, body: Dict[str, Any]) -> bool: return mode == "socket_mode" or (body is not None and body.get("ssl_check") == "1") @staticmethod def _build_error_response() -> BoltResponse: return BoltResponse(status=401, body={"error": "invalid request"}) def _debug_log_error(self, signature, timestamp, body) -> None: self.logger.info( "Invalid request signature detected " f"(signature: {signature}, timestamp: {timestamp}, body: {body})" ) ``` A middleware can process request data before other middleware and listener functions. Verifies an incoming request by checking the validity of `x-slack-signature`, `x-slack-request-timestamp`, and its body data. Refer to [https://docs.slack.dev/authentication/verifying-requests-from-slack/](https://docs.slack.dev/authentication/verifying-requests-from-slack/) for details. ## Args **`signing_secret`** The signing secret **`base_logger`** The base logger ### Ancestors * [Middleware](../middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware") ### Subclasses * [AsyncRequestVerification](async_request_verification.html#slack_bolt.middleware.request_verification.async_request_verification.AsyncRequestVerification "slack_bolt.middleware.request_verification.async_request_verification.AsyncRequestVerification") ### Inherited members * `**[Middleware](../middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")**`: * `[name](../middleware.html#slack_bolt.middleware.middleware.Middleware.name "slack_bolt.middleware.middleware.Middleware.name")` * `[process](../middleware.html#slack_bolt.middleware.middleware.Middleware.process "slack_bolt.middleware.middleware.Middleware.process")` --- Source: https://docs.slack.dev/tools/bolt-python/reference/middleware/ssl_check # Module slack_bolt.middleware.ssl_check ## Sub-modules `[slack_bolt.middleware.ssl_check.async_ssl_check](async_ssl_check.html "slack_bolt.middleware.ssl_check.async_ssl_check")` `[slack_bolt.middleware.ssl_check.ssl_check](ssl_check.html "slack_bolt.middleware.ssl_check.ssl_check")` ## Classes `class SslCheck (verification_token: str | None = None, base_logger: logging.Logger | None = None)` Expand source code ``` class SslCheck(Middleware): verification_token: Optional[str] logger: Logger def __init__( self, verification_token: Optional[str] = None, base_logger: Optional[Logger] = None, ): """Handles `ssl_check` requests. Refer to https://docs.slack.dev/interactivity/implementing-slash-commands/ for details. Args: verification_token: The verification token to check (optional as it's already deprecated - https://docs.slack.dev/authentication/verifying-requests-from-slack/#deprecation) base_logger: The base logger """ # noqa: E501 self.verification_token = verification_token self.logger = get_bolt_logger(SslCheck, base_logger=base_logger) def process( self, *, req: BoltRequest, resp: BoltResponse, # As this method is not supposed to be invoked by bolt-python users, # the naming conflict with the built-in one affects # only the internals of this method next: Callable[[], BoltResponse], ) -> BoltResponse: if self._is_ssl_check_request(req.body): if self._verify_token_if_needed(req.body): return self._build_error_response() return self._build_success_response() else: return next() # ----------------------------------------- @staticmethod def _is_ssl_check_request(body: dict): return "ssl_check" in body and body["ssl_check"] == "1" def _verify_token_if_needed(self, body: dict): return self.verification_token and self.verification_token == body["token"] @staticmethod def _build_success_response() -> BoltResponse: return BoltResponse(status=200, body="") @staticmethod def _build_error_response() -> BoltResponse: return BoltResponse(status=401, body={"error": "invalid verification token"}) ``` A middleware can process request data before other middleware and listener functions. Handles `[slack_bolt.middleware.ssl_check.ssl_check](ssl_check.html "slack_bolt.middleware.ssl_check.ssl_check")` requests. Refer to [https://docs.slack.dev/interactivity/implementing-slash-commands/](https://docs.slack.dev/interactivity/implementing-slash-commands/) for details. ## Args **`verification_token`** The verification token to check (optional as it's already deprecated - [https://docs.slack.dev/authentication/verifying-requests-from-slack/#deprecation](https://docs.slack.dev/authentication/verifying-requests-from-slack/#deprecation)) **`base_logger`** The base logger ### Ancestors * [Middleware](../middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware") ### Subclasses * [AsyncSslCheck](async_ssl_check.html#slack_bolt.middleware.ssl_check.async_ssl_check.AsyncSslCheck "slack_bolt.middleware.ssl_check.async_ssl_check.AsyncSslCheck") ### Class variables `var logger : logging.Logger` The type of the None singleton. `var verification_token : str | None` The type of the None singleton. ### Inherited members * `**[Middleware](../middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")**`: * `[name](../middleware.html#slack_bolt.middleware.middleware.Middleware.name "slack_bolt.middleware.middleware.Middleware.name")` * `[process](../middleware.html#slack_bolt.middleware.middleware.Middleware.process "slack_bolt.middleware.middleware.Middleware.process")` --- Source: https://docs.slack.dev/tools/bolt-python/reference/middleware/url_verification # Module slack_bolt.middleware.url_verification ## Sub-modules `[slack_bolt.middleware.url_verification.async_url_verification](async_url_verification.html "slack_bolt.middleware.url_verification.async_url_verification")` `[slack_bolt.middleware.url_verification.url_verification](url_verification.html "slack_bolt.middleware.url_verification.url_verification")` ## Classes `class UrlVerification (base_logger: logging.Logger | None = None)` Expand source code ``` class UrlVerification(Middleware): def __init__(self, base_logger: Optional[Logger] = None): """Handles url_verification requests. Refer to https://docs.slack.dev/reference/events/url_verification/ for details. Args: base_logger: The base logger """ self.logger = get_bolt_logger(UrlVerification, base_logger=base_logger) def process( self, *, req: BoltRequest, resp: BoltResponse, # As this method is not supposed to be invoked by bolt-python users, # the naming conflict with the built-in one affects # only the internals of this method next: Callable[[], BoltResponse], ) -> BoltResponse: if self._is_url_verification_request(req.body): return self._build_success_response(req.body) else: return next() # ----------------------------------------- @staticmethod def _is_url_verification_request(body: dict) -> bool: return body is not None and body.get("type") == "url_verification" @staticmethod def _build_success_response(body: dict) -> BoltResponse: return BoltResponse(status=200, body={"challenge": body.get("challenge")}) ``` A middleware can process request data before other middleware and listener functions. Handles url\_verification requests. Refer to [https://docs.slack.dev/reference/events/url\_verification/](https://docs.slack.dev/reference/events/url_verification/) for details. ## Args **`base_logger`** The base logger ### Ancestors * [Middleware](../middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware") ### Subclasses * [AsyncUrlVerification](async_url_verification.html#slack_bolt.middleware.url_verification.async_url_verification.AsyncUrlVerification "slack_bolt.middleware.url_verification.async_url_verification.AsyncUrlVerification") ### Inherited members * `**[Middleware](../middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")**`: * `[name](../middleware.html#slack_bolt.middleware.middleware.Middleware.name "slack_bolt.middleware.middleware.Middleware.name")` * `[process](../middleware.html#slack_bolt.middleware.middleware.Middleware.process "slack_bolt.middleware.middleware.Middleware.process")` --- Source: https://docs.slack.dev/tools/bolt-python/reference/oauth # Module slack_bolt.oauth Slack OAuth flow support for building an app that is installable in any workspaces. Refer to [https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth](https://docs.slack.dev/tools/bolt-python/concepts/authenticating-oauth) for details. ## Sub-modules `[slack_bolt.oauth.async_callback_options](async_callback_options.html "slack_bolt.oauth.async_callback_options")` `[slack_bolt.oauth.async_internals](async_internals.html "slack_bolt.oauth.async_internals")` `[slack_bolt.oauth.async_oauth_flow](async_oauth_flow.html "slack_bolt.oauth.async_oauth_flow")` `[slack_bolt.oauth.async_oauth_settings](async_oauth_settings.html "slack_bolt.oauth.async_oauth_settings")` `[slack_bolt.oauth.callback_options](callback_options.html "slack_bolt.oauth.callback_options")` `[slack_bolt.oauth.internals](internals.html "slack_bolt.oauth.internals")` `[slack_bolt.oauth.oauth_flow](oauth_flow.html "slack_bolt.oauth.oauth_flow")` `[slack_bolt.oauth.oauth_settings](oauth_settings.html "slack_bolt.oauth.oauth_settings")` ## Classes `class OAuthFlow (*, client: slack_sdk.web.client.WebClient | None = None, logger: logging.Logger | None = None, settings: [OAuthSettings](oauth_settings.html#slack_bolt.oauth.oauth_settings.OAuthSettings "slack_bolt.oauth.oauth_settings.OAuthSettings"))` Expand source code ``` class OAuthFlow: settings: OAuthSettings client_id: str redirect_uri: Optional[str] install_path: str redirect_uri_path: str success_handler: Callable[[SuccessArgs], BoltResponse] failure_handler: Callable[[FailureArgs], BoltResponse] def __init__( self, *, client: Optional[WebClient] = None, logger: Optional[Logger] = None, settings: OAuthSettings, ): """The module to run the Slack app installation flow (OAuth flow). Args: client: The `slack_sdk.web.WebClient` instance. logger: The logger. settings: OAuth settings to configure this module. """ self._client = client self._logger = logger self.settings = settings if self._logger is not None: self.settings.logger = self._logger self.client_id = self.settings.client_id self.redirect_uri = self.settings.redirect_uri self.install_path = self.settings.install_path self.redirect_uri_path = self.settings.redirect_uri_path self.default_callback_options = DefaultCallbackOptions( logger=logger, # type: ignore[arg-type] state_utils=self.settings.state_utils, redirect_uri_page_renderer=self.settings.redirect_uri_page_renderer, ) if settings.callback_options is None: settings.callback_options = self.default_callback_options self.success_handler = settings.callback_options.success self.failure_handler = settings.callback_options.failure @property def client(self) -> WebClient: if self._client is None: self._client = create_web_client(logger=self.logger) return self._client @property def logger(self) -> Logger: if self._logger is None: self._logger = logging.getLogger(__name__) return self._logger # ----------------------------- # Factory Methods # ----------------------------- @classmethod def sqlite3( cls, database: str, # OAuth flow parameters/credentials client_id: Optional[str] = None, # required client_secret: Optional[str] = None, # required scopes: Optional[Sequence[str]] = None, user_scopes: Optional[Sequence[str]] = None, redirect_uri: Optional[str] = None, # Handler configuration install_path: Optional[str] = None, redirect_uri_path: Optional[str] = None, callback_options: Optional[CallbackOptions] = None, success_url: Optional[str] = None, failure_url: Optional[str] = None, authorization_url: Optional[str] = None, # Installation Management # state parameter related configurations state_cookie_name: str = OAuthStateUtils.default_cookie_name, state_expiration_seconds: int = OAuthStateUtils.default_expiration_seconds, installation_store_bot_only: bool = False, token_rotation_expiration_minutes: int = 120, client: Optional[WebClient] = None, logger: Optional[Logger] = None, ) -> "OAuthFlow": client_id = client_id or os.environ["SLACK_CLIENT_ID"] # required client_secret = client_secret or os.environ["SLACK_CLIENT_SECRET"] # required scopes = scopes or os.environ.get("SLACK_SCOPES", "").split(",") user_scopes = user_scopes or os.environ.get("SLACK_USER_SCOPES", "").split(",") redirect_uri = redirect_uri or os.environ.get("SLACK_REDIRECT_URI") installation_store = ( SQLite3InstallationStore(database=database, client_id=client_id) if logger is None else SQLite3InstallationStore(database=database, client_id=client_id, logger=logger) ) state_store = ( SQLite3OAuthStateStore(database=database, expiration_seconds=state_expiration_seconds) if logger is None else SQLite3OAuthStateStore(database=database, expiration_seconds=state_expiration_seconds, logger=logger) ) return OAuthFlow( client=client or WebClient(), logger=logger, settings=OAuthSettings( # OAuth flow parameters/credentials client_id=client_id, client_secret=client_secret, scopes=scopes, user_scopes=user_scopes, redirect_uri=redirect_uri, # Handler configuration install_path=install_path, # type: ignore[arg-type] redirect_uri_path=redirect_uri_path, # type: ignore[arg-type] callback_options=callback_options, success_url=success_url, failure_url=failure_url, authorization_url=authorization_url, # Installation Management installation_store=installation_store, installation_store_bot_only=installation_store_bot_only, token_rotation_expiration_minutes=token_rotation_expiration_minutes, # state parameter related configurations state_store=state_store, state_cookie_name=state_cookie_name, state_expiration_seconds=state_expiration_seconds, ), ) # ----------------------------- # Installation # ----------------------------- def handle_installation(self, request: BoltRequest) -> BoltResponse: set_cookie_value: Optional[str] = None url = self.build_authorize_url("", request) if self.settings.state_validation_enabled is True: state = self.issue_new_state(request) url = self.build_authorize_url(state, request) set_cookie_value = self.settings.state_utils.build_set_cookie_for_new_state(state) if self.settings.install_page_rendering_enabled: html = self.build_install_page_html(url, request) return BoltResponse( status=200, body=html, headers=self.append_set_cookie_headers( {"Content-Type": "text/html; charset=utf-8"}, set_cookie_value, ), ) else: return BoltResponse( status=302, body="", headers=self.append_set_cookie_headers( {"Content-Type": "text/html; charset=utf-8", "Location": url}, set_cookie_value, ), ) # ---------------------- # Internal methods for Installation def issue_new_state(self, request: BoltRequest) -> str: return self.settings.state_store.issue() def build_authorize_url(self, state: str, request: BoltRequest) -> str: team_ids: Optional[Sequence[str]] = request.query.get("team") return self.settings.authorize_url_generator.generate( state=state, team=team_ids[0] if team_ids is not None else None, ) def build_install_page_html(self, url: str, request: BoltRequest) -> str: return _build_default_install_page_html(url) def append_set_cookie_headers(self, headers: dict, set_cookie_value: Optional[str]): if set_cookie_value is not None: headers["Set-Cookie"] = [set_cookie_value] return headers # ----------------------------- # Callback # ----------------------------- def handle_callback(self, request: BoltRequest) -> BoltResponse: # failure due to end-user's cancellation or invalid redirection to slack.com error = request.query.get("error", [None])[0] if error is not None: return self.failure_handler( FailureArgs( request=request, reason=error, suggested_status_code=200, settings=self.settings, default=self.default_callback_options, ) ) # state parameter verification if self.settings.state_validation_enabled is True: state = request.query.get("state", [None])[0] if not self.settings.state_utils.is_valid_browser(state, request.headers): return self.failure_handler( FailureArgs( request=request, reason="invalid_browser", suggested_status_code=400, settings=self.settings, default=self.default_callback_options, ) ) valid_state_consumed = self.settings.state_store.consume(state) # type: ignore[arg-type] if not valid_state_consumed: return self.failure_handler( FailureArgs( request=request, reason="invalid_state", suggested_status_code=401, settings=self.settings, default=self.default_callback_options, ) ) # run installation code = request.query.get("code", [None])[0] if code is None: return self.failure_handler( FailureArgs( request=request, reason="missing_code", suggested_status_code=401, settings=self.settings, default=self.default_callback_options, ) ) installation = self.run_installation(code) if installation is None: # failed to run installation with the code return self.failure_handler( FailureArgs( request=request, reason="invalid_code", suggested_status_code=401, settings=self.settings, default=self.default_callback_options, ) ) # persist the installation try: self.store_installation(request, installation) except BoltError as err: return self.failure_handler( FailureArgs( request=request, reason="storage_error", error=err, suggested_status_code=500, settings=self.settings, default=self.default_callback_options, ) ) # display a successful completion page to the end-user return self.success_handler( SuccessArgs( request=request, installation=installation, settings=self.settings, default=self.default_callback_options, ) ) # ---------------------- # Internal methods for Callback def run_installation(self, code: str) -> Optional[Installation]: try: oauth_response: SlackResponse = self.client.oauth_v2_access( code=code, client_id=self.settings.client_id, client_secret=self.settings.client_secret, redirect_uri=self.settings.redirect_uri, # can be None ) installed_enterprise: Dict[str, str] = oauth_response.get("enterprise") or {} is_enterprise_install: bool = oauth_response.get("is_enterprise_install") or False installed_team: Dict[str, str] = oauth_response.get("team") or {} installer: Dict[str, str] = oauth_response.get("authed_user") or {} incoming_webhook: Dict[str, str] = oauth_response.get("incoming_webhook") or {} bot_token: Optional[str] = oauth_response.get("access_token") # NOTE: oauth.v2.access doesn't include bot_id in response bot_id: Optional[str] = None enterprise_url: Optional[str] = None if bot_token is not None: auth_test = self.client.auth_test(token=bot_token) bot_id = auth_test["bot_id"] if is_enterprise_install is True: enterprise_url = auth_test.get("url") return Installation( app_id=oauth_response.get("app_id"), enterprise_id=installed_enterprise.get("id"), enterprise_name=installed_enterprise.get("name"), enterprise_url=enterprise_url, team_id=installed_team.get("id"), team_name=installed_team.get("name"), bot_token=bot_token, bot_id=bot_id, bot_user_id=oauth_response.get("bot_user_id"), bot_scopes=oauth_response.get("scope"), # type: ignore[arg-type] # comma-separated string bot_refresh_token=oauth_response.get("refresh_token"), # since v1.7 bot_token_expires_in=oauth_response.get("expires_in"), # since v1.7 user_id=installer.get("id"), # type: ignore[arg-type] user_token=installer.get("access_token"), user_scopes=installer.get("scope"), # type: ignore[arg-type] # comma-separated string user_refresh_token=installer.get("refresh_token"), # since v1.7 user_token_expires_in=installer.get("expires_in"), # type: ignore[arg-type] # since v1.7 incoming_webhook_url=incoming_webhook.get("url"), incoming_webhook_channel=incoming_webhook.get("channel"), incoming_webhook_channel_id=incoming_webhook.get("channel_id"), incoming_webhook_configuration_url=incoming_webhook.get("configuration_url"), is_enterprise_install=is_enterprise_install, token_type=oauth_response.get("token_type"), ) except SlackApiError as e: message = f"Failed to fetch oauth.v2.access result with code: {code} - error: {e}" self.logger.warning(message) return None def store_installation(self, request: BoltRequest, installation: Installation): # may raise BoltError self.settings.installation_store.save(installation) ``` The module to run the Slack app installation flow (OAuth flow). ## Args **`client`** The `slack_sdk.web.WebClient` instance. **`logger`** The logger. **`settings`** OAuth settings to configure this module. ### Subclasses * [LambdaS3OAuthFlow](../adapter/aws_lambda/lambda_s3_oauth_flow.html#slack_bolt.adapter.aws_lambda.lambda_s3_oauth_flow.LambdaS3OAuthFlow "slack_bolt.adapter.aws_lambda.lambda_s3_oauth_flow.LambdaS3OAuthFlow") ### Class variables `var client_id : str` The type of the None singleton. `var failure_handler : Callable[[[FailureArgs](callback_options.html#slack_bolt.oauth.callback_options.FailureArgs "slack_bolt.oauth.callback_options.FailureArgs")], [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse")]` The type of the None singleton. `var install_path : str` The type of the None singleton. `var redirect_uri : str | None` The type of the None singleton. `var redirect_uri_path : str` The type of the None singleton. `var settings : [OAuthSettings](oauth_settings.html#slack_bolt.oauth.oauth_settings.OAuthSettings "slack_bolt.oauth.oauth_settings.OAuthSettings")` The type of the None singleton. `var success_handler : Callable[[[SuccessArgs](callback_options.html#slack_bolt.oauth.callback_options.SuccessArgs "slack_bolt.oauth.callback_options.SuccessArgs")], [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse")]` The type of the None singleton. ### Static methods `def sqlite3(database: str, client_id: str | None = None, client_secret: str | None = None, scopes: Sequence[str] | None = None, user_scopes: Sequence[str] | None = None, redirect_uri: str | None = None, install_path: str | None = None, redirect_uri_path: str | None = None, callback_options: [CallbackOptions](callback_options.html#slack_bolt.oauth.callback_options.CallbackOptions "slack_bolt.oauth.callback_options.CallbackOptions") | None = None, success_url: str | None = None, failure_url: str | None = None, authorization_url: str | None = None, state_cookie_name: str = 'slack-app-oauth-state', state_expiration_seconds: int = 600, installation_store_bot_only: bool = False, token_rotation_expiration_minutes: int = 120, client: slack_sdk.web.client.WebClient | None = None, logger: logging.Logger | None = None) ‑> [OAuthFlow](oauth_flow.html#slack_bolt.oauth.oauth_flow.OAuthFlow "slack_bolt.oauth.oauth_flow.OAuthFlow")` ### Instance variables `prop client : slack_sdk.web.client.WebClient` Expand source code ``` @property def client(self) -> WebClient: if self._client is None: self._client = create_web_client(logger=self.logger) return self._client ``` `prop logger : logging.Logger` Expand source code ``` @property def logger(self) -> Logger: if self._logger is None: self._logger = logging.getLogger(__name__) return self._logger ``` ### Methods `def append_set_cookie_headers(self, headers: dict, set_cookie_value: str | None)` Expand source code ``` def append_set_cookie_headers(self, headers: dict, set_cookie_value: Optional[str]): if set_cookie_value is not None: headers["Set-Cookie"] = [set_cookie_value] return headers ``` `def build_authorize_url(self, state: str, request: [BoltRequest](../request/request.html#slack_bolt.request.request.BoltRequest "slack_bolt.request.request.BoltRequest")) ‑> str` Expand source code ``` def build_authorize_url(self, state: str, request: BoltRequest) -> str: team_ids: Optional[Sequence[str]] = request.query.get("team") return self.settings.authorize_url_generator.generate( state=state, team=team_ids[0] if team_ids is not None else None, ) ``` `def build_install_page_html(self, url: str, request: [BoltRequest](../request/request.html#slack_bolt.request.request.BoltRequest "slack_bolt.request.request.BoltRequest")) ‑> str` Expand source code ``` def build_install_page_html(self, url: str, request: BoltRequest) -> str: return _build_default_install_page_html(url) ``` `def handle_callback(self, request: [BoltRequest](../request/request.html#slack_bolt.request.request.BoltRequest "slack_bolt.request.request.BoltRequest")) ‑> [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse")` Expand source code ``` def handle_callback(self, request: BoltRequest) -> BoltResponse: # failure due to end-user's cancellation or invalid redirection to slack.com error = request.query.get("error", [None])[0] if error is not None: return self.failure_handler( FailureArgs( request=request, reason=error, suggested_status_code=200, settings=self.settings, default=self.default_callback_options, ) ) # state parameter verification if self.settings.state_validation_enabled is True: state = request.query.get("state", [None])[0] if not self.settings.state_utils.is_valid_browser(state, request.headers): return self.failure_handler( FailureArgs( request=request, reason="invalid_browser", suggested_status_code=400, settings=self.settings, default=self.default_callback_options, ) ) valid_state_consumed = self.settings.state_store.consume(state) # type: ignore[arg-type] if not valid_state_consumed: return self.failure_handler( FailureArgs( request=request, reason="invalid_state", suggested_status_code=401, settings=self.settings, default=self.default_callback_options, ) ) # run installation code = request.query.get("code", [None])[0] if code is None: return self.failure_handler( FailureArgs( request=request, reason="missing_code", suggested_status_code=401, settings=self.settings, default=self.default_callback_options, ) ) installation = self.run_installation(code) if installation is None: # failed to run installation with the code return self.failure_handler( FailureArgs( request=request, reason="invalid_code", suggested_status_code=401, settings=self.settings, default=self.default_callback_options, ) ) # persist the installation try: self.store_installation(request, installation) except BoltError as err: return self.failure_handler( FailureArgs( request=request, reason="storage_error", error=err, suggested_status_code=500, settings=self.settings, default=self.default_callback_options, ) ) # display a successful completion page to the end-user return self.success_handler( SuccessArgs( request=request, installation=installation, settings=self.settings, default=self.default_callback_options, ) ) ``` `def handle_installation(self, request: [BoltRequest](../request/request.html#slack_bolt.request.request.BoltRequest "slack_bolt.request.request.BoltRequest")) ‑> [BoltResponse](../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse")` Expand source code ``` def handle_installation(self, request: BoltRequest) -> BoltResponse: set_cookie_value: Optional[str] = None url = self.build_authorize_url("", request) if self.settings.state_validation_enabled is True: state = self.issue_new_state(request) url = self.build_authorize_url(state, request) set_cookie_value = self.settings.state_utils.build_set_cookie_for_new_state(state) if self.settings.install_page_rendering_enabled: html = self.build_install_page_html(url, request) return BoltResponse( status=200, body=html, headers=self.append_set_cookie_headers( {"Content-Type": "text/html; charset=utf-8"}, set_cookie_value, ), ) else: return BoltResponse( status=302, body="", headers=self.append_set_cookie_headers( {"Content-Type": "text/html; charset=utf-8", "Location": url}, set_cookie_value, ), ) ``` `def issue_new_state(self, request: [BoltRequest](../request/request.html#slack_bolt.request.request.BoltRequest "slack_bolt.request.request.BoltRequest")) ‑> str` Expand source code ``` def issue_new_state(self, request: BoltRequest) -> str: return self.settings.state_store.issue() ``` `def run_installation(self, code: str) ‑> slack_sdk.oauth.installation_store.models.installation.Installation | None` Expand source code ``` def run_installation(self, code: str) -> Optional[Installation]: try: oauth_response: SlackResponse = self.client.oauth_v2_access( code=code, client_id=self.settings.client_id, client_secret=self.settings.client_secret, redirect_uri=self.settings.redirect_uri, # can be None ) installed_enterprise: Dict[str, str] = oauth_response.get("enterprise") or {} is_enterprise_install: bool = oauth_response.get("is_enterprise_install") or False installed_team: Dict[str, str] = oauth_response.get("team") or {} installer: Dict[str, str] = oauth_response.get("authed_user") or {} incoming_webhook: Dict[str, str] = oauth_response.get("incoming_webhook") or {} bot_token: Optional[str] = oauth_response.get("access_token") # NOTE: oauth.v2.access doesn't include bot_id in response bot_id: Optional[str] = None enterprise_url: Optional[str] = None if bot_token is not None: auth_test = self.client.auth_test(token=bot_token) bot_id = auth_test["bot_id"] if is_enterprise_install is True: enterprise_url = auth_test.get("url") return Installation( app_id=oauth_response.get("app_id"), enterprise_id=installed_enterprise.get("id"), enterprise_name=installed_enterprise.get("name"), enterprise_url=enterprise_url, team_id=installed_team.get("id"), team_name=installed_team.get("name"), bot_token=bot_token, bot_id=bot_id, bot_user_id=oauth_response.get("bot_user_id"), bot_scopes=oauth_response.get("scope"), # type: ignore[arg-type] # comma-separated string bot_refresh_token=oauth_response.get("refresh_token"), # since v1.7 bot_token_expires_in=oauth_response.get("expires_in"), # since v1.7 user_id=installer.get("id"), # type: ignore[arg-type] user_token=installer.get("access_token"), user_scopes=installer.get("scope"), # type: ignore[arg-type] # comma-separated string user_refresh_token=installer.get("refresh_token"), # since v1.7 user_token_expires_in=installer.get("expires_in"), # type: ignore[arg-type] # since v1.7 incoming_webhook_url=incoming_webhook.get("url"), incoming_webhook_channel=incoming_webhook.get("channel"), incoming_webhook_channel_id=incoming_webhook.get("channel_id"), incoming_webhook_configuration_url=incoming_webhook.get("configuration_url"), is_enterprise_install=is_enterprise_install, token_type=oauth_response.get("token_type"), ) except SlackApiError as e: message = f"Failed to fetch oauth.v2.access result with code: {code} - error: {e}" self.logger.warning(message) return None ``` `def store_installation(self, request: [BoltRequest](../request/request.html#slack_bolt.request.request.BoltRequest "slack_bolt.request.request.BoltRequest"), installation: slack_sdk.oauth.installation_store.models.installation.Installation)` Expand source code ``` def store_installation(self, request: BoltRequest, installation: Installation): # may raise BoltError self.settings.installation_store.save(installation) ``` --- Source: https://docs.slack.dev/tools/bolt-python/reference/request # Module slack_bolt.request Incoming request from Slack through either HTTP request or Socket Mode connection. Refer to [https://docs.slack.dev/apis/events-api/](https://docs.slack.dev/apis/events-api/) for the two types of connections. This interface encapsulates the difference between the two. ## Sub-modules `[slack_bolt.request.async_internals](async_internals.html "slack_bolt.request.async_internals")` `[slack_bolt.request.async_request](async_request.html "slack_bolt.request.async_request")` `[slack_bolt.request.internals](internals.html "slack_bolt.request.internals")` `[slack_bolt.request.payload_utils](payload_utils.html "slack_bolt.request.payload_utils")` `[slack_bolt.request.request](request.html "slack_bolt.request.request")` ## Classes `class BoltRequest (*, body: str | dict, query: str | Dict[str, str] | Dict[str, Sequence[str]] | None = None, headers: Dict[str, str | Sequence[str]] | None = None, context: Dict[str, Any] | None = None, mode: str = 'http')` Expand source code ``` class BoltRequest: raw_body: str query: Dict[str, Sequence[str]] headers: Dict[str, Sequence[str]] content_type: Optional[str] body: Dict[str, Any] context: BoltContext lazy_only: bool lazy_function_name: Optional[str] mode: str # either "http" or "socket_mode" def __init__( self, *, body: Union[str, dict], query: Optional[Union[str, Dict[str, str], Dict[str, Sequence[str]]]] = None, headers: Optional[Dict[str, Union[str, Sequence[str]]]] = None, context: Optional[Dict[str, Any]] = None, mode: str = "http", # either "http" or "socket_mode" ): """Request to a Bolt app. Args: body: The raw request body (only plain text is supported for "http" mode) query: The query string data in any data format. headers: The request headers. context: The context in this request. mode: The mode used for this request. (either "http" or "socket_mode") """ if mode == "http": # HTTP Mode if body is not None and not isinstance(body, str): raise BoltError(error_message_raw_body_required_in_http_mode()) self.raw_body = body if body is not None else "" else: # Socket Mode if body is not None and isinstance(body, str): self.raw_body = body else: # We don't convert the dict value to str # as doing so does not guarantee to keep the original structure/format. self.raw_body = "" self.query = parse_query(query) self.headers = build_normalized_headers(headers) self.content_type = extract_content_type(self.headers) if isinstance(body, str): self.body = parse_body(self.raw_body, self.content_type) elif isinstance(body, dict): self.body = body else: self.body = {} self.context = build_context(BoltContext(context if context else {}), self.body) self.lazy_only = bool(self.headers.get("x-slack-bolt-lazy-only", [False])[0]) self.lazy_function_name = self.headers.get("x-slack-bolt-lazy-function-name", [None])[0] self.mode = mode def to_copyable(self) -> "BoltRequest": body: Union[str, dict] = self.raw_body if self.mode == "http" else self.body return BoltRequest( body=body, query=self.query, headers=self.headers, context=self.context.to_copyable(), mode=self.mode, ) ``` Request to a Bolt app. ## Args **`body`** The raw request body (only plain text is supported for "http" mode) **`query`** The query string data in any data format. **`headers`** The request headers. **`context`** The context in this request. **`mode`** The mode used for this request. (either "http" or "socket\_mode") ### Class variables `var body : Dict[str, Any]` The type of the None singleton. `var content_type : str | None` The type of the None singleton. `var context : [BoltContext](../context/context.html#slack_bolt.context.context.BoltContext "slack_bolt.context.context.BoltContext")` The type of the None singleton. `var headers : Dict[str, Sequence[str]]` The type of the None singleton. `var lazy_function_name : str | None` The type of the None singleton. `var lazy_only : bool` The type of the None singleton. `var mode : str` The type of the None singleton. `var query : Dict[str, Sequence[str]]` The type of the None singleton. `var raw_body : str` The type of the None singleton. ### Methods `def to_copyable(self) ‑> [BoltRequest](request.html#slack_bolt.request.request.BoltRequest "slack_bolt.request.request.BoltRequest")` Expand source code ``` def to_copyable(self) -> "BoltRequest": body: Union[str, dict] = self.raw_body if self.mode == "http" else self.body return BoltRequest( body=body, query=self.query, headers=self.headers, context=self.context.to_copyable(), mode=self.mode, ) ``` --- Source: https://docs.slack.dev/tools/bolt-python/reference/response # Module slack_bolt.response This interface represents Bolt's synchronous response to Slack. In Socket Mode, the response data can be transformed to a WebSocket message. In the HTTP endpoint mode, the response data becomes an HTTP response data. Refer to [https://docs.slack.dev/apis/events-api/](https://docs.slack.dev/apis/events-api/) for the two types of connections. ## Sub-modules `[slack_bolt.response.response](response.html "slack_bolt.response.response")` ## Classes `class BoltResponse (*, status: int, body: str | dict = '', headers: Dict[str, str | Sequence[str]] | None = None)` Expand source code ``` class BoltResponse: status: int body: str headers: Dict[str, Sequence[str]] def __init__( self, *, status: int, body: Union[str, dict] = "", headers: Optional[Dict[str, Union[str, Sequence[str]]]] = None, ): """The response from a Bolt app. Args: status: HTTP status code body: The response body (dict and str are supported) headers: The response headers. """ self.status: int = status self.body: str = json.dumps(body) if isinstance(body, dict) else body self.headers: Dict[str, Sequence[str]] = {} if headers is not None: for name, value in headers.items(): if value is None: continue if isinstance(value, list): self.headers[name.lower()] = value elif isinstance(value, set): self.headers[name.lower()] = list(value) else: self.headers[name.lower()] = [str(value)] if "content-type" not in self.headers.keys(): if self.body and self.body.startswith("{"): self.headers["content-type"] = ["application/json;charset=utf-8"] else: self.headers["content-type"] = ["text/plain;charset=utf-8"] def first_headers(self) -> Dict[str, str]: return {k: list(v)[0] for k, v in self.headers.items()} def first_headers_without_set_cookie(self) -> Dict[str, str]: return {k: list(v)[0] for k, v in self.headers.items() if k != "set-cookie"} def cookies(self) -> Sequence[SimpleCookie]: header_values = self.headers.get("set-cookie", []) return [self._to_simple_cookie(v) for v in header_values] @staticmethod def _to_simple_cookie(header_value: str) -> SimpleCookie: c = SimpleCookie() c.load(header_value) return c ``` The response from a Bolt app. ## Args **`status`** HTTP status code **`body`** The response body (dict and str are supported) **`headers`** The response headers. ### Class variables `var body : str` The type of the None singleton. `var headers : Dict[str, Sequence[str]]` The type of the None singleton. `var status : int` The type of the None singleton. ### Methods `def cookies(self) ‑> Sequence[http.cookies.SimpleCookie]` Expand source code ``` def cookies(self) -> Sequence[SimpleCookie]: header_values = self.headers.get("set-cookie", []) return [self._to_simple_cookie(v) for v in header_values] ``` `def first_headers(self) ‑> Dict[str, str]` Expand source code ``` def first_headers(self) -> Dict[str, str]: return {k: list(v)[0] for k, v in self.headers.items()} ``` `def first_headers_without_set_cookie(self) ‑> Dict[str, str]` Expand source code ``` def first_headers_without_set_cookie(self) -> Dict[str, str]: return {k: list(v)[0] for k, v in self.headers.items() if k != "set-cookie"} ``` --- Source: https://docs.slack.dev/tools/bolt-python/reference/util # Module slack_bolt.util Internal utilities for the Bolt framework. ## Sub-modules `[slack_bolt.util.async_utils](async_utils.html "slack_bolt.util.async_utils")` `[slack_bolt.util.utils](utils.html "slack_bolt.util.utils")` --- Source: https://docs.slack.dev/tools/bolt-python/reference/workflows # Module slack_bolt.workflows Steps from apps enables developers to build their own steps. Check the following API documents first: * `[slack_bolt.workflows.step.step](step/step.html "slack_bolt.workflows.step.step")` * `[slack_bolt.workflows.step.utilities](step/utilities/index.html "slack_bolt.workflows.step.utilities")` * `[slack_bolt.workflows.step.async_step](step/async_step.html "slack_bolt.workflows.step.async_step")` (if you use asyncio-based `AsyncApp`) Refer to [https://docs.slack.dev/legacy/legacy-steps-from-apps/](https://docs.slack.dev/legacy/legacy-steps-from-apps/) for details. ## Sub-modules `[slack_bolt.workflows.step](step/index.html "slack_bolt.workflows.step")` --- Source: https://docs.slack.dev/tools/bolt-python/reference/workflows/step # Module slack_bolt.workflows.step ## Sub-modules `[slack_bolt.workflows.step.async_step](async_step.html "slack_bolt.workflows.step.async_step")` `[slack_bolt.workflows.step.async_step_middleware](async_step_middleware.html "slack_bolt.workflows.step.async_step_middleware")` `[slack_bolt.workflows.step.internals](internals.html "slack_bolt.workflows.step.internals")` `[slack_bolt.workflows.step.step](step.html "slack_bolt.workflows.step.step")` `[slack_bolt.workflows.step.step_middleware](step_middleware.html "slack_bolt.workflows.step.step_middleware")` `[slack_bolt.workflows.step.utilities](utilities/index.html "slack_bolt.workflows.step.utilities")` Utilities specific to steps from apps … ## Classes `class Complete (*, client: slack_sdk.web.client.WebClient, body: dict)` Expand source code ``` class Complete: """`complete()` utility to tell Slack the completion of a step from app execution. def execute(step, complete, fail): inputs = step["inputs"] # if everything was successful outputs = { "task_name": inputs["task_name"]["value"], "task_description": inputs["task_description"]["value"], } complete(outputs=outputs) ws = WorkflowStep( callback_id="add_task", edit=edit, save=save, execute=execute, ) app.step(ws) This utility is a thin wrapper of workflows.stepCompleted API method. Refer to https://api.slack.com/methods/workflows.stepCompleted for details. """ def __init__(self, *, client: WebClient, body: dict): self.client = client self.body = body def __call__(self, **kwargs) -> None: self.client.workflows_stepCompleted( workflow_step_execute_id=self.body["event"]["workflow_step"]["workflow_step_execute_id"], **kwargs, ) ``` `complete()` utility to tell Slack the completion of a step from app execution. ``` def execute(step, complete, fail): inputs = step["inputs"] # if everything was successful outputs = { "task_name": inputs["task_name"]["value"], "task_description": inputs["task_description"]["value"], } complete(outputs=outputs) ws = WorkflowStep( callback_id="add_task", edit=edit, save=save, execute=execute, ) app.step(ws) ``` This utility is a thin wrapper of workflows.stepCompleted API method. Refer to [https://api.slack.com/methods/workflows.stepCompleted](https://api.slack.com/methods/workflows.stepCompleted) for details. `class Configure (*, callback_id: str, client: slack_sdk.web.client.WebClient, body: dict)` Expand source code ``` class Configure: """`configure()` utility to send the modal view in Workflow Builder. def edit(ack, step, configure): ack() blocks = [ { "type": "input", "block_id": "task_name_input", "element": { "type": "plain_text_input", "action_id": "name", "placeholder": {"type": "plain_text", "text": "Add a task name"}, }, "label": {"type": "plain_text", "text": "Task name"}, }, ] configure(blocks=blocks) ws = WorkflowStep( callback_id="add_task", edit=edit, save=save, execute=execute, ) app.step(ws) Refer to https://docs.slack.dev/legacy/legacy-steps-from-apps/ for details. """ def __init__(self, *, callback_id: str, client: WebClient, body: dict): self.callback_id = callback_id self.client = client self.body = body def __call__(self, *, blocks: Optional[Sequence[Union[dict, Block]]] = None, **kwargs) -> None: self.client.views_open( trigger_id=self.body["trigger_id"], view={ "type": "workflow_step", "callback_id": self.callback_id, "blocks": blocks, **kwargs, }, ) ``` `configure()` utility to send the modal view in Workflow Builder. ``` def edit(ack, step, configure): ack() blocks = [ { "type": "input", "block_id": "task_name_input", "element": { "type": "plain_text_input", "action_id": "name", "placeholder": {"type": "plain_text", "text": "Add a task name"}, }, "label": {"type": "plain_text", "text": "Task name"}, }, ] configure(blocks=blocks) ws = WorkflowStep( callback_id="add_task", edit=edit, save=save, execute=execute, ) app.step(ws) ``` Refer to [https://docs.slack.dev/legacy/legacy-steps-from-apps/](https://docs.slack.dev/legacy/legacy-steps-from-apps/) for details. `class Fail (*, client: slack_sdk.web.client.WebClient, body: dict)` Expand source code ``` class Fail: """`fail()` utility to tell Slack the execution failure of a step from app. def execute(step, complete, fail): inputs = step["inputs"] # if something went wrong error = {"message": "Just testing step failure!"} fail(error=error) ws = WorkflowStep( callback_id="add_task", edit=edit, save=save, execute=execute, ) app.step(ws) This utility is a thin wrapper of workflows.stepFailed API method. Refer to https://api.slack.com/methods/workflows.stepFailed for details. """ def __init__(self, *, client: WebClient, body: dict): self.client = client self.body = body def __call__( self, *, error: dict, ) -> None: self.client.workflows_stepFailed( workflow_step_execute_id=self.body["event"]["workflow_step"]["workflow_step_execute_id"], error=error, ) ``` `fail()` utility to tell Slack the execution failure of a step from app. ``` def execute(step, complete, fail): inputs = step["inputs"] # if something went wrong error = {"message": "Just testing step failure!"} fail(error=error) ws = WorkflowStep( callback_id="add_task", edit=edit, save=save, execute=execute, ) app.step(ws) ``` This utility is a thin wrapper of workflows.stepFailed API method. Refer to [https://api.slack.com/methods/workflows.stepFailed](https://api.slack.com/methods/workflows.stepFailed) for details. `class Update (*, client: slack_sdk.web.client.WebClient, body: dict)` Expand source code ``` class Update: """`update()` utility to tell Slack the processing results of a `save` listener. def save(ack, view, update): ack() values = view["state"]["values"] task_name = values["task_name_input"]["name"] task_description = values["task_description_input"]["description"] inputs = { "task_name": {"value": task_name["value"]}, "task_description": {"value": task_description["value"]} } outputs = [ { "type": "text", "name": "task_name", "label": "Task name", }, { "type": "text", "name": "task_description", "label": "Task description", } ] update(inputs=inputs, outputs=outputs) ws = WorkflowStep( callback_id="add_task", edit=edit, save=save, execute=execute, ) app.step(ws) This utility is a thin wrapper of workflows.stepFailed API method. Refer to https://api.slack.com/methods/workflows.updateStep for details. """ def __init__(self, *, client: WebClient, body: dict): self.client = client self.body = body def __call__(self, **kwargs) -> None: self.client.workflows_updateStep( workflow_step_edit_id=self.body["workflow_step"]["workflow_step_edit_id"], **kwargs, ) ``` `update()` utility to tell Slack the processing results of a `save` listener. ``` def save(ack, view, update): ack() values = view["state"]["values"] task_name = values["task_name_input"]["name"] task_description = values["task_description_input"]["description"] inputs = { "task_name": {"value": task_name["value"]}, "task_description": {"value": task_description["value"]} } outputs = [ { "type": "text", "name": "task_name", "label": "Task name", }, { "type": "text", "name": "task_description", "label": "Task description", } ] update(inputs=inputs, outputs=outputs) ws = WorkflowStep( callback_id="add_task", edit=edit, save=save, execute=execute, ) app.step(ws) ``` This utility is a thin wrapper of workflows.stepFailed API method. Refer to [https://api.slack.com/methods/workflows.updateStep](https://api.slack.com/methods/workflows.updateStep) for details. `class WorkflowStep (*, callback_id: str | Pattern, edit: Callable[..., [BoltResponse](../../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | [Listener](../../listener/listener.html#slack_bolt.listener.listener.Listener "slack_bolt.listener.listener.Listener") | Sequence[Callable], save: Callable[..., [BoltResponse](../../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | [Listener](../../listener/listener.html#slack_bolt.listener.listener.Listener "slack_bolt.listener.listener.Listener") | Sequence[Callable], execute: Callable[..., [BoltResponse](../../response/response.html#slack_bolt.response.response.BoltResponse "slack_bolt.response.response.BoltResponse") | None] | [Listener](../../listener/listener.html#slack_bolt.listener.listener.Listener "slack_bolt.listener.listener.Listener") | Sequence[Callable], app_name: str | None = None, base_logger: logging.Logger | None = None)` Expand source code ``` class WorkflowStep: callback_id: Union[str, Pattern] """The Callback ID of the step from app""" edit: Listener """`edit` listener, which displays a modal in Workflow Builder""" save: Listener """`save` listener, which accepts workflow creator's data submission in Workflow Builder""" execute: Listener """`execute` listener, which processes step from app execution""" def __init__( self, *, callback_id: Union[str, Pattern], edit: Union[Callable[..., Optional[BoltResponse]], Listener, Sequence[Callable]], save: Union[Callable[..., Optional[BoltResponse]], Listener, Sequence[Callable]], execute: Union[Callable[..., Optional[BoltResponse]], Listener, Sequence[Callable]], app_name: Optional[str] = None, base_logger: Optional[Logger] = None, ): """ Deprecated: Steps from apps for legacy workflows are now deprecated. Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ Args: callback_id: The callback_id for this step from app edit: Either a single function or a list of functions for opening a modal in the builder UI When it's a list, the first one is responsible for ack() while the rest are lazy listeners. save: Either a single function or a list of functions for handling modal interactions in the builder UI When it's a list, the first one is responsible for ack() while the rest are lazy listeners. execute: Either a single function or a list of functions for handling step from app executions When it's a list, the first one is responsible for ack() while the rest are lazy listeners. app_name: The app name that can be mainly used for logging base_logger: The logger instance that can be used as a template when creating this step's logger """ self.callback_id = callback_id app_name = app_name or __name__ self.edit = self.build_listener( callback_id=callback_id, app_name=app_name, listener_or_functions=edit, name="edit", base_logger=base_logger, ) self.save = self.build_listener( callback_id=callback_id, app_name=app_name, listener_or_functions=save, name="save", base_logger=base_logger, ) self.execute = self.build_listener( callback_id=callback_id, app_name=app_name, listener_or_functions=execute, name="execute", base_logger=base_logger, ) @classmethod def builder(cls, callback_id: Union[str, Pattern], base_logger: Optional[Logger] = None) -> WorkflowStepBuilder: """ Deprecated: Steps from apps for legacy workflows are now deprecated. Use new custom steps: https://docs.slack.dev/workflows/workflow-steps/ """ return WorkflowStepBuilder( callback_id, base_logger=base_logger, ) @classmethod def build_listener( cls, callback_id: Union[str, Pattern], app_name: str, listener_or_functions: Union[Listener, Callable, List[Callable]], name: str, matchers: Optional[List[ListenerMatcher]] = None, middleware: Optional[List[Middleware]] = None, base_logger: Optional[Logger] = None, ) -> Listener: if listener_or_functions is None: raise BoltError(f"{name} listener is required (callback_id: {callback_id})") if isinstance(listener_or_functions, Callable): listener_or_functions = [listener_or_functions] if isinstance(listener_or_functions, Listener): return listener_or_functions elif isinstance(listener_or_functions, list): matchers = matchers if matchers else [] matchers.insert( 0, cls._build_primary_matcher( name, callback_id, base_logger=base_logger, ), ) middleware = middleware if middleware else [] middleware.insert( 0, cls._build_single_middleware( name, callback_id, base_logger=base_logger, ), ) functions = listener_or_functions ack_function = functions.pop(0) return CustomListener( app_name=app_name, matchers=matchers, middleware=middleware, ack_function=ack_function, lazy_functions=functions, auto_acknowledgement=name == "execute", base_logger=base_logger, ) else: raise BoltError(f"Invalid {name} listener: {type(listener_or_functions)} detected (callback_id: {callback_id})") @classmethod def _build_primary_matcher( cls, name: str, callback_id: Union[str, Pattern], base_logger: Optional[Logger] = None, ) -> ListenerMatcher: if name == "edit": return workflow_step_edit(callback_id, base_logger=base_logger) elif name == "save": return workflow_step_save(callback_id, base_logger=base_logger) elif name == "execute": return workflow_step_execute(callback_id, base_logger=base_logger) else: raise ValueError(f"Invalid name {name}") @classmethod def _build_single_middleware( cls, name: str, callback_id: Union[str, Pattern], base_logger: Optional[Logger] = None, ) -> Middleware: if name == "edit": return _build_edit_listener_middleware(callback_id, base_logger=base_logger) elif name == "save": return _build_save_listener_middleware(base_logger=base_logger) elif name == "execute": return _build_execute_listener_middleware(base_logger=base_logger) else: raise ValueError(f"Invalid name {name}") ``` ## Deprecated Steps from apps for legacy workflows are now deprecated. Use new custom steps: [https://docs.slack.dev/workflows/workflow-steps/](https://docs.slack.dev/workflows/workflow-steps/) ## Args **`callback_id`** The callback\_id for this step from app **`edit`** Either a single function or a list of functions for opening a modal in the builder UI When it's a list, the first one is responsible for ack() while the rest are lazy listeners. **`save`** Either a single function or a list of functions for handling modal interactions in the builder UI When it's a list, the first one is responsible for ack() while the rest are lazy listeners. **`execute`** Either a single function or a list of functions for handling step from app executions When it's a list, the first one is responsible for ack() while the rest are lazy listeners. **`app_name`** The app name that can be mainly used for logging **`base_logger`** The logger instance that can be used as a template when creating this step's logger ### Class variables `var callback_id : str | Pattern` The Callback ID of the step from app `var edit : [Listener](../../listener/listener.html#slack_bolt.listener.listener.Listener "slack_bolt.listener.listener.Listener")` `edit` listener, which displays a modal in Workflow Builder `var execute : [Listener](../../listener/listener.html#slack_bolt.listener.listener.Listener "slack_bolt.listener.listener.Listener")` `execute` listener, which processes step from app execution `var save : [Listener](../../listener/listener.html#slack_bolt.listener.listener.Listener "slack_bolt.listener.listener.Listener")` `save` listener, which accepts workflow creator's data submission in Workflow Builder ### Static methods `def build_listener(callback_id: str | Pattern, app_name: str, listener_or_functions: [Listener](../../listener/listener.html#slack_bolt.listener.listener.Listener "slack_bolt.listener.listener.Listener") | Callable | List[Callable], name: str, matchers: List[[ListenerMatcher](../../listener_matcher/listener_matcher.html#slack_bolt.listener_matcher.listener_matcher.ListenerMatcher "slack_bolt.listener_matcher.listener_matcher.ListenerMatcher")] | None = None, middleware: List[[Middleware](../../middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")] | None = None, base_logger: logging.Logger | None = None) ‑> [Listener](../../listener/listener.html#slack_bolt.listener.listener.Listener "slack_bolt.listener.listener.Listener")` `def builder(callback_id: str | Pattern, base_logger: logging.Logger | None = None) ‑> [WorkflowStepBuilder](step.html#slack_bolt.workflows.step.step.WorkflowStepBuilder "slack_bolt.workflows.step.step.WorkflowStepBuilder")` ## Deprecated Steps from apps for legacy workflows are now deprecated. Use new custom steps: [https://docs.slack.dev/workflows/workflow-steps/](https://docs.slack.dev/workflows/workflow-steps/) `class WorkflowStepMiddleware (step: [WorkflowStep](step.html#slack_bolt.workflows.step.step.WorkflowStep "slack_bolt.workflows.step.step.WorkflowStep"))` Expand source code ``` class WorkflowStepMiddleware(Middleware): """Base middleware for step from app specific ones""" def __init__(self, step: WorkflowStep): self.step = step def process( self, *, req: BoltRequest, resp: BoltResponse, # As this method is not supposed to be invoked by bolt-python users, # the naming conflict with the built-in one affects # only the internals of this method next: Callable[[], BoltResponse], ) -> Optional[BoltResponse]: if self.step.edit.matches(req=req, resp=resp): resp = self._run(self.step.edit, req, resp) if resp is not None: return resp elif self.step.save.matches(req=req, resp=resp): resp = self._run(self.step.save, req, resp) if resp is not None: return resp elif self.step.execute.matches(req=req, resp=resp): resp = self._run(self.step.execute, req, resp) if resp is not None: return resp return next() @staticmethod def _run( listener: Listener, req: BoltRequest, resp: BoltResponse, ) -> Optional[BoltResponse]: resp, next_was_not_called = listener.run_middleware(req=req, resp=resp) if next_was_not_called: return None return req.context.listener_runner.run( request=req, response=resp, listener_name=get_name_for_callable(listener.ack_function), listener=listener, ) ``` Base middleware for step from app specific ones ### Ancestors * [Middleware](../../middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware") ### Inherited members * `**[Middleware](../../middleware/middleware.html#slack_bolt.middleware.middleware.Middleware "slack_bolt.middleware.middleware.Middleware")**`: * `[name](../../middleware/middleware.html#slack_bolt.middleware.middleware.Middleware.name "slack_bolt.middleware.middleware.Middleware.name")` * `[process](../../middleware/middleware.html#slack_bolt.middleware.middleware.Middleware.process "slack_bolt.middleware.middleware.Middleware.process")` --- Source: https://docs.slack.dev/tools/bolt-python/reference/workflows/step/utilities # Module slack_bolt.workflows.step.utilities Utilities specific to steps from apps. In steps from apps listeners, you can use a few specific listener/middleware arguments. ### edit listener * `[slack_bolt.workflows.step.utilities.configure](configure.html "slack_bolt.workflows.step.utilities.configure")` for building a modal view ### save listener * `[slack_bolt.workflows.step.utilities.update](update.html "slack_bolt.workflows.step.utilities.update")` for updating the step metadata ### execute listener * `[slack_bolt.workflows.step.utilities.fail](fail.html "slack_bolt.workflows.step.utilities.fail")` for notifying the execution failure to Slack * `[slack_bolt.workflows.step.utilities.complete](complete.html "slack_bolt.workflows.step.utilities.complete")` for notifying the execution completion to Slack For asyncio-based apps, refer to the corresponding `async` prefixed ones. ## Sub-modules `[slack_bolt.workflows.step.utilities.async_complete](async_complete.html "slack_bolt.workflows.step.utilities.async_complete")` `[slack_bolt.workflows.step.utilities.async_configure](async_configure.html "slack_bolt.workflows.step.utilities.async_configure")` `[slack_bolt.workflows.step.utilities.async_fail](async_fail.html "slack_bolt.workflows.step.utilities.async_fail")` `[slack_bolt.workflows.step.utilities.async_update](async_update.html "slack_bolt.workflows.step.utilities.async_update")` `[slack_bolt.workflows.step.utilities.complete](complete.html "slack_bolt.workflows.step.utilities.complete")` `[slack_bolt.workflows.step.utilities.configure](configure.html "slack_bolt.workflows.step.utilities.configure")` `[slack_bolt.workflows.step.utilities.fail](fail.html "slack_bolt.workflows.step.utilities.fail")` `[slack_bolt.workflows.step.utilities.update](update.html "slack_bolt.workflows.step.utilities.update")` --- Source: https://docs.slack.dev/tools/bolt-python/tutorial/ai-chatbot # AI Chatbot In this tutorial, you'll learn how to bring the power of AI into your Slack workspace using a chatbot called Bolty that uses Anthropic or OpenAI. With Bolty, users can: * send direct messages to Bolty and get AI-powered responses in response, * use the `/ask-bolty` slash command to ask Bolty questions, and * receive channel summaries when joining new channels. Intrigued? First, grab your tools by following the three steps below. MacOS/LinuxWindows 1 ### Install the Slack CLI Download the command-line tool for developing Slack apps. `curl -fsSL https://downloads.slack-edge.com/slack-cli/install.sh | bash`![](/img/devhub-icons/copy.svg) 2 ### Connect to your workspace Log in and authenticate with your Slack workspace. `slack login`![](/img/devhub-icons/copy.svg) 3 ### Create a project Set up a new Bolt project from a starter template. `slack create ai-chatbot --template slack-samples/bolt-python-ai-chatbot`![](/img/devhub-icons/copy.svg) [View sample app](https://github.com/slack-samples/bolt-python-ai-chatbot) ## Prerequisites {#prereqs} You will also need the following: * a development workspace where you have permissions to install apps. If you don’t have a workspace you can join the [Developer Program](https://api.slack.com/developer-program) and provision a sandbox with access to all Slack features for free. * a development environment with [Python 3.7](https://www.python.org/downloads/) or later. * an Anthropic or OpenAI account with sufficient credits, and in which you have generated a secret key. ### Obtaining and storing your environment variables {#environment-variables} Before you'll be able to successfully run the app, you'll need to first obtain and set some environment variables. #### Provider tokens {#provider-tokens} Models from different AI providers are available if the corresponding environment variable is added as shown in the sections below. * Anthropic * Google Cloud Vertex AI * OpenAI To interact with Anthropic models, navigate to your Anthropic account dashboard to [create an API key](https://console.anthropic.com/settings/keys), then export the key as follows: ``` export ANTHROPIC_API_KEY= ``` To use Google Cloud Vertex AI, [follow this quick start](https://cloud.google.com/vertex-ai/generative-ai/docs/start/quickstarts/quickstart-multimodal#expandable-1) to create a project for sending requests to the Gemini API, then gather [Application Default Credentials](https://cloud.google.com/docs/authentication/provide-credentials-adc) with the strategy to match your development environment. Once your project and credentials are configured, export environment variables to select from Gemini models: ``` export VERTEX_AI_PROJECT_ID=export VERTEX_AI_LOCATION= ``` The project location can be located under the **Region** on the [Vertex AI](https://console.cloud.google.com/vertex-ai) dashboard, as well as more details about available Gemini models. Unlock the OpenAI models from your OpenAI account dashboard by clicking [create a new secret key](https://platform.openai.com/api-keys), then export the key like so: ``` export OPENAI_API_KEY= ``` ## Setting up and running your local project {#configure-project} Start your Python virtual environment: * macOS * Windows ``` python3 -m venv .venvsource .venv/bin/activate ``` ``` py -m venv .venv.venv\Scripts\activate ``` Install the required dependencies: ``` pip install -r requirements.txt ``` Run your app locally: ``` slack run ``` If your app is indeed up and running, you'll see a message that says "⚡️ Bolt app is running!" ## Choosing your provider {#provider} Navigate to the Bolty **App Home** and select a provider from the drop-down menu. The options listed will be dependent on which secret keys you added when setting your environment variables. If you don't see Bolty listed under **Apps** in your workspace right away, never fear! You can mention **@Bolty** in a public channel to add the app, then navigate to your **App Home**. ![Choose your AI provider](/assets/images/6-90ef0d4d041ca7d3607699b802eca6ba.png) ## Setting up your workflow {#workflow} Within your development workspace, open Workflow Builder by clicking on your workspace name and then **Tools > Workflow Builder**. Select **New Workflow** > **Build Workflow**. Click **Untitled Workflow** at the top to rename your workflow. For this tutorial, we'll call the workflow **Welcome to the channel**. Enter a description, such as _Summarizes channels for new members_, and click **Save**. ![Setting up a new workflow](/assets/images/1-f9c24f965e8474d3c7e673ce05673ffd.png) Select **Choose an event** under **Start the workflow...**, and then choose **When a person joins a channel**. Select the channel name from the drop-down menu and click **Save**. ![Start the workflow](/assets/images/2-f28431ae65be1c00553a7b2aec61bc10.png) Under **Then, do these things**, click **Add steps** and complete the following: 1. Select **Messages** > **Send a message to a person**. 2. Under **Select a member**, choose **The user who joined the channel** from the drop-down menu. 3. Under **Add a message**, enter a short message, such as _Hi! Welcome to `{}The channel that the user joined`. Would you like a summary of the recent conversation?_ Note that the _`{}The channel that the user joined`_ is a variable; you can insert it by selecting **Insert a variable** at the bottom of the message text box. 4. Select the **Add Button** button, and name the button _Yes, give me a summary_. Click **Done**. ![Send a message](/assets/images/3-905316d2b75f243f2a05ed06eaf32cd0.png) We'll add two more steps under the **Then, do these things** section. First, scroll to the bottom of the list of steps and choose **Custom**, then choose **Bolty** and **Bolty Custom Function**. In the **Channel** drop-down menu, select **Channel that the user joined**. Click **Save**. ![Bolty custom function](/assets/images/4-dc916f899491971827db1a6ebe7a16aa.png) For the final step, complete the following: 1. Choose **Messages** and then **Send a message to a person**. Under **Select a member**, choose **Person who clicked the button** from the drop-down menu. 2. Under **Add a message**, click **Insert a variable** and choose **`{}Summary`** under the **Bolty Custom Function** section in the list that appears. Click **Save**. ![Summary](/assets/images/5-51cb5fc9c8350067939001cee6315f8f.png) When finished, click **Finish Up**, then click **Publish** to make the workflow available in your workspace. ## Interacting with Bolty {#interact} ### Summarizing recent conversations {#summarize} In order for Bolty to provide summaries of recent conversation in a channel, Bolty _must_ be a member of that channel. 1. Invite Bolty to a channel that you are able to leave and rejoin (for example, not the **#general** channel or a private channel someone else created) by mentioning the app in the channel — i.e., tagging **@Bolty** in the channel and sending your message. 2. Slackbot will prompt you to either invite Bolty to the channel, or do nothing. Click **Invite Them**. Now when new users join the channel, the workflow you just created will be kicked off. To test this, leave the channel you just invited Bolty to and rejoin it. This will kick off your workflow and you'll receive a direct message from **Welcome to the channel**. Click the **Yes, give me a summary** button, and Bolty will summarize the recent conversations in the channel you joined. ![Channel summary](/assets/images/7-a94698b85daeed62f48e11366cae7cba.png) The central part of this functionality is shown in the following code snippet. Note the use of the [`user_context`](/tools/deno-slack-sdk/reference/slack-types#usercontext) object, a Slack type that represents the user who is interacting with our workflow, as well as the `history` of the channel that will be summarized, which includes the ten most recent messages. ``` from ai.providers import get_provider_responsefrom logging import Loggerfrom slack_bolt import Complete, Fail, Ackfrom slack_sdk import WebClientfrom ..listener_utils.listener_constants import SUMMARIZE_CHANNEL_WORKFLOWfrom ..listener_utils.parse_conversation import parse_conversation"""Handles the event to summarize a Slack channel's conversation history.It retrieves the conversation history, parses it, generates a summary using an AI response,and completes the workflow with the summary or fails if an error occurs."""def handle_summary_function_callback( ack: Ack, inputs: dict, fail: Fail, logger: Logger, client: WebClient, complete: Complete): ack() try: user_context = inputs["user_context"] channel_id = inputs["channel_id"] history = client.conversations_history(channel=channel_id, limit=10)["messages"] conversation = parse_conversation(history) summary = get_provider_response(user_context["id"], SUMMARIZE_CHANNEL_WORKFLOW, conversation) complete({"user_context": user_context, "response": summary}) except Exception as e: logger.exception(e) fail(e) ``` ### Asking Bolty a question {#ask-app} To ask Bolty a question, you can chat with Bolty in any channel the app is in. Use the `\ask-bolty` slash command to provide a prompt for Bolty to answer. Note that Bolty is currently not supported in threads. You can also navigate to **Bolty** in your **Apps** list and select the **Messages** tab to chat with Bolty directly. ![Ask Bolty](/assets/images/8-5942a49b49344e9477dbbb5605f92567.png) ## Next steps {#next-steps} Congratulations! You've successfully integrated the power of AI into your workspace. Check out these links to take the next steps in your Bolt for Python journey. * To learn more about Bolt for Python, refer to the [Getting started](/tools/bolt-python/getting-started) documentation. * For more details about creating workflow steps using the Bolt SDK, refer to the [workflow steps for Bolt](/workflows/workflow-steps) guide. --- Source: https://docs.slack.dev/tools/bolt-python/tutorial/custom-steps-for-jira # Custom steps for JIRA In this tutorial, you'll learn how to configure custom steps for use with JIRA. Here's what we'll do with this sample app: 1. Create your app from an app manifest and clone a starter template 2. Set up and run your local project 3. Create a workflow with a custom step using Workflow Builder 4. Create an issue in JIRA using your custom step ## Prerequisites {#prereqs} Before getting started, you will need the following: * a development workspace where you have permissions to install apps. If you don’t have a workspace, go ahead and set that up now—you can [go here](https://slack.com/get-started#create) to create one, or you can join the [Developer Program](https://api.slack.com/developer-program) and provision a sandbox with access to all Slack features for free. * a development environment with [Python 3.7](https://www.python.org/downloads/) or later. **Skip to the code** If you'd rather skip the tutorial and just head straight to the code, you can use our [Bolt for Python JIRA functions sample](https://github.com/slack-samples/bolt-python-jira-functions) as a template. ## Creating your app {#create-app} 1. Navigate to the [app creation page](https://api.slack.com/apps/new) and select **From a manifest**. 2. Select the workspace you want to install the application in, then click **Next**. 3. Copy the contents of the [`manifest.json`](https://github.com/slack-samples/bolt-python-jira-functions/blob/main/manifest.json) file below into the text box that says **Paste your manifest code here** (within the **JSON** tab), then click **Next**: ```json { "_metadata": { "major_version": 2 }, "display_information": { "name": "BoltPy Jira Functions" }, "outgoing_domains": [], "settings": { "org_deploy_enabled": true, "socket_mode_enabled": true, "event_subscriptions": { "bot_events": [ "app_home_opened", "function_executed" ] }, "function_runtime": "remote" }, "features": { "bot_user": { "display_name": "BoltPy Jira Functions", "always_online": true }, "app_home": { "messages_tab_enabled": true, "home_tab_enabled": true } }, "oauth_config": { "scopes": { "bot": [ "chat:write" ] } }, "functions": { "create_issue": { "title": "Create an issue", "description": "Create a JIRA SERVER issue", "input_parameters": { "properties": { "user_context": { "type": "slack#/types/user_context", "title": "Represents a user who interacted with a workflow at runtime.", "is_required": true }, "project": { "type": "string", "title": "Project", "description": "Project", "is_required": true }, "issuetype": { "type": "string", "title": "Issue type", "description": "Type of issue to create: Bug, Improvement, New Feature, or Epic.", "is_required": true }, "summary": { "type": "string", "title": "Summary", "description": "Summary of the bug or issue...", "is_required": true }, "description": { "type": "string", "title": "Description", "description": "Description of the bug or issue...", "is_required": true } } }, "output_parameters": { "properties": { "issue_url": { "type": "string", "title": "Issue url", "description": "Url of the issue that was created", "is_required": true } } } } } } ``` 4. Review the configuration and click **Create**. 5. You're now in your app configuration's **Basic Information** page. Click **Install App**, then **Install to _your-workspace-name_**, then **Allow** on the screen that follows. ### Obtaining and storing your environment variables {#environment-variables} Before you'll be able to successfully run the app, you'll need to obtain and set some environment variables. 1. Once you have installed the app to your workspace, copy the **Bot User OAuth Token** from the **Install App** page. You will store this in your environment as `SLACK_BOT_TOKEN` (we'll get to that next). 2. Navigate to **Basic Information** and in the **App-Level Tokens** section , click **Generate Token and Scopes**. Add the [`connections:write`](/reference/scopes/connections.write) scope, name the token, and click **Generate**. Copy this token. You will store this in your environment as `SLACK_APP_TOKEN`. 3. Follow [these instructions](https://confluence.atlassian.com/adminjiraserver0909/configure-an-incoming-link-1251415519.html) to create an external app link and to generate its redirect URL (the base of which will be stored as your APP\_BASE\_URL variable below), client ID, and client secret. 4. Run the following commands in your terminal to store your environment variables, client ID, and client secret. 5. You'll also need to know your team ID (found by opening your Slack instance in a web browser and copying the value within the link that starts with the letter **T**) and your app ID (found under **Basic Information**). **For macOS** ``` export SLACK_BOT_TOKEN=export SLACK_APP_TOKEN=export JIRA_CLIENT_ID=export JIRA_CLIENT_SECRET= ``` **For Windows** ``` set SLACK_BOT_TOKEN=set SLACK_APP_TOKEN=set JIRA_CLIENT_ID=set JIRA_CLIENT_SECRET= ``` ## Setting up and running your local project {#configure-project} Clone the starter template onto your machine by running the following command: ``` git clone https://github.com/slack-samples/bolt-python-jira-functions.git ``` Change into the new project directory: ``` cd bolt-python-jira-functions ``` Start your Python virtual environment: * For macOS * For Windows ``` python3 -m venv .venvsource .venv/bin/activate ``` ``` py -m venv .venv.venv\Scripts\activate ``` Install the required dependencies: ``` pip install -r requirements.txt ``` Rename the `.example.env` file to `.env` and replace the values for each of the variables listed in the file: ``` JIRA_BASE_URL=https://your-jira-instance.comSECRET_HEADER_KEY=Your-HeaderSECRET_HEADER_VALUE=abc123JIRA_CLIENT_ID=abc123JIRA_CLIENT_SECRET=abc123APP_BASE_URL=https://1234-123-123-12.ngrok-free.appAPP_HOME_PAGE_URL=slack://app?team=YOUR_TEAM_ID&id=YOUR_APP_ID&tab=home ``` You could also store the values for your `SLACK_BOT_TOKEN` and `SLACK_APP_TOKEN` here. Start your local server: ``` python app.py ``` If your app is up and running, you'll see a message noting that the app is starting to receive messages from a new connection. ## Setting up your workflow in Workflow Builder {#workflow} 1. Within your development workspace, open Workflow Builder by clicking your workspace name and then selecting **Tools** > **Workflow Builder**. 2. Select **New Workflow** > **Build Workflow**. 3. Click **Untitled Workflow** at the top of the pane to rename your workflow. We'll call it **Create Issue**. For the description, enter _Creates a new issue_, then click **Save**. ![Workflow details](/assets/images/1-425310ad12615ccd0d18d0cef4b64217.png) 4. Select **Choose an event** under **Start the workflow...**, and then select **From a link in Slack**. Click **Continue**. ![Start the workflow](/assets/images/2-441d07625b2eca2ed63788c329ec3845.png) 5. Under **Then, do these things** click **Add steps** to add the custom step. Your custom step will be the function defined in the [`create_issue.py`](https://github.com/slack-samples/bolt-python-jira-functions/blob/main/listeners/functions/create_issue.py) file. Scroll down to the bottom of the list on the right-hand pane and select **Custom**, then **BoltPy Jira Functions** > **Create an issue**. Enter the project details, issue type (optional), summary (optional), and description (optional). Click **Save**. ![Custom function](/assets/images/3-26cf11cb05b5054b7b603535871fc557.png) 6. Add another step and select **Messages** > **Send a message to a channel**. Select **Channel where the workflow was used** from the drop-down list and then select **Insert a variable** and **Issue url**. Click **Save**. ![Insert variable for issue URL](/assets/images/4-53106026fb0e8c1589934ea392233aa9.png) 7. Click **Publish** to make the workflow available to your workspace. ## Running your app {#run} 1. Copy your workflow link. 2. Navigate to your app's home tab and click **Connect an Account** to connect your JIRA account to the app. ![Connect account](/assets/images/5-2cac4c1ea5da8c0fb1be6cfdabb54c7d.png) 3. Click **Allow** on the screen that appears. ![Allow connection](/assets/images/6-8acba7b05f5b68f0134eb774cbc7813c.png) 4. In any channel, post the workflow link you copied. 5. Click **Start Workflow** and observe as the link to a new JIRA ticket is posted in the channel. Click the link to be directed to the newly-created issue within your JIRA project. ![JIRA issue](/assets/images/7-05462c260209bf7acd0857892b8cf69f.png) When finished, you can click the **Disconnect Account** button in the home tab to disconnect your app from your JIRA account. ## Next steps {#next-steps} Congratulations! You've successfully customized your workspace with custom steps in Workflow Builder. Check out these links to take the next steps in your journey. * To learn more about Bolt for Python, refer to the [getting started](/tools/bolt-python/getting-started) documentation. * For more details about creating workflow steps using the Bolt SDK, refer to the [workflow steps for Bolt](/workflows/workflow-steps) guide. * For information about custom steps dynamic options, refer to [custom steps dynamic options in Workflow Builder](/tools/bolt-python/concepts/custom-steps-dynamic-options). --- Source: https://docs.slack.dev/tools/bolt-python/tutorial/custom-steps-workflow-builder-existing # Custom Steps for Workflow Builder (existing app) This feature requires a paid plan If you don't have a paid workspace for development, you can join the [Developer Program](https://api.slack.com/developer-program) and provision a sandbox with access to all Slack features for free. If you followed along with our [create a custom step for Workflow Builder: new app](/tools/bolt-python/tutorial/custom-steps-workflow-builder-new) tutorial, you have seen how to add custom steps to a brand new app. But what if you have an app up and running currently to which you'd like to add custom steps? You've come to the right place! In this tutorial we will: * Start with an existing Bolt app * Add a custom **workflow step** in the [app settings](https://api.slack.com/apps) * Wire up the new step to a **function listener** in our project, using the [Bolt for Python](https://docs.slack.dev/tools/bolt-python/) framework * See the step as a custom workflow step in Workflow Builder ## Prerequisites {#prereqs} The custom steps feature is compatible with Bolt version 1.20.0 and above. First, update your `requirements.txt` file to reflect version 1.20.0 of Bolt (e.g., `slack-bolt>=1.20.0`), then run the following commands in your terminal: ``` python3 -m venv .venv source .venv/bin/activate pip install -r requirements.txt ``` In order to add custom workflow steps to an app, the app also needs to be org-ready. To do this, navigate to your [app settings page](https://api.slack.com/apps) and select your Bolt app. Navigate to **Org Level Apps** in the left nav and click **Opt-In**, then confirm **Yes, Opt-In**. ![Make your app org-ready](/assets/images/org-ready-261edb9e4029529888a0be439fd4e3b1.png) ## Adding a new workflow step {#add-step} Before we can add the new workflow step, we first need to ensure the workflow step is listening for the `function_executed` event so that our app knows when the workflow step is executed. ### Adding the function_executed event subscription {#event-subscription} Navigate to **App Manifest** in the left nav and add the `function_executed` event subscription, then click **Save Changes**: ``` ... "settings": { "event_subscriptions": { "bot_events": [ ... "function_executed" ] }, } ``` ### Adding the workflow step {#add-step} Navigate to **Workflow Steps** in the left nav and click **Add Step**. This is where we'll configure our step's inputs, outputs, name, and description. ![Add step](/assets/images/add-step-12321b1d5b11e39ce1381b5e9740a1b5.png) For illustration purposes in this tutorial, we're going to write a custom step called Request Time Off. When the step is invoked, a message will be sent to the provided manager with an option to approve or deny the time-off request. When the manager takes an action (approves or denies the request), a message is posted with the decision and the manager who made the decision. The step will take two user IDs as inputs, representing the requesting user and their manager, and it will output both of those user IDs as well as the decision made. Add the pertinent details to the step: ![Define step](/assets/images/define-step-d9008c54d16806371367024f27901357.png) Remember this `callback_id`. We will use this later when implementing a function listener. Then add the input and output parameters: ![Add inputs](/assets/images/inputs-2c3f63c1780f6fdcb95219d288170489.png) ![Add outputs](/assets/images/outputs-537358a27699ab920b1e34ca76d0692d.png) Save your changes. ### Viewing our updates in the App Manifest {#view-updates} Navigate to **App Manifest** and notice your new step reflected in the `functions` property! Exciting. It should look like this: ``` "functions": { "request_time_off": { "title": "Request time off", "description": "Submit a request to take time off", "input_parameters": { "manager_id": { "type": "slack#/types/user_id", "title": "Manager", "description": "Approving manager", "is_required": true, "hint": "Select a user in the workspace", "name": "manager_id" }, "submitter_id": { "type": "slack#/types/user_id", "title": "Submitting user", "description": "User that submitted the request", "is_required": true, "name": "submitter_id" } }, "output_parameters": { "manager_id": { "type": "slack#/types/user_id", "title": "Manager", "description": "Approving manager", "is_required": true, "name": "manager_id" }, "request_decision": { "type": "boolean", "title": "Request decision", "description": "Decision to the request for time off", "is_required": true, "name": "request_decision" }, "submitter_id": { "type": "slack#/types/user_id", "title": "Submitting user", "description": "User that submitted the request", "is_required": true, "name": "submitter_id" } } } } ``` Next, we'll define a function listener to handle what happens when the workflow step is used. ## Adding function and action listeners {#adding-listeners} ### Implementing the function listener {#function-listener} Direct your attention back to your app project in VSCode or your preferred code editor. Here we'll add logic that your app will execute when the custom step is executed. Open your `app.py` file and add the following function listener code for the `request_time_off` step. ``` @app.function("request_time_off")def handle_request_time_off(inputs: dict, fail: Fail, logger: logging.Logger, say: Say): submitter_id = inputs["submitter_id"] manager_id = inputs["manager_id"] try: say( channel=manager_id, text=f"<@{submitter_id}> requested time off! What say you?", blocks=[ { "type": 'section', "text": { "type": 'mrkdwn', "text": f"<@{submitter_id}> requested time off! What say you?", }, }, { 'type': 'actions', 'elements': [ { 'type': 'button', 'text': { 'type': 'plain_text', 'text': 'Approve', 'emoji': True, }, 'value': 'approve', 'action_id': 'approve_button', }, { 'type': 'button', 'text': { 'type': 'plain_text', 'text': 'Deny', 'emoji': True, }, 'value': 'deny', 'action_id': 'deny_button', }, ], }, ], ) except Exception as e: logger.exception(e) fail(f"Failed to handle a function request (error: {e})") ``` #### Anatomy of a .function() listener {#function-listener-anatomy} The function decorator (`function()`) accepts an argument of type `str` and is the unique callback ID of the step. For our custom step, we’re using `request_time_off`. Every custom step you implement in an app needs to have a unique callback ID. The callback function is where we define the logic that will run when Slack tells the app that a user in the Slack client started a workflow that contains the `request_time_off` custom step. The callback function offers various utilities that can be used to take action when a function execution event is received. The ones we’ll be using here are: * `inputs` provides access to the workflow variables passed into the step when the workflow was started * `fail` indicates when the step invoked for the current workflow step has an error * `logger` provides a Python standard logger instance * `say` calls the `chat.Postmessage` API method ### Implementing the action listener {#action-listener} This custom step also requires an action listener to respond to the action of a user clicking a button. In that same `app.py` file, add the following action listener: ``` @app.action(re.compile("(approve_button|deny_button)"))def manager_resp_handler(ack: Ack, action, body: dict, client: WebClient, complete: Complete, fail: Fail, logger: logging.Logger): ack() try: inputs = body['function_data']['inputs'] manager_id = inputs['manager_id'] submitter_id = inputs['submitter_id'] request_decision = action['value'] client.chat_update( channel=body['channel']['id'], ts=body["message"]["ts"], text=f"Request {'approved' if request_decision == 'approve' else 'denied'}!" ) complete({ 'manager_id': manager_id, 'submitter_id': submitter_id, 'request_decision': request_decision == 'approve' }) except Exception as e: logger.exception(e) fail(f"Failed to handle a function request (error: {e})") ``` #### Anatomy of an .action() listener {#action-listener-anatomy} Similar to a function listener, the action listener registration method (`.action()`) takes two arguments: * The first argument is the unique callback ID of the action that your app will respond to. In our case, because we want to execute the same logic for both buttons, we’re using a little bit of RegEx magic to listen for two callback IDs at the same time — `approve_button` and `deny_button`. * The second argument is an asynchronous callback function, where we define the logic that will run when Slack tells our app that the manager has clicked or tapped the Approve button or the Deny button. Just like the function listener’s callback function, the action listener’s callback function offers various utilities that can be used to take action when an action event is received. The ones we’ll be using here are: * `client`, which provides access to Slack API methods * `action`, which provides the action’s event payload * `complete`, which is a utility method indicating to Slack that the step behind the workflow step that was just invoked has completed successfully * `fail`, which is a utility method for indicating that the step invoked for the current workflow step had an error Slack will send an action event payload to your app when one of the buttons is clicked or tapped. In the action listener, we’ll extract all the information we can use, and if all goes well, let Slack know the step was successful by invoking complete. We’ll also handle cases where something goes wrong and produces an error. Now that the custom step has been added to the app and we've defined step and action listeners for it, we're ready to see the step in action in Workflow Builder. Go ahead and run your app to pick up the changes. ### Creating a workflow with the new step {#add-new-step} Turn your attention to the Slack client where your app is installed. Open Workflow Builder by clicking on the workspace name, then **Tools**, then **Workflow Builder**. Click the button to create a **New Workflow**, then **Build Workflow**. Choose to start your workflow **from a link in Slack**. In the **Steps** pane to the right, search for your app name and locate the **Request time off** step we created. ![Find step](/assets/images/find-step-44162a923fcc47fd4623b7da2d6d6961.png) Select the step and choose the desired inputs and click **Save**. ![Step inputs](/assets/images/step-inputs-a50e5a160f4c57b357d35d5f2b355f54.png) Next, click **Finish Up**, give your workflow a name and description, then click **Publish**. Copy the link for your workflow on the next screen, then click **Done**. ### Running the workflow {#run-workflow} In any channel where your app is installed, paste the link you copied and send it as a message. The link will unfurl into a button to start the workflow. Click the button to start the workflow. If you set yourself up as the manager, you will then see a message from your app. Pressing either button will return a confirmation or denial of your time off request. ![Message](/assets/images/app-message-d2fbe48f00b64d118a456422d9f9a8cc.png) ## Next steps {#next-steps} Nice work! Now that you've added a workflow step to your Bolt app, a world of possibilities is open to you! Create and share workflow steps across your organization to optimize Slack users' time and make their working lives more productive. If you're looking to create a brand new Bolt app with custom workflow steps, check out [the tutorial here](/tools/bolt-python/tutorial/custom-steps-workflow-builder-new). If you're interested in exploring how to create custom steps to use in Workflow Builder as steps with our Deno Slack SDK, too, that tutorial can be found [here](/tools/deno-slack-sdk/tutorials/workflow-builder-custom-step/). --- Source: https://docs.slack.dev/tools/bolt-python/tutorial/custom-steps-workflow-builder-new # Custom Steps for Workflow Builder (new app) This feature requires a paid plan If you don't have a paid workspace for development, you can join the [Developer Program](https://api.slack.com/developer-program) and provision a sandbox with access to all Slack features for free. Adding a workflow step to your app and implementing a corresponding function listener is how you define a custom Workflow Builder step. In this tutorial, you'll use [Bolt for Python](/tools/bolt-python/) to add your workflow step, then wire it up in [Workflow Builder](https://slack.com/help/articles/360035692513-Guide-to-Workflow-Builder). When finished, you'll be ready to build scalable and innovative workflow steps for anyone using Workflow Builder in your workspace. ## What are we building? {#what-are-we-building} In this tutorial, you'll be wiring up a sample app with a sample step and corresponding function listener to be used as a workflow step in Workflow Builder. Here's how it works: * When someone starts the workflow, Slack will notify your app that your custom step was invoked as part of a workflow. * Your app will send a message to the requestor, along with a button to complete the step. * When the user clicks or taps the button, Slack will let your app know, and your app will respond by changing the message. Skip to the code If you'd rather skip the tutorial and just head straight to the code, create a new app and use our [Bolt Python custom step sample](https://github.com/slack-samples/bolt-python-custom-step-template) as a template. The sample custom step provided in the template will be a good place to start exploring! ## Prerequisites {#prereqs} Before we begin, let's make sure you're set up for success. Ensure you have a development workspace where you have permission to install apps. We recommend setting up your own space used for exploration and testing in a [developer sandbox](https://api.slack.com/developer-program). ## Cloning the sample project {#clone} For this tutorial, We'll use `boltstep` as the app name. For your app, be sure to use a unique name that will be easy for you to find: then, use that name wherever you see `boltstep` in this tutorial. The app will be named "Bolt Custom Step", as that is defined in the `manifest.json` file of the sample app code. Let's start by opening a terminal and cloning the starter template repository: ``` git clone https://github.com/slack-samples/bolt-python-custom-step-template boltstep ``` Once the terminal is finished cloning the template, change directories into your newly prepared app project: ``` cd boltstep ``` If you're using VSCode (highly recommended), you can enter `code .` from your project's directory and VSCode will open your new project. You can also open a terminal window from inside VSCode like this: `Ctrl` + `~` Once in VSCode, open the terminal. Let's install our package dependencies: run the following command(s) in the terminal inside VSCode: ``` python3 -m venv .venvsource .venv/bin/activatepip install -r requirements.txt ``` We now have a Bolt app ready for development! Open the `manifest.json` file and copy its contents; you'll need this in the next step. ## Creating your app from a manifest {#create-app} Open a browser and navigate to [your apps page](https://api.slack.com/apps). This is where we will create a new app with our previously copied manifest details. Click the **Create New App** button, then select **From an app manifest** when prompted to choose how you'd like to configure your app's settings. ![Create app from manifest](/assets/images/manifest-fd70aca1881066f8f7d7a3a16d2ad94a.png) Next, select a workspace where you have permissions to install apps, and click **Next**. Select the **JSON** tab and clear the existing contents. Paste the contents of the `manifest.json` file you previously copied. Click **Next** again. You will be shown a brief overview of the features your app includes. You'll see we are creating an app with a `chat:write` bot scope, an App Home and Bot User, as well as Socket Mode, Interactivity, an Event Subscription, and Org Deploy. We'll get into these details later. Click **Create**. ### App settings {#app-settings} All of your app's settings can be configured within these screens. By creating an app from an existing manifest, you will notice many settings have already been configured. Navigate to **Org Level Apps** and notice that we've already opted in. This is a requirement for adding workflow steps to an app. Navigate to **Event Subscriptions** and expand **Subscribe to bot events** to see that we have subscribed to the `function_executed` event. This is also a requirement for adding workflow steps to our app, as it lets our app know when a step has been triggered, allowing our app to respond to it. Another configuration setting to note is **Socket Mode**. We have turned this on for our local development, but socket mode is not intended for use in a production environment. When you are satisfied with your app and ready to deploy it to a production environment, you should switch to using public HTTP request URLs. Read more about getting started with HTTP in [Bolt for Python here](/tools/bolt-python/getting-started). Clicking on **Workflow Steps** in the left nav will show you that one workflow step has been added! This reflects the `function` defined in our manifest: functions are workflow steps. We will get to this step's implementation later. ![Workflow step](/assets/images/workflow-step-253a02d23b2c096781c2bda15f57e261.png) ### Tokens {#tokens} In order to connect our app here with the logic of our sample code set up locally, we need to obtain two tokens, a bot token and an app token. * **Bot tokens** are associated with bot users, and are only granted once in a workspace where someone installs the app. The bot token your app uses will be the same no matter which user performed the installation. Bot tokens are the token type that most apps use. * **App-level** tokens represent your app across organizations, including installations by all individual users on all workspaces in a given organization and are commonly used for creating websocket connections to your app. To generate an app token, navigate to **Basic Information** and scroll down to **App-Level Token**. ![App token](/assets/images/app-token-f1c2bc2fa7b3d0dbec967a3b49524bf3.png) Click **Generate Token and Scopes**, then **Add Scope** and choose `connections:write`. Choose a name for your token and click **Generate**. Copy that value, save it somewhere accessible, and click **Done** to close out of the modal. Next up is the bot token. We can only get this token by installing the app into the workspace. Navigate to **Install App** and click the button to install, choosing **Allow** at the next screen. ![Install app](/assets/images/install-dbccbf01e581ff4193fc03fa463f16e4.png) You will then have a bot token. Again, copy that value and save it somewhere accessible. ![Bot token](/assets/images/bot-token-c9d93e4b53b71218b869d416903a6334.png) 💡 Treat your tokens like passwords and keep them safe. Your app uses them to post and retrieve information from Slack workspaces. Minimally, do NOT commit them to version control. ## Starting your local development server {#local} While building your app, you can see your changes appear in your workspace in real-time with `python app.py`. Soon we'll start our local development server and see what our sample code is all about! But first, we need to store those tokens we gathered as environment variables. Navigate back to VSCode. Rename the `.env.sample` file to `.env`. Open this file and update `SLACK_APP_TOKEN` and `SLACK_BOT_TOKEN` with the values you previously saved. It will look like this, with your actual token values where you see `` and ``: ``` SLACK_APP_TOKEN=SLACK_BOT_TOKEN= ``` Now save the file and try starting your app: ``` python app.py ``` You'll know the local development server is up and running successfully when it emits a bunch of `[DEBUG]` statements to your terminal, the last one containing `connected:ready`. With your development server running, continue to the next step. If you need to stop running the local development server, press `` + `c` to end the process. ## Wiring up the sample step in Workflow Builder {#wfb} The starter project you cloned contains a sample custom step lovingly titled “Sample step". Let’s see how a custom step defined in Bolt appears in Workflow Builder. In the Slack Client of your development workspace, open Workflow Builder by clicking on the workspace name, **Tools**, then **Workflow Builder**. Create a new workflow, then select **Build Workflow**: ![Creating a new workflow](/assets/images/wfb-1-fc29b23e4e67ce4eaad8b143728996f8.png) Select **Choose an event** under **Start the workflow...**, then **From a link in Slack** to configure this workflow to start when someone clicks its shortcut link: ![Starting a new workflow from a shortcut link](/assets/images/wfb-2-6cdaa8bf31a9f78b376bc6dd6273bab1.png) Click the **Continue** button to confirm that this is workflow should start with a shortcut link: ![Confirming a new shortcut workflow setup](/assets/images/wfb-3-9b83a821dbe604b79d32d8bf09395035.png) Find the sample step provided in the template by either searching for the name of your app (e.g., `Bolt Custom Step`) or the name of your step (e.g. `Sample step`) in the Steps search bar. If you search by app name, any custom step that your app has defined will be listed. Add the “Sample step" in the search results to the workflow: ![Adding the sample step to the workflow](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAzwAAAI0BAMAAAAq/DEEAAAAAXNSR0IArs4c6QAAADBQTFRFGx0h+OS3v8DA7qkunZ6eKb8/gIGB3DjmNYTcaGhpV1dYjC2URkVJOTk4IiQpIB8lPowYTAAAI+VJREFUeNrsl8GK20YYx/0G+wwBs7Bmb4YGkuZiBsEI+biX0l5UYZCxT/smYhDMYF+2imEG69RTobmkqkFCc+rWIHW+vEGKH6Cw/Wbkmk3aJumhXpPkJ1nfzHwrLeyP/2jde/37q7vjsXPXM6zdwNbd2dld7w5PHO2Qu95ud9bD427Xw0Fv1/tI+UNxyR3p4YjxdJc4nowePeq9fl1J/ikihTulFA8DqnkfDPVUNXzmJDEy7Bn9+e9wqphV71PPjj5c8WPwOCWqnoaPnVat6xqQuq7U2hY3MAbAfiqlbFtrMHBqmFPRY/T/9ijUU+lOj1I5GKcHBxrw1Ad7nZxXp6fHyB+0m6y+yMHy89WxpFXa5GDZzgFWGpB2Bm1uD7CkAE28ymGPWUOr8egWuLZNfWhypd2jcH2z3K+hj7+nx+DA6dCH9OD08Ji8mYEDn/2w9PDz03D4BLo6BKS9urqC45AtNwGWbbCdZznLAWl82ATFuBgDYiKAgrIlOFyzmBWzbN41c8goyw9NQmZYNjNPw80cHNvAxmNdH9Jzf3PTyF96cLJPT0bobQAOdgp6htePh7mtyBLrc9TzNRyFG9RTykV2US3CKctLASUfwHaQDbK5XBglQiOSKVuWwqzLXMIW3flJkCywKRaREtE0wbuqRamVbs4zH2ubk1om80poKa2ehjuEwMsk5si6kjGXHJH7NSndeo3OgAivCCquW66TWjy4nvYpanmBdYh8CQBXx9UTkXPWZ8GIJoqQNSEDaM7ZOZOESOKFGcWGIERQ5hNMD56RH2GnJH7E6MhL8BZOGUamuVj5UU42gbcho1lE8HR64kk0QdLJJMIBolQ6ORAh3SgVmCsN0VQVPiNBRvxkRR9cz2/D4fX1M4BmiDzZ6/nqiHpEvxgUmAi28kLqrQbQkgmJCprEZB2SdTJj6UUWeBGlNj2tRzyS+YyfryOSs1kiaUa90Peg7Y8C1FOMsR1OCfMj0enBP7/npZ2KvZ5oEuPaxC4SXOuYOj2M0Ftfpj7hcUIWD67nl+H1cHhPj7myHFHPmmwuUc+SZX1CB80AgNDQexm8pNSEfZ3NWXq5uUQpNjro7iLysnkWU8AmWyYrf+uTyKPQXJTU6gmwfROPCEbJ6tneT48bijfS43n30rPWYFbW/ogSXSej/BTS8/iwuT057uaWzbLA7keDYmzTQyvpbVBP6N+c3/pJjHqi2c3cpmccDkhg02PCy+QimyWcmmg0t3po4YcUm+1F6SWib9MTJFOvyjs9DbcSJnGMBQODqAr1OGmpc+bcIW5zM5OaZJSsfKLiJAr+4Rvsgbfm+sPm/1GPwXfP0xdYh8izvZ5v4ChsCJlbPYQFmXv3zEYE9dwEt7QhhFMINxT1CPwxFoRzmx4Ix1mA95UUIkZRj8Jm4odjaPrEz0gf07Pp23dP0OlplRJcCeWQXAhV16VEOFcILnBhi+q+qkJEvMKPCGX7d4+Bf8XAh2Heru96iHlTD3yPu5vG+h3qybH+ino0HIU2jbXU3PByWQqpJc74AqBZNAtIp0YAb0WZlzk2yqXMbXoA50uTinYBvBIyd3eVC2wazvMy5s2Sm9SOcq73elRlz8qikBqwuEGt7rMGSznB3y75oolzWXMNHfo9ZrS7vHvueP/z3tJjvn36IyCt+8cNeY5724OhwfEne+b3mjYUxfHzNFipXspgsNeDCI72YewvWNjLxto/IohQsftbnAzB0omUIRT88S+Ib0ri8iCDSrRhyB6KJnkYg0mb7OYameI0+OB09nxINOeeSOB+/F6J+SsiPSt9mbkevVQSc18qNZWS56nZVXw9vJyiqej86ptEn9cTiL62enU6pVU/UO52eG4E3I5IT1NVJ3qUiR8/VQF25qIUUAehBjWhu/Poqqpw+BvfFVXcevIXMabwmje8YZXT3TaAnqoEs3k9G1jjaPKDAZKyUXRa3P5jwFxGb+pgDfUWMgwYnD2h59eL+qIcLqnn52M4UwNBEATxkGA0BaSHID27SJimgNJDkB7SQ5AegvSQHoL00H0PQekhSA/pIei3h9JDkB6C9JAe4iHrcflmw5h7A4B1/aprTBouCNiQt8Hpmfau6XFs2AJCV9k/nBvjsYQGxTx4WPA5D0YoPm6weAEaKRil7NGpf2pc4wazrxO7lp7bRNL4N4Z0b190pf7BNO/B4w5rTD4Gzt47fjA6C6EGHmEpDfWI/S1itCNjPftSDaCNEm5ZephjuR6mt1mWY83WwXoyiNV2AdYPKxYgnDUWND8ILU9eHgiegZCCtbB87IINP56zr5X+4T1qTAReSrN+RKujVo8Bm+hh9aPvaIO7RYs3M5djBelxMBX/kkn7Y2JbF7/ews8zWMArX8+LKT37Ij1XzaR5GcnlypnoBWqDXAs6SUzDIyzIWL08GmTLRqOZlKrXF3KsgW4n17r9FD63rguweRwzADvgX4N9rLgD6c1N5/TmcblYCTeSBqyLj61EbbEegZ+ep+AxEOmREFN8xyTiIfZkjJq8SsMe5lE6kU8yGKnKUkxSpRRiDu8kjLZjI/zN3tm9JpLuefzH7NDQvakq0nMu9mZh56GQoyS99GZYWDgXs0VRoKjzL/Q6IihafTkXZ29291zMZeOKoKjdmyOC2WgW9uJcnL2QIEJCNJ4ggsGq1AnBRbEei2YIdNMpz1OlPTH9EmOGGDtdn+BT9TxVAbs+futXbx1l37PFDw9hanqctROEdjnO/RIhWw+hJ3BT3A+EYQY94/RwLt/Kum2Pi6zbq+iQ3eB20AbRQyMXWl/l0ijl8/psSS7HytGVFuqweZRnOyjC1eHWoX+xHiaKbG0UPrHt2I5tOdRgd+twU1D+59P0fPWB9IjbjgMH4xMPVijURDzK2gZED/h4EhO0S0rQqs9DQuOA6N/fRw27zoVQg3OzyiehR7tcD933oQx6dszy7LFdR2XkhhujE3DNmJ5nNOflnm6vHNjP9fBlG80RPdvIcYrYFls/WPWJS0SPtk70lB005+HWY3absvilh6BMuWI90DkRPTtgeeHYTqMqh57BDUEH9mP7l+r5am1t7fG5Hopzt9BTosdxYB8YelTUYmXphJWMN/kSPXqAbH1UW3/kEykuz9YNPQ2byj31oRxa0RZfz/T06EmJE7nMsR1LL+19VDvkvoUbohWGV2HtEj3EzsjPWA+sIw6VTD1sxicesykkIYE/RLyh5xQ9pZBD5wRONPRUuaeGni7yo/o620Ye+KTTM9bzGvGoyAkdFHAesz5bU7g5Pbn6Jec9hp61ET/rYXocimjGzu0V8vjEU+RGR2Vkxz7W98x45yXgPEwT2Y4MPbXoyvrfvUJ6FLmZY5uORLgL6aF8yKVsGwes7mOEPB3E1uFmYFTSnF2SHhIek8emHhMsa4A1egjqQCdzAx2G6gB0owMMaWgNdFUBXRtiGGq6Rg+AwRoMBzBUYN4MGdJo4/lzPb2NjWqaTFXSqElM2MRqYbqeMbIGOmYYWXtpxxqoCsyN6XqGzMSbZYbjOUaD95jcMjfMnkYdwbvsArPZU4B6CiaHys96GjyfdBmajMZpeOJx3z515zYxw2jG5KXDHLs1PWtvMfQsLnSs/uAZvAOTBwj1FIbyjj4nzfq5Ho8kpVVVJomRVEEiegTcs5OBC+nRpt5QuJ+C29RDwpP+fu2fmiQ+C65HfPC8X+grb/BGvVoAaafepov5nXqIDEieTa1XZHZyE3pE/MZdjjn74Q6f5v2RUXoqMeekHioNvX16V7lED2i3qYeE5xvIrj35ksRnsfUkAw8yifLOfrOxkY6nIbsRyenebN4b6jQ3q7asHMr2wvEJPc5E25V1+Q/dsc0qn3eO0kMG5ImdW0dQYpElvnSJHgZuUw8Jzz/CX609o75fcD1M6iSfcbeSmXxOCgcB8jiZO/PmtRDRU1C9p5uRZq50vnMbNFih7Vovxnbd2bAqqM5RerLFbPFcD2PqoS7oWSTM9KwVfr/2m9bCpydFBdLODSniiW8UQyM9ujcPRI8ULxA9oY1KabL2iHLPRWzsurt82okFQ0/Xka2VMxPpoQrQO2L2lMXVQ+Iz5vGCp4fJZsLqIP60gmWiJ4fD2b43p3qInn7Ke1qIqJ2ti7Wn71g39KgVj6EHR1NZT7YYq005sF48Pb/Z+P3C66FT0H1eEeqNUpfPED1Zf+ZE8Ob8W6FOK1Dw3tvKCn0+eEHPOD0xf8SpOskIyxezvIA/OT0l6f7C6yEMQR+QBmjQAPIYYDBkBgBklFEAgLyYidPSIj5Lt+RDKd1L4k3yg/FOAWfzhSnnPQvF+Lznf/9zrOfTYVf76IbVsQFpTQZ4gmwNX5oeeuH0fKT2MDAJs3BPhuFrXXM7lMd6Ppn0fPW+niQAVGCSHQU+wo4GTG7ibvkduGK9UHqW39NDCXVgyjBJ/qP/HrLkTaTz4tPRMz09qnQbXFnPq+AWQFY9otUjVaZlSaMlIgFLCqgypZxpZ5quGD+qDJgswc3M6QtK0jDIIH/iemgA+OsfboV//aAewjt6mM6eSPTkw68T6URCDcVL3URQgXwiQiWS3VKz1FJeb3W3unIiIScTeTVdDnW2dhLFjWGIjgB88o+C/OGH2wHe51+WDR6bdh6Pn3ODnBomeqRsNyLLzT1397ucnFAgLgW7xdbOk9zzvPL6aeVps5VuZeJSvrPVyZwWwv1IRQ30vQv/FKLJYBH1lOA9DpYn8YABncDERplptJ4Drux5l7zJYZ7o0RLNYLzgTXqTQIl5MX+ybyzJObXTrXubIhPq7ORbzz6N4qNcpueHxdFD+flvvuE5juNZnhfqo7FAgMxlodF6RsXj53qYRLMg4VA6lAYIpZKpk9KSNwW5eP30hamnm+vl6rBY8dGnhGfB08MMNYwZTYMhoww1MHkt4s4Lpoxz3ee9TNPQkzfiFIdEN6MOcplKBiAnViKnma6YgnyXHBh0CuGz8OvAm4AGCwEzvBSAhU3P9LtNpy+0109JKiKvtt4EE3vfLX3XDBI9CUio8WC9UepsAZxsdZ5Q8aBM9KjJzot7W5VAkQpTIQY+bcbpKcG8MfVcCVoDWgGMB7RCq3igMAotYwAMmMEyWaprALpGKwzGzMAYphWdrKKZnTug54fF1jN+m+a+TmPIrGb0TYbjZeYCYBS4S0ymh1HbVeNrfka0zebG+lVJM/VY/3f0quk5kwcwN2hVtvRcCXqUnv+RNJgnqmLpmSE9NRnmCj6y9MxQe6rKnEMrW3pmSM+eAvPF0jNL7dnULD2LnB5Lz+LWnvnp0XNCWgMCtvTMkJ5NMNB3NxUw0W7mcWumwnN82krP9dLTREgcjabBgCrCJVzD3RLPcpxQt/TMWntMG9zmDquAwQoYPIjcr8M5KZiEEWFWmBOO+OG3puvRLDMmzMSR26sVoP/rBRDokZ43np/2J1Z1wiT0o9mf9MnycT7GRazac530nHgBjp+AgT2w9SaQfuOJhhRoBiWRElvBQzbdDdRauXA8A0wl1LIVK0G8QTpXhvHzkWSW91g7t+vUnhORvEw9tC3vLqcCfc96SqOEXclDrcZ327ZqYMPdCPuTLqCcuy3HnquS8W844crQftap+jm3pWfWIzeNzHY8wIzSw6wwzgCU256XJXjghSWiJ1ZknNQqHWiUskdB0IX9pUevnHFvUI8rM+jh3e0Yb+m5Vnpe2zXaVwICbepp7Bl67nuBInq6/L5z6RH4G6Uy0QNNof3oNLxbCw5n0AMxflMocx6r9lyn9ui+UPbhPhis6K7YoNz3vNyHNx5ddfdXcUd0nq3owkgPgxubq12vjoN4hsctmDLL8zybsdJznSM36CI2ujKqPXFvIxjse34KKXQgURPiq/ncFl+IJTwjPXQyLtkKQqLoT7jh6rwyn5AqWXqukx7Q2rUzXjFGN5N1KlGkaksphTlM1lvp4mFSaRV7iaOecqjsgb6T0vK1w6QcSOzD1aFiPMe7FEvPjFes/++tLaatAWGojdQxQ/Iy5rUhGWTGQgnmGKPRQQ1moednhSPrmtv10jP7eSazCTPBqNWBdcV6xtpj3VCw7pZaeq5fewpz1sNYemZJT+0I5opuPakz01OiEljPuS1uekqqrMHcYKynRGd/xnp+SJJi6ZkpPTAcDgbD8Y8xS9oL/Ssun94nE6wA8wdLz9XT89s/zhsrPTOkZ/5Yeq6cHkuPlR5LzzWgbzE9dWvzXyU9/387dn5rbf0r6YH//uNtMGt4NPgQQzBgrO8tnSNM/wjepZeGD5Eokaaf3FSsb5yfGw2EtvolGEHVSHOoNBzwAWjuCcAbDqGSlZ55QXM2LrP9LYx4taIBE91v2Icf0/MTu2HpmR8UyrSxz630N7W+XG3Ya3DGpcr2dh2oTQX0qtavU1Vo16AtF7gnS9Vtu4xK+q45qCzVLD03rcc96CCU4pC4bWMRWoGXCMXIVPchDyyhks/zhYOsUIrydu5J1ImQF+2vI1uHxej5tsfSc7PQPuTqIdsOy9m3kc3HpuEeCmYRx3ZRgNVolEErBys+G7caRQ4ujDLrbAZVkYCS7C6K+ERLz83CNEluuG9BOrBt2+RjhwYUt9Wwd1GDzSOF8YWRPepG4oE9apc53qa8dOiowh75PCiH3NwzS88NQx9yHu5b3c/Zth1wvAJET8n4Et8G4pEC24LdxqXQiy9s0VWGQzY4dtCobFPWPT6/zY7qd1DPcAgEfTT//lpDmCsyEyV6fmJjph4zPfsNB9FjlySAl2iVR3vo+bEt+ojhAqj+BdFTYZWoZxuFWVa7e3oYv1AHgAStAHT5DBgY82OawmAA82MJBZDIuQ/s26Ye+z7RkzL0dNFeEOAe8voQ5txRR3SV5iJINPTsok1OPEYFzv7a9trWE7S7pId27qyCBvF7JdADeyEw6L2AMXRwR8rMcVe4xCF2fx1lEW/o6aBHQPtQmehRfcimwQO0tW7Tygg9NdLzJLryhUNHNR9i6x2EfaunbIftojulh3Hr7v7GUbyR0h54AKp07bDQDFMbR+2NwwIwrkHXJe/W2rs1mAuHiQL0ElKikOqmgUoUAVrJwzSV0IwFoOeVHhnOp5RWkcmXuun7aSap9MmipSTsPFtKUSkqCXcqPUJczOUj8UZIufcUmOCSGCw0wo2EJxd3+oGOhbvOVihUToRgPgwAmAHoGmjvfBMoHlXCIYC5VPv586UNBzBGYxRNu1u1R8h5wno4/tMLuPcEgOgR5M5WHAdzqudAgb5/TzwJ+is4oVgX+W9n5xb00KH4ly/gtRdooqfj6GwFhgGi51gB7TQknmxWK0rc0jNX6LEeV9uVaEfiX6Y1ylndDBx6pXgnXZZceUMPnapshptpqVwNWVvsVtITiGdaiWL+QUiBSiDdTIiJ1OtQN5jZxeKJoscDalBKpMqJjLXFbkMPSDLQkoZpGchU0WVFHdAyQ0a0AQWgHoGqqUcVSbH+msr89UzGaNQyDLwVoZ2P71iVx7pbanEOY20CS4+Fpecu62F0PIbMDMjkxvtDa+NfXc+ZNHesw8Ar66HlATMcMkODOU112dr6V9VD4fmjWlvf0nMn9JwtmB7aWG6+xnwmfQ0uQi9kemgVqyoevcZ8Fn0sXyU9A4xvsj9dD/XOb+ufTX/wCdQeGr+H/rn0F6D26NP0yPhzRb6s9gzO25vrT0+P/lkGx+Tsw+m56ZIz2den6aHwZ4t6Se2ZvDo2eKePPxoMfab+7OnBA3zX+9PT837OmnwRn9MR3y6Xf3GOB1Z6PoaqXZaeSaNCXryoZ0RvNHM9MedhmpKez4/p6bmIA+N+Um7vSAnSFDoi6WC8U2i65FZarSZkfG2s2nO99KjcQ4Nf1TCBL+BYTIwF/H5xPSh0vNl4BPecyaxQdcbafCzyy85LrfTMnp7j5RGPMKGJirjvjqpCz5utlSsepxTC3QjueE8izbC77/4FagZWeq6Tnj8TNejHr5f/Fhu0Vg7j7hh2Ej1yI+cWgiHc59Md8ViIpzzqWI9+Xlem9q0D66lQ2iXp+dPyw1//+OO/jfTIqiOe+FlPxeOSZIwPAx3xJCK1PX33xWjogxn6Uy/qfI5MP+/508MfCf++Zujpu5orru5Iz3ot0PQGpALuy/GWp+vu7bm6HnwtdOvQ4DLOLk0PMvT87rGZnhhbiwrhGHb1veucpyM2UQT3eLeKalH2kGdrGF/j9MtKzy9Iz59NPf/xD6Ye9fzTnpUn7xkR+heyYx1Yz8T107Ns6iHpuUhFxhfpR/A1mT09OwV811Gr+C1wAfqd2vPrD+pR8btM+rrZ9HQRquE7TkyQ8Qj10vQ8/Bui5/t/JnqmMbf0RNOVVXy36Tkr4pWO3JZNPdwt66Em13Ji1T7RzRfwRXaK+B266TwmJI3V029/qzteLYkXj+z5Sf4ZXFp7kKlngdKj1i7o6bCs5MEmrZq5nM/gdzjxcDKZ8KTpOrBBmbUdeLGBKsh40eiGAntv9aiX1h5Tz9rXi5OeHrLj7up5fzu9+7ZbLporOPD7evbwWM+45kYLewusJ1ts8eLVjty+/vF3aG3Z1CPhD3EmzzU90aBPXs9M6ImQKKRjTjUocEKXJKfBhoLuCl9sxML+CMYVQSZ6/K24IPOqE/fd5ZjL0IMPvBWh1uXTghRctMrjGn9opp/3LD9Ey8u/GukRAh8y0c9gk+o80jNoOdV+z4nPabCpsm0j4KuyTl9wOyjipi3Phvi4Y5vosmMc8IlED9ew+SJ82UOyFRU4GWfZ9IEnEBWjQXN0sYgXcTOCr5KezvIIM2rubkZW1basVg0ZbVmqGq9+Rq1h3JaSMum1bzo9sUwjGUvjc/oxtrmK29G0TV4vbpNg9Fa6jr5dFbZFX1XAWCo/MvV4Gx6OrxE960UfmfhJ7ZHKHlZWea62YOEJ4b4ZhOnpUeOBgCAEgvJIT6HYaSX/wt75rTQORHG4T7C+g4iQRV+ihF5I60NIWUixPkyRQIcmF2UJTEjmIbwoRGyQRQIpzuyd0JA59gnKTuq6VrKYKk2N7fkCSc/M5S9f/nSSiUU9SFxObUrpBaWe2kJKxsYNsQLKyrVHahDta7CMPHOO03rd0uCKTfdbWTxHyXfQVTxcB+jVX+LZF5k9rMcApD5sXdabByCz1kpxl8lT+I91noZxbz9EY9IHiBnILon9Fkk7dmckki50HjzipBfl2pNdBbw2NOa90dHd8U8Vz5DJ6GgRT6rxw6d4Eu3uXzz68PQ5nmvevjrRImUPV5pBpXCEVPIU2ZOnSVIVj3DjzD7ebw6m7JzIjm24Akx2/kA8Ki/KtUfFk3RhmZ5+OD0wtbPMnsao/WSPbLdP/sZz0F7EE51GLT3WVO8wi+dM1341Dy9bw0azoVorxQ8lz0eeNWhFN9ZIxcMDkHHfP8/i6U8G9oCDvLfI1BfOxC7XHqXFpQ3LJKYlTc90wwEkRmyEIK3UgokRxmEALoBJbEgYTVjCXCCqNxCBUP1syhziJ/1Fa5WQDUfJ8wF7uqnVp7G4vvYgpRNieUlomfSeBS5INwzGxHdoWK49ShZNwGvm87eGyefwxUh1w4cP2PMoQHApQUgBwNWiNoLw51qCFJSXfeWWmiFsN9J/Xb7n/Z55tizvquOXnVdx89SI4z3lj/dsAhwtLWa29vd70J71IdGe6pI/96A9JYHnni2k+P2ejHxZTPXteYRqUnjfMxewcQTas+qlwR7f+C4mb/Hcs5o9im9804ga2lNsz8t2b7HKdz3/Uv1rrNUK7VnJnjyzXH8JNcazij2fBh7c/kftC8Tze3ftKY5nVnpdhIAdJT/lER7dqsTtO+KZ1T6J+a7qI2rVtid3GyZ5htiNWs5mFfyGQrHBe9te4yzwOEk/gvHg93sQtAfBeDAeBOPBeBCMB8F4/rBzRquNG1EYPpSlkBCN3uEgBDK2+g6DMGRx1FdYhAkkJO6rFLEEbJI0iNwkpMkrBBEWDNlgQljYktiiLIaEtTyUEnBppMquoMS9KlQbe/1/F/Z4rg7+/M8ZnwtBD4AegKkB0gOgB3oAeg9AeqAHQA/0AOgB0AM9AHqgB0APgB7oAdCDiTVAegD0QA9A70F68BVAD4Ae6AHQA2Zcj54O0/RpSFM8dShHDCKaRtwR6TH0FM/KRsbFNk1x08hrVqJ+Ts/R1XJVkbbegZ7C+ZaZDb9MU4Q2TVjeEvKAnqH3fxixohU+gJ7CWWlKqxWU0+cPhU1D++/lY+lJnuX7af7UoNAdcUpiPfpnF1ODwvBsCqxmj35pdR72mleUMdjx7aTdikgE5p5s7kbJ0Z763LwS7d2INM864vaeOIp3P+6qzzs/9x42ekhPUfg2hczlZcm1a2ZrXGjAXOkzr5GWbUrmn26Yz3wuZXsNWmLeYDZu5SUzX/kGu5Ir0FNkeszAvDbfmaF5aEREuqz5dmAdWkqE1nu56blezXd5cy8oHZZJ82ptbvIJX3JLNuRxtmqdQk+R6Sl/MgPDMUKrzxGR4LOw4jXuDUW/lYQ8C2x2ZIXNnmdIk3TfHfFAbmV6ut6abBl3vBqh9xSZnvKj6RtONSyNxno0Pg9t2Rixol9LWqbHZcNphFzyDOc1ibGep4meoecGbCkfh1vB6emboRV3r60sF1ckeD+oeGv9SXoG8iCw5f4gGgSmXxn0xum5z/X0fNevnqpbvwQ9ReF9N0lPn9dXr0v3fGEp4ZnSDg2vRPSNsT3WE5j17fW31oVRXyPyzaNcT+y5WaDeV70y9BSZntqjOfC4dl0aGR9M0i+4bj9IPiVa4po8D76/Zz71uPGn5AbRDW+wJnflLce+6zm8JnkfeooiVpRE+pD0riKlxytlypaUbQ4pW8UqJTH+oCddnZIuEYmYUkr0ZPyqGd3ATYaYGnwpHn/Ma9UVZaR57RHl5Hs5mjT4XE+h50uhRfQf0NvrWxFmbjOLiBVGogB6oAdAD4Ae6AGYWAOkB3oA9KD3AKQHQA/0/C8kdwtJN5oLPXpX0SKSdNU86BlEihYSMZwHPT1aUFR3DvSIIS0qiZp9PVpEi4o2lR78V55lPWI2a1xYtKnDDXpmCjEPehTSg96D3oPDDb0HeqAHetB7iATSg6kB9BRwuM3cXPCpg/TMcI2v3iA9c6NHRESxgp6XRvxLj7YZEf3RIH2nM6kaN7eZSs+S0SG6t0mvZ++ZJKTn5fXE8Sc3jmnM73xASd9O40xPEgtnKGKVxEOKh7gavNDNTas70nCq0bjKsO6Kt47dd5yOOKy2eeuDs39RXx3Ua0jPC/2ExPFx8Pr4ZKJHfiwvWYe2v+mca87xpXHiHJWDTe+ddfLV9Z65mVjryas3SUr0Vzt3rNpEHAdw/Lcq5e4dQggUyj1E+S+K+hQ3ig+TQdBB5XCxxDxDECcx11AkQ0ji4SAIUm8ShCYVOnQuXXJpP9+pcz/8+IXfJRex6S8HP4u/RfpezrLj51n/olceVqefPvTHpmf3n9we9Hq9+bN/RfpTzmLeG/cPBq9G1enZm+Gh3bP7T27fjr4OXx7Ni3KaZvnq7EV/lVZ19XE4XaXW9Oycp3qb/3icUvE5pUlWpsnxk/fpaZUGv9KjuFtl+/S09OG7uGrZxEH9ZVr/Hi2b7WLULMYXJ3X1ut6c1HZPB573tBF5tNd/X1VNLiNau6ejDxQWjatBh3nyy7h7ZR7HmR7fNfBNHU9L8eDBY/e4GpgeHw3w+H0PHrvnRr/v8ctsP5z3XoObdr4HPPk67mnZeXSfJzZNeKdOd3lifT99tuvYC57M+9y8DbFz5V5W6WWVwoNHeIQHj9zuTY/wCI/dI9ODR3iEB4/wCA8e4XE1kOkRHjyye0yP8AgPHuHBIzzCg0d48AiP8LhYy/QIDx7ZPaZHeIQHj/DgER7hwSNXA9MjPMJj98j04BEe4cEjPMKDR3jwCI/w4JGLtekRHuGxe2R68AiP8OARHjzCIzyuBjI9woNHdo/pER7hwSM8eIRHePAIDx7hER4Xa5kePMIju8f0CI/w4BEePMIjPHjkamB6hEd47B6ZHjzCIzx4hAeP8AgPHuHB41+ARy7Wpkd4hMfukenBIzzCg0d48AiP8LgayPTgER7ZPaZHePAIj/DgER7hwSM8eIRHeFysZXrwCI/sHtMjPHiER3jwCA8e4ZGrgekRHuGxe2R68AiP8OARHjzCIzx4hAeP8MjF2vQIDx7ZPTI9eIRHePAIDx7hER5XA5kePMIju8f0CA8e4REePMKDR3iEB4/wCI+LtUwPHuGR3WN6hAeP8Kj7PC0IPHhuVR4NiO7yZDED0V2e7X971mEEWoGKWAAAAABJRU5ErkJggg==) As soon as you add the “Sample step" to the workflow, a modal will appear to configure the step's input—in this case, a user variable: ![Configuring the sample step's inputs](/assets/images/wfb-5-c36a5713c5a6fb66913bd7e1f1d51729.png) Configure the user input to be “Person who used this workflow”, then click the **Save** button: ![Saving the sample step after configuring the user input](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAg0AAAEZCAMAAAAjaea2AAAAAXNSR0IArs4c6QAAADxQTFRFCQoM3TjngyuMKF+hJ37gYZ3j3qU/2LuF+/z8AFY9EUAzNjk9bXFzrrGzztDRjpGUSk5SGh0hHB0hJicpVAqcSAAAFyxJREFUeNrsnIFuozAQRE8qJJYghh3//78esMzZwRgSteqVekdpbPbttFEztUkl+GP6oZLvcOUSffAgAfaW/NIMSXYkaVEQJnUh6MAZRRY7imLD59R9ll/69bHl69XtTvGcjrkAEQEAkXkOCFYJJGgVWicPIAeCGgK5RI49jhPOb0ouGT/xf/HrkzP/F3NAJumwToWD+nc5Tn6/knKAHSHIv3VCAr9nfBU8hlCRI+NYLRvOapHjkKsgB/zM/97rkxOO7+ZU4f0545yQY8+vriDcQBgGdmRBNP7rOQK3CTytOHk2jVfA1zhI0BKDokpH4zVwPXUAwBLB5sh4DRxINwqk+0hSE+M1cCCITGlQsLbsnG8ar4DzPDKwTvjcBTFeA2caAEXRoF42G6+CMw0iFNKBdeN18LhT4LkT6ZHxOjjTQIS0i41ivA7ONMQKOIs1GK+CI0lD/n9tsGK8Dp7sFCDhhD6I8Tp49pmC7YhOgfEauKZBgmZjVhIdxDbjdfC4U7DKGWuAwHgdnDsFFCjXaWyC8So41wZsTyko7TJeA2ca0uDwmTYsKnDCQ78YvwiPnynY9LysQDC4Ew0o+5UYvwbn2oBsDeHR4E41oOSnjF+BMw2xE+sDnLsXhAP/IuOX4HFtII1Q5fblWx8PUPKzaPwSXNPAHkgq7dmPQtM0bTwu+7Vu/BqcOwWWItiUOAtRaNsmllD2a9H4FXj6CVOZgmQVyaPQzmraxjsKJT8H45fgmgaW08adtaHlqjBryoO73273+wRQ8rNs/AqcaWBoYh992K4KmoR53kxpmOSdQ8nPwfgleLZTYHu93nMUqHZ++BvTUParjP8njr4HOfoRh36m4eD+AM557x4fy5mCquXQMg3I/aEfhqHv3rn/AIL3fixxPht/i/ePPpme+DUNxev/mQb3wRQky0M73CbdJ4rMPz78oiG8cf+Bbk4DyhyCbuxKnF/GE844CKCTAz/TcHR9v6ZhSLcJBkPT4Jzb+tF5qsOL9w9gGnY5/cH73u6/8A4XTQHm4dS/pqFwfT/T4J3/aDLFNGz9GCZPP/YP3+P1+wdoGlDmIqP3A+z+C69zxmF5OvVrGsrX9wPOuYduFZn4kcLh2Q94v+QA3fpjQ0h40O705yycO4XI0pNyCWrtveaLpciD3X+hwJckTA+c+pmGg+v73bpV7MThxjRg4w8+/g0LlnOIx4h5D3vMC4Yf8ZedK8iRHISBh96RkLAxlP//15VtLDQk2d5RzxEOHaBc9IGK7Vyq89ySxtUXNWgdA1WSEKOKBZCRDOA+oBmCzkxVfHH8GR5wYib8B1/f+jeUKBV8VcNXNpHlwvf7pziXWwwCqM0xJx1DWosA1qwUfREil+Q66akLlxy1PIGOP8Mtnmp4zzc1/Nu/oTyWCkk1FN34ee3So4cQsvtS32Zhh8TmGP7wBSHUoE600IpsQpiIWavziaparsgss07T489wxVel+A3/hjJLxet1rwbDsfOxXuOhXREX7bs664gvMDhu0R4riJEtAryYNPEZwJExyEhAN6AbKf6wH3+GG3x1kb/g31C2UrE1kWy4Xvhe621U39WlhjHiToctFOLKcCSCDGURCcDIkmUHhonvZAijGxNQl97xZ9jwFAPs8bl/A1IN5fXYRJZbfhR3AvoUBkU6SDX4NcaLH9KoOtWQg+OwGiuazDG+hcQxAx5x/Bl2PJNCTD72byhbqdjUwAbjnu/vq0rzsXLDVQ1jyw1CPuo8rcqUg2PDHxwhPdQw/wvHn2HDXQOrffjUvwHlqVS8TAzyZTA2Pjrp/BjoZD+KlRugWSmWGjz3R3xMN8krmQ6SmSGOhKjQnXv8GXacCInb/FP/hlLWV8W1iaQ/hurGj88CrxQ1SkF9yA1+sdVhmUE90kAVwTyNKowqkRtUI1xRSXTKQNlOO/4MOz5+xH/r34CylYo7NeA7H9RykM9FHvsGRzj6zVADZJH9RPEDFsS6/kDQfSvCjz/DX3buaLdtGIbC8MVQ1AApUjp8/3cdJMVQBDVL0nQBNh1fxHH+aQ0Kward9Puil5/0G44bS8VHu6T4ZbXGPH6+wOw3EP32uaFvGedvreLymkd73/DxU6RcnoVa25Igj7lDn+Gv+w1H39L8m6uPj6tLimMdH1ndVQoKoj5DqAqyqqJAVTOKqGq/wszqKm1JUBWUAqlD8vkuox62UIpoC+c/KYhsZvUgg/7Ci73Phj/6Dce6VPTPR06zYRkPYH4bvWO6cwaMW0tLb0PG7qwYN0zacTvF0F94j9+AY14qPi4firTPfl+6RTzrB4yHZOY4O57zB8Y1Bf2Ft/gN02ywX/1TLqke9NnQY3zbD4Cb+Uv+QJsN9Bfe4zfgmJeKPhWOY5oN+LYfECESL/kDISL0F97lNxzXS0Uys/P48/P8seEA/YM9/IYxG/p2XG/nEf2DXfyG4/rkcGOjf7CJ3/CQ5kG/YRO/4RHph/7BJn5DfwwAaB3LRv9gD79hjLjZAfoH/53fQL+AffxVLv0C9nGFSb+AffgN9A/Yx0pB/4B9XGHSP2Afs4H+AfuV30D/gH3yG+gfsJ/nhvYS/YN9++o30D/YuK9+A/2DffvqN9A/2LivfgP9g3376jfQP9i3r34D/YN9++o30D/Ytq9+A/2DffvqN9A/2Lv32UC/gH18gp5+Afs4N9AvYB9+A/0C9nFuoF/APu5M0y9gf8xvAEDfgH4DLq+kVHvd0T/Y3m8oZq2bBf2D7f2GYlY7zGLtoH+wmd8wzg3InlwA8eQZUBd3+gd7+Q1mgT4psiVPjrZrWHyyRP9gL7/BrPa6U1MUIJkgksPNg37DZn6DmZTz3GCeEdbODQluQv9gN78hWS6AmBXkZOZhSVVzhpvQP9jNb1DzANy8viDJkCxqDTehf7CZ34BIbWUwQfacLUEtqSaFm9A/2M5vCDczl75QJCnQtoeb0D/Yzm8ASgB1j4h2WPcooH9Av4GdfgM7/Qb6BvQb6BvQb6BfQL+BfgM7/QZ2+g3sd/2GQr+BnX4DO/0G9lf9BnzdAaA+3hsPrP1Md3yB8dWf9gnWvxh4ajwwdSzj515u96e/P/c7MHfg0f663xCSuf3zm8SP+A2SJXCj4yEfAGsHHhyPfoznfQLMHXhg/HiCeTwAPPn/42E/AS933BuPkCy/2Tm33baVJIq+Gqhdt1X//68DNElLOplYtjkTHCvMg9jdi1UdQBtNwrDXeX8DZpff4CX4mHHW32AGl9/gNbjZSX/D1Fx+gxfhUHPO32B2+Q1eh5ud8zfUXH6D1+FTnPI31OU3eCFOnfM31OU3eCVe5/wNdfkNXokXp/wNdfkNXohTnPI3VDd33GpoTHP5D34kL075G6rpGzd36+5yt/+lP2BsvlZvdsfNdj6fqmeMgzP8Vf4GilP+hlV+cORs3MVdRyDd3dP4jj+AjOIr/gE82IYDhC9OhvhEPRm2OJYREclf5G8oTvkbqpt3jnLjpHh86ux/u1vf8gdk1Jf8A7OlAdJpwlkkQ5+pnwzbgIdKkS/oZ+B3vE75Gyiad46SbZR6/K1bPAoUyTf8AWTUl/wDeLBdvZvwbYJ9qp4MA7onEiry5fwMVA7dPV48cuqDep75G/byg4+Ljcs5wE360RYOStcwWeU18tQ0lZ7VVJq2xwmWoitrfWQoPaebyo13k0lPim2tsdVj3SIPaCzDU4RXumhlsTake1ZrpYFyHva3DGMyK8OzVhpW1aC0Rjl0Jj/Yz0C9xdATb8Ujp875G+o2bNIPjidr8cAe1VT4ZGRGMuERWpNq7eKwinDftGITDhkOHpNrPddCRhRbR5jwrvB0Ubce6RF0t2VEJhGxihTF2tBoKrLxEBO+164eEZZh41GreqVhPNLDR+v2EBb5s/0MeouZeBP/5HXO31C8E5Cz8/E8FhfHQ1KEKhIUNREFHkMz4YOFz4IZRdMZhnvMRJIhxmMsnF4fqyM94cf9HtbjuV+DprdbO2KoSBR1vFE2hLeFOxUaD1v9K9yaDMsQWCR7oegMHdlDUT/bz4DeYgvDI6fO+RtqzQ7mYnHk8+hv8IgIFwr39NCE02S4bH82e1iFQCG6qVCFoipEbskxhWBXCR1paB3ysfQMJ5xerI/gHBFSFB6u7azL1S5MYbb2z20vyPBI+pYGD9sGGZOeMRnz0/0MensTv3CKU/6GapqD//oWSe/cQzbdo0hJspWGHnlEbWnIxzRMpBzPjCGjFl9oT8McaaA8VrokqeaWhmaONHCkYW1oe9o8LeTe92lYgwyf7rs0TPd+urgqKpIf72eo4ldOnfM31DGnb2mAFM1dsvCotaLYyJaGBkVaOIzH3Keh0z1b4d4cZ0NFwlYJHrPS0I2Frym793pWGuDXs6GZ97R5FO6hnnD2/mKPXXJLw9peIWzVhIf4+X4G+JVTnPI3FLcSkLPNXY/34VFrOB5Suu1PCpVCnZHKEHdpaBRRXRGij7MBD8kXh4yUh6OsiuxdPtbbcrA2i1Ddp2FSlVGrf0YMirBGa//c9p8MI0NYJF2RWLgUYbSH0xkxL+pvoDjlb6iHmJl7sS52nyzAo7aJZUSIlYZNILZfNQ9p6AmfxsPgSAOTES5YjTxC7i2PyDnkY6uXMug9UXn/pFgb7fUVua3DKCJyeD8b2jzsOBugVuvVT1DhvKi/geKUv6G6+8aZGpop+60fAJi7+jkKd/bY/5ZQNgZz40PzcF2NmYOzlvoY03DQ+81uveB3/oL5O/wNFKf8DUVzx4/Bp/0AXH6EfxOvc/6GuvwGL8Spc/6GuvwGr8TrI77S8KG/obj8Bq/DKU75G2ouv8Hr8ClO+RvMLr/By/A2O+dvmHrqZ7j4T+FTc9LfYNYfcvriP4WbnfU3YDbP/AF98R/AMeO0vwErG37D+a5fgM/W83gTfK5/A//gfPT/g0cO8BF/3h/+nJ+BZ/yweZz1NzSX6edFTD/QJ/0N3JZ+5cB3/QJb9L5Qz875TP9b5L/c/6DAB3yRs/t/n8MJ/sTfcPkNLn77yfTlN7h4c6Th8htcHI4nxeU3uPjtveHyG1y8OdJw+Q0u/jl/Aw38//wCXH6FP8xP+Rsaq5qDj3jkfMUfUPZPXsVn6pE963/x8/y5vwF5ps/OzXnkVV/wB2Te86pGyafq3Z71v/h5/tzfMG5gx5r5I0fi034BZu65RCvpJ/U0jRs863/xk/y5v2GloekeSdPm9Cg1MJKoTBkNpizoxkSPhpKqMamgrHK6qVpDTTdbqbKkaVZ3aGTrNkwgyegyadx6ZFTKuqwkoCTrXpc1GZ75DS7+MX/ubyBdRpMy5Zizj8ZVqsos695tDZs3gR4f8yphXpZCmQI6hVyVortX6Zrm3pNuJEgHicySG5kSbpOi0qrIVK1JyQdllVBa+TzzG1y8+5y/oUfponwgy/wYKWnoFEedRMOehvXdIIE5SpptKkH5qkmxyHtP66YcyzR8fbtIawe8trsHyPcW42YrA+YDKp75DS7OKX/DWjAveWammVPbKLX4dqFHcu1nA+Mz8jRy/Rtt946E1JTTbKVKMJ+te9HdbpJkTiVNJVsa3KexdM2qMx9M6bViQe31z/wGF+9z/oaGJquSptm/pW4keE9Dm5dJ032cDc3IZ6fSHDXSUE43d2fDVHIkT0qzlFYjlPvZYEpoLJM9DcoaL3NWXJ/5DS5+42f8DRrKbbygMGcflVubdQoaJLavnvKhfGZ6fCqHKaStf2oNb2noPQ3j1RS96pNJt7XPZHUK9vcGG2rFY5R4YV7jgppUU8Mzv8HFOedvGLl7gaW7xvwYIfc0yr26Mc+UoJt0l49lesFWLNHw+N5Al3vtadh79iEak0NXugtS3bgdjysjMz0HeSprq1yfOc/8Bhfv7lP+BujFBzj4bGwa1viRD2s69DZogL0/t/5HKfsmPOy/yG14V0KKWTvOoYNdjGf+gosvfsrfsOoX58/5B/ojnoL/wnnW/+JP+J6Gn+UnsPkPO2e0mzgMRFGVJ1ObZOb6//91S2GZKKwoYsepgo4fGqojD0i+eOJUPT/Nh7/GL2nAbwCPMwV+A3ikAb8BPDoFfgN43EXiN4DH3oDfAB57A34DeDx9wm8Aj70BvwH8SX+Du+Dvxl/3N7jD34Pn+Bvg++P4G+D4G+D4G+D4G+D4G+D4G+C/42/AXwCPMwX+Anj81Qp/ATxOmPgL4PFfufgL4HHCxF8AD38D/gJ4dAr8BfA4YeIvgEca8BfA43mD8BfAb0+m8RfAY28Q/gL47UyBvwCOvwGOvwGOvwGOvwGOvwGOvwGOv+FNuR2Gj46/YSf88LHBOOBv2AOPMIwdHX/DHvjHNuOAv2EHfLM04G/YAd8sDfgbdsA3SwP+hh3wzdKAv2EPPHbysfXxN+yAx2oNro+/YRjXy/O14rFaQz5/1MffMIj3agmjdnf5+rvrn8eE8fn380d9/A1jeLXaV1x6dr6uXL1albuWfT2ykJAHufuiPv6GEVy1Kqd+r1Xx3VVsDDlxWHUK/A0jeK1Kq1+rtPzuRhiy4hD18TcM4N16Yn3ry9Xqx9ThWnYi/A0DeK2Z9Wv1WK3oE4mbw60+/oYB3LoS63dTrJaOycOlSAP+hnwuU2Z9RRrknp0GySMN+BvyuSy3fqTB89PgrkgD/oYB3HLr23K1hnYK/A35XKbU+mPvG9wjDfgbBnBLqz+4U6zTgL8hn8v+xWtrVn+aLzuZVvzpNLS52QudYpEG/A0DuN1zzeU85v54vlppWvFn7xum7zf4r70Bf0M+l93zuUyztWnqD+df0rDmz903nMqptemFNMhv9fE3DOC25rJSqiR1d1WrLlmXWT/zbtavl0saVvWf7BRTsesrOzcMO/92+fmwf8g90oC/YQC3Nddc5iu3qZTS5GWaSpmqfC5fV9P3paqVtq6/7BR6uDfY+drObzBfwjF/vbjUf5AGRafA3zCA2x0/lfannTPYbRyGgahzG0EkRQ3//18XdrYx4mwLB5CyMMo59OCHjnMwJFmJ3p1T1N1QCTRXNArMvVWBVjejQHjs358GspfvIgBMShEVMWwPQjEUgRaH/bRu4KM//Q3jOZ0HHgrhvtvY4ISxV2gofCUKcVeEQHjoP/tO4fpYRSqkwIpDi6GJKM6tItPfMJ7TeeD3mWJLiBrghJEVSkP03mmwNVUgPPaf34tsQClNDZCikAYpBhhg53am098wgfsLF1jcx2BFE30aG+p98KgbFwgP/e/sPgHFoKKQIlCzUgxv7Dekv2ECdx45FSbVmzmUVOxjQ0MjJQSN3Z0C4bHf96eBP7xTqJcGc9h9pii2zRsNWorIuXeK9DeM5/RXXhVrNAxqto8NDIMZJBQwWDxmirfXDYY1Xgy2Fm7TxuO6ndtvSH/DBO585fTWmrPX1iRaZRP22P7Idp2yAnrzl/5zY0MR1VZK8aZSmpdSWvu6Lie/0U5/wwTu/+R8XXps4c/9x5licLabpr9hHqdzYP/k3z5t905/w0TuI/snf6O93Sn9DRO5D+2fOzZsN01/wzzefWg/vc9eN6S/YSL3GNkfU38XyfQ3TOa1clz/0+kaDj9dk/6GyZzhA/vDo887eZf+hvm81mH9rPXZrzD2VG76G+Zz1hpj+lkrD36FoSf2098wn/+1efBA3+6P6vW4OzTU5pH+ho/wgaafo19hmOkn2Hv6G/4X5/v/37/xK3DU50t/w9X4PpJP6E9/w+X4Q/Y6KelvuBK/TU76G67Eb5OT/oYr8dvkpL/hSvw2OelvuBK/fSae/oYrcP/Q0MD0N1yB+0cehkh/Q/Kdp78h+c7T35B85+lvSL7z9Dck33n6G5LvPP0NyQ/84G8g08+QvJN/n4b0MyRfLyxrgulnSN5j2UKmnyE5udwT6WdIHstXguln+N2cseyJYPoZfjEPLk9hZH5tuGQyL/kD5Lvk387BIWoAAAAASUVORK5CYII=) Click the **Finish Up** button, then provide a name and description for your workflow. Finally, click the **Publish** button: ![Publishing a workflow](/assets/images/wfb-7-b9213935ca6034f794cffe362c43c4b1.png) Copy the shortcut link, then exit Workflow Builder and paste the link to a message in any channel you’re in: ![Copying a workflow link](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAf8AAADBBAMAAAAz2KTkAAAAAXNSR0IArs4c6QAAACpQTFRFGx0h8vLy1NbWuru9np+hgIKE3TjnZWZphCuMQ0ZKbSh0KSsvGRwhEBMXS+RTgQAAG3NJREFUeNrsXfFKG1sTP1BLEWdfYnaDhPuPbCIi8oFsIkFaoSSGi7R5hxKVctG8Q8lVPqTJO0gTkWKEgVZKyFlYuAkS3H2Xb2Y3YW/SVr5bvfdWNweze34zc87J/jxndnWGsyoIlDpVP0NZOLmIwemCFHV6ehqdv4fVreX0/xAH0ot0HPUdF5bLZ1T+ASwcxHj8Tbkih29jNpvuLy7jC4vLWC/ieFBFCuQUHYmPMZbyb2KK6gDfw3DX8Sg8kPxw0QJ+OsxlGt9b/5MlFEKycFQgAknEExqC5GERxD4BEojlOPIJUk8elkO8FiBpOD7DSJs4zIUg9osJxPGEIEou1pEQdAIxTLpCSBCOC5EcFBAfE4dpTIyi8JA0HN8T5QAqiZhiYRJxXIBLAvFYJoCSh8eFQH6SiWPXqCiZeAxAJRCLgCCSJBJHopgVSh4mYhwXShaO58X4lhgE/gSO9VrHeEJ/KwYdkR5o7dLXeq2n20uL6f7+dhwzof9bPvDHeOLcy614k3JNMf7eGdqZmpzd4/xG+c1Xeigt01Q7aRHjf+hMYwBtRPMVxTi26tkpL8Zc4GMnxrH9JIYPWJPKM+SyRF/pHUtPtZcW0/393VjqkaznIOKiF+G4CA4JmLB3tiaxlGkMLSFAGXXM5nCdpvXasSjGcYup/v5eTBDfHq/R2rfXSQGBSDT/iF5wRACN7AHUELdINIy14Lg/thoLiC9H9I5lDEICKB4PQgJ0iHUo1CMCtChBKynf6p/LD2FNgg0a4en7IBF9xq3gWQPcBfCVeD3SypUKAQkB2mfI9sBnuMYt1hJ4Smt3geUkhmKrlRgQ1zUIAZrIXvSFAIPCjsVSSfdMgA8+gL/gkvTlRwQQI65rPkYNXJEQ8Sf+vn8d62dNYvSsMaUfHcXwEy75LunjTPbQPSocZWrHmU36kn+fyzaFANrLbGpxiHuZwnwJswX/WS57qI8KTzMFT0E7X4O9Atu/vmEDMPYK7WW+nN72xh6aG1e4rru5zCv/KN/pbT+np/mmzICj7Evpb7kD87nsKwIm4LdcQaub7cyOVsMSywb589xyJ/qtxdGtH8CfVz1QunT47ai5mPXROgjoiziCoGrauGIj1gaYQ1y9sVN+mxUVbjLH51/5k7px+ODWTYfNCD7ji55twROsHbHyuVu1HIsJuMJ0EdFkAkjONfZyQ0z5dWwwAYhowe+ROSs7xATYiK9D2aHeZVmzzxJZP/ECkvIDuJdrElyt6D/rJ+Pl14jmpjfArG02q2jZaOZwbYCYxxQT4Dlm2U57yq1iOfO+hMuFPi6XsFYPzUhd49oQ8bKOpw63sIwqohBQxdoZmmUmYIBWDlf7uMUVpkdr7fBQ2Kyb4QDWdooUtaS3tGfLWEPb2sO1vkgWvbvnA7jtVY8nQKz/Kl4OReGd3hrh9262cO0a0wNc9R3z1E7xBQZVy1NG0QoWNF9HUDeb7Avr+PpavmDXZltuaHGjoCo9rJy08MC2lLYXA/YBLXx9Y7N2aQ7NS0duq47JfFW6B0HR6uIvwSmJD1gfYjRWSsZwFvu4JvTfPR9ApgBPgFvyA2CYQ0z7AT3BShUbPP5QCFhyq/jOTg1w5W3RAiYAC5fiMN2i+XYfl+oYeghF/F1Nu1Lk772kn+AWU+i2MIfPtagH+IIFRtG8wF8+WPjGTpMSJ8hDGQFVzXk0fxs5QRb3w7FaWHjrcHcvyLH0PeQDGO1VngC35QcYN0eYCs5zdkjAZxwTUBcC+oiyTJWsz2VgpXZEssarmWxLlobVWnTW7PQnfAFzTIDZgVboNogdyAC3imbHqGLHSVfT9g6uE8htcA5f037GNnnd4HNSOiTAbIdj1eUoBIjhfeQD8POsf1t+APhK5qiNOCagKwSsjQlYLpd/ZbOuw3ohoGiWy+VDcWd8hQrqVjVdXbSXnvyZgDymvd6IABQCLoup4rpTYF5GBFTaIbFnNlqgKJoBc+FYH3CjXN7pM1dseB/5AG5p1bstP6B3EPh26gPutCeWwPp4CfwnCAK2d7slXI+WgBEE4s+HNvsAeGI6S3WLuZElMCbg0LYgXgLc5LIu6yTLrCmICHDMA7YNzh2WyRIgIXuLx2phhftnApgSuo98gKuVXPO2/IA5c+cYF6um0RICmqMlIPdAa8FOsW+C81dawfzlNb74jM8vf8eXvX12Y4dXmOb2A8TKE8QGu0Ti5kJAdBcQAkInuN61U5pNmnU0QaloBrxkF8mr4yT4IKSwp/yCi9wFnL/kofXxYUzAXfMBjFKNbwTfzw8wwjX3oo7lSR8gdyF5DihiVpwdFJdLWGHxmqjkLmCxPbcfIjavkC/bwQxa7oiAOVwbEXAt5mt6wCYtTPkxAdY2mvP2hmOB+ADTYa/jyFg3NmbsNPsAYMM75wPIBPD4RvD9/AA4txGtyzZidsIHcHkty/iMK6tEhsNWna6NKS98sqmjYJK/GM3ONV8ZtEdPNh31AWss6aEQsKRLbN6kLlr6Che9iIDwhoM5fBZ2ruAD2mg2oC3QDR+RorvAPeQDyDMA3wjo+/kB7nk+/4b42fXddo2fWL/kD7v5zQGu5Dd92i54+jhfuOTW89tsBcf5VzTczh8EddxnrIhgr6B725uk9F5+U7tHGx04yzdob+OUG3/Jv9Ri7ik20V1uTEpxg6f5wy/5QneD+9uQ592zjfP8K4/0fr7QUTd7+R2QltsFunM+gH4qzwA3ucYt+QGu7/oKfEMHnhuQERAE8leMHygVBApcCkjMxUqMNZv6Rh2b2g/bi03AVTcANnTDj0g1K4zAA83mSkzCDwFFctcPQFoEWklbl9hIC9Qs9MlgWza8ez7A00MShg9vzQ/QUqURBgJQMndppJcKiDw8RPZMQENDHHDRofib8XgAmh4v7k/RGFN8kwJ9j/kApIkx6b+WHyAE3KoXAh5GPgD8WH5A7+Qd3aaHjyedR50fAIG+Va+0nuUHzPIDZvkBs/yAWX7ALD/gseUHAIWYCCDS05Q9Pe78ADlEWMPYONZHBQL/8eYHGLvZDuPPmcpTrowFFNt3M6s3bw8eW35AjI2qKdf9B25VsSL6SBDbdzF9Y6dpuv1Dw9+Nm8v1Es+AkADGEQGxPiYgwg/5TGMApDQp+YCW6wWlPmHlLNthTMAC0Y7SOvQwImA63v7A8HS8XPc6FJkwAZcLWmYAnZL2LlyXBadaKXfh1CfSF/EMuO/4/b+XHzDInnAguJvjf8ZlmlWTY8PeJ9xqZxq9UqbAjHB0mIAjwm/IaGc2IgKm4/EPDE/G0/uYQ4lwpoM6NqtoIzaZgDrWWlGoROIXvfG/RDEiYDoe/9DwRDy9jxKnHeKiHxJgObjOPuB3rHEIKMOCrIOVK1zexcocZksRAdPx+AeAb8sPkEDwx/EMaA4wzbdBJsDheLBblVD9Fmtu7CWeFUMJYv+ip+PxDwxPxtP5+twqfhzNALPDcyEiQOLBLgsGuFTleKD9S6hM+87adDz+YeHp+DmHIJiAEzsdRAR07cV+SIDEg42IgCJySRdDAnRpXT+m/QPGBMgMGBPwR+gDJB5cGxEgEeE3EQHeY9s/oB9GfD/ai6MZcD32AW5vF7dGSwAvgyDgZwKZAT1Qj2r/AHaCXdvqcSy4KAQctnEtIuD8cjAiYK2Fzxf2mnV82ca05zx/XPsH9NHMiGvDDDIBaNpY+xQ5wWwJaxEB12KzPifKtKQGPar9A/qIaB5KTDYnM8DGFPAMqOMbSYczIgL0rth0WWenh0zAo9o/oI+b2zs+JykW5suXxztn+UP6Uq6dlZvz2xsNfVzudMsv9c1+/oCM443m8Y7ee/O49g9gJ+j7pCAIIFCub7iegjBODD5X+cxIKdf1QQWaXF8F/uPaP0DysQTBCMdGmqbj7VowPLL9A67wBSV6/4DeSSPh+wf43mz/gNn+AbP9A2b5AbP8gFl+wCw/YJYfMMsPmO0fMNs/IJH7ByQSS6Gknu8eX39U+QHJw3G8PKl4Ol6ePHznePujyg8gRUnD0/HyxGEFs/cLzN4vMHu/wOz9AhPxczfwk/1+gfP8AUUY9Lf0egpHUBPLQc4hGukZxvaaHsT7BXpO3oIQg3eqlR7rdVQjfQHfird/7FwIZXyW44WO9O6CQScAY5OH8H4BaKfdeiXEtJt5RRcU6eFirDc734i3Q/FdpiGo2GC8kOlEI51lVhYyo/6Nau0hvF/A2G1Sf4kED8zc4nDFk0kA2t3tEBDrizugIMoXIFZxEbErBBAQFBuKVC8jxmziZO33JrAZm7rVitI//fsFwLvw1GBJdNBa895fL/ouaS5G8VJ7LimdMcBVQC5BwBVfcROD2xWbF6Q9g4pNuVqpa4KuBR95BvjgKReCauUBvF+gV8o2oFURDK10oHfNzWe5TV0+Ktv5i9JGB9q40X67MswVoL2/fJ59o1Vve7nDE6OxfVHYLehis/2caGO+UNokNTQv/V5G7WUP4Shbq1aebuqfPD8A2qlS+ibXCXHf3DH+1971vTSy5PtinN25bqrhqglDOC+ajoTgXHC6IyKjMGkVkTEw6sgg5+R1H+7TMSoiTsN5uiz37oPGmSshCeyfkMnM2UOwA8VkfhC6GpqxQwjp/l/2++1OJuPZHsi4L8a1Zoz1rapPddcn36ouqU/y3Y2uZpbEqiQtw8CjmwX2h8hyVo5lZ5R0NqrIizGT3o1KPxFwfakSSUSKGwXlBSPicGRJLDBdmino8rCogJ7s2aOMuvHTddcH6Eqx/fPbOHNtXYk8acTsipFRpaeCXIUpQRiXq6cxohTvxsvT72PNqEnq1aFH3NgoKhWxmklvvJq0CQw6amyolL6MTAZkctyctOJ2NXMQ5dddH/A23lTbStGzaWs32oiZ5pmSlqqGpL0Xt4EwuZpda8u0ESuvNeKtKCfGb7uPcPFTKlEjm9qQ1nARrEzClAeOX0lHolDfnxyaZkZGesSuuT6AKkUrujlnerbBW+KHmH2akNKSRiVN3xQLLgGPdZk2Y+XHSACjf5AXp5jRISC9gfAAeIBLgO1knsrDytLkHSRgKWZec31APWbqezPVjv1bsYUeIBczQABRqhzHRJAAJlWteHntA3qAkPnp/RTjXzwgF6XoAUgADfzobPws3o23Ji/iTjVzJGvXXB/QfMSEc4F5tpCNLsab0W3pAD2AK8tvt5Wi5wFGZlZZy6IHIAGrG4+AgKLkEVDcSBPd8wDWEJfEc/FubG+yKe7PZdKZx9dcH9CcstsJ1rXvKdEXujJ3Ku6qixo9jcK2Dqpk7c1j/kmaqZ4+bsy1Ehx2e1vuGrBYSRin6c3i2zmmJ4ZnnIxKdHiKthPD0uyyvSs+zagfZs3rrQ/QE9qbOO/aRqBk0gDVSzCpKdFL/LzKOgAhoEEjVmIlQpm78ffioAagPZTyACmBzah5bmCZEICcDZ2Urrs+IJuQiz2bMuK+UnhlHBLaHoBybw/MaacnztwSwj2LcK9nzhGLecgMgD6gtfWjOXDn/X/6y/en/y356wMExx64837yP3++Qvpvf33AIOoF/uPPV0qlG6MP+Hg1An65MfqAP16RgBuiD7g6ATdEH3B1Am7Mef9VCbgx+gAk4JfvwnuQb+gDKOeMcU45xSy/9vYXAvrGE+ZB/PUBnJQCnURKXvZa24R2CeBQ1A+edDnz1QfwSm7AUp51/LnSN4J7EN/4AoF8iQxUClSOP+JoaCXfN6LgeYCvPiBHGVho92qvtc1Jzh2NnqeX6nFA/jaFQboQP32AfswG7Xyf1/8Oo/nrsAb5/vB0WHMJ8NEHkGFt4M73uf7/SECF9o/Xj/+IEB99AK3wATzvRwJ+ybPvwOc/IsRHH0DzbPA+/8//jqM5Zv3jacUlwEcfQPN84D7/zygS8H/H/eO7BPjFF8izwfv8f5eA/vEdAnz0AUDAoH3+v0dA/3hacZ8CPvqALgGc/vN5OmUcbMo6NuXfPG9nnu1fz3FDzsDGfDfLOPcs7lmMUwbdMI6tu3ju4Rn1QIRitkdA//qA7hTw0wfkGQWb5hi8MG/6dOv10jDBJwhHGzN+5+3DYNcLPZv36pnX/Jydn59jebvKApClAciWBF2z0dIClJpaAE8cEBYggXPBpoSXGAUkqReorhHAY//Gue1ev0cAJsOxf6cHcBihNtq0e78dAnz0ATzv5mjCJKSu0fzX5+lD6i5lxBpnnt0M+5y3C89MRq151rXJOevW1zW3PZvFiCVanulKVHsDWXWTCpliU4o83QBrflMTMuoSYY3pxiMmnBbLEfEFo+8fMaLHTFpb06U1QBXdg+zIHF6/twZg/8ar5Z/ZpfN/fYuQptuy7gZM7HmAnz4gzxAriDanFyqLA8Wc4X8Cd1mdsSkSQNHkzTDnyCa09yYD5CjcJOfWPCedGWIsmwzQ6AdDKgEAaU1ZoYODQJwPxTLT97a2kkWFNkPG+sxe6NVWcF9Naq2gIcF1/tMK2vSk+HplL2QK6yqlzWnGawtWXDvZeq5B/1w6kDTktrcGUEo/jIyMFtjX5/+tUY1cQF9AQ9AGu/cU8NEHdNYAIWob9J3ajtuGIzDBdKhNhKXWlMFsa5ybBjFMBwgAPqltMIPaUIDyINIchybvF0ybYCG0nAGowHB+31FNyFErZT2wnXbY2Sm27juOI3NFOEk1g4Zz7rShIFnNznOZcmvKGlGRgLQDnIRsQmtpg10sXKScjOEwahrN+87rNL20CBIirIfOlILhcGIbNrfR9+tAQH3b4I7ZDKLGqecBPvoAmnczgrw500omFsfmXr4UCy15tvUj0+NW6pM8Z43XF2cZFDXD7WWM3pbQ3spP+PONJ7uTjFykP8mr1qz0lEIhW9l4NjpjbiZKs/abo6Ss7iYKtFywxm3jdGxV5lw2SSvsKC2R1tyJpcN7nQyIlMvoaVYibCMBwnqxnMIwXtonWXmcFNOb5wZcd+b9OLt4wH73GKyPqI7J68+L7PDedvuQ1Q9dD6gffsrvF5pB56yA4+s8BXz0AV0PeLiSPFpffhbc3pnJjJdXthr3zcZCWT15/twaf7OSLJ6sPPv0Q22K06HY3rF0INKHK+JsUqXZ6s7zFWtsP9SOQKGU2B97boX2fg7Zr39cXz4L7anCJry356WXwe2EKSiU1uYNJfOIl1MEErzRQnJ3ilHJJWBaqroeIFWTwKax5OysJNcyM2oyMsvLs3tv5mlj/JIHUEqbYzjzkyNwAw9Hjka1WthEAhqhz6Mj95vB1mjhKw/w0QfwPENbkJ07qddqy3XU8EnR0Y9YTd0VdoowBXTnc1p27GZYqRJjpwgZ50SVoeW7lLDorFed9+OO2MBCqWpPwi9HBwLSr9XGlGOyWdsajcRaYTNqC4oGPQrJkTR9ncbrux4wkSIdD1g4WcA1YDPUGt2KaHrcFJ2Lhc8p++zsYXG9atcWSAMdh3YIcCe0hZO9MZabOJ54svMgmT6ZJx4BF8FXY81gOWwS2iXgrz76gC9rgPk5deISUNBDQ2MvqO2ugeUgOLDx2/rapMmao9AZvomwKrxLydByaCEQs7PRAiyCi1B4JyWbLOSsa8QlALqLrLLmFLfuHxw1gQDTUCiXGVfe3rfLKbx+Cwn4NWQaEkEC5pshIGBUfGFNOSfp5jTwU1u4kyKOU07LjF7MQ6NLU4AS0gjCCGtw348nqu/CJ/+1rnYJCLcj98Yeql/vA3z0AeABaAuy+Q4IqHsEtHeDnOEayPTNkDV+N4oEwNIdtanxNQG1BVio29AECZjvEUA7BNBfJ1QrRa0Hjt0Kc5lTGbwHJ0KyWJvCZ43rAUJSFZIav5i25o31dZgC3L6YhyvU0tDNBRLAKVyPUQvXgEs7QVS4jHEilH8wTqYntIvwxf0IJfUvBPw2MlL8+o8hH30A9aYAFZEAbwqAhzsbGsU1kBiOYj3YKX5O4RT4YUdlAnh84wdYjXEK1BZqaWgiIwFQeKImTBJyp0DQQQI4eHC5QN/DYJveU8CuzTPgcGgKnwI5zwM0fP9UeMuteTI0AgRQ8HTAZzU96rwDAqgAfSpVx30K4P1zjwD3zlsTqtPqeMCdcHM0ZIIHCE7HA4InU9iqS4CPPsAjgAmi/Tn1eiUXPNxZzcy/zMnNOK6Bwl4uYT1Yz62ndlb37oUb4YvH5bmXR2JOqqIHvFvIasJ+LgpvnaxDoSaaTDysxV6mpbOH6dcrZ9vZ9CaHRZDReugQ9wGwhBAgoB2pJmf2o54HaEzSasG9Uc2aZ/qES4AunknVJdNIHiZTd1J64kCsZudOi94+gFDy1VNAWA8eJPfHKrAGHK3PswkYcH3k4ODXDgEWzhDCOgT46AN4nqFNE+ZF6kNEVeI7Csaem2vG36m7lJ6CbnL6jbiV/hSZhNm8ODTdkqLVl5FVvmRnC3dTSzY2sdaEJQ6/2ZLJM5NtRSyeinsQe/dQipIY6pA5YUq8rUQ1usSYsEiF7E+4E2SBmClsaMKbNbYRecKtNUKzxbJKCHS62o6bsPXbTw2l3cq6FNXeYqBb9mUKQI4w3AgFS/gUmIANkbCeAqcYGRnZD30OtyfuBdsTKo6vQ4CPPgA8AFInNHSJt4WdgsCMgEHpr9ohd3ffDF8EXaDejrAN9YR3tv2HjHBsAv05XqGOW3vGNZvQEtcF/SnzGMZywb2wt1PEvwV6n7ugdslGvPsDCKNk158SYhAbEVhJdYGhidW8twZAm7OtAq9vFfXIqyPwmSJh+v7W88r2pyN2ENjmrwo4vg4BfvqAPHMtjjYnHHyUgQkQgXMsR4t7o6UezG2OGSiwKeY9XjGDAPzhHCsQ5I0SDWzvoRFAsQky16t3+e2UMGrYmIMyjxaODaC4twgWOvdjOK7MRZ+o2qQ1hn04jiPYgo3h7IhjIoIDAf76APSAS/Yr7drrA7pT4HK5jpJMEHl6Pn4Z5+MBrFNJ87+zjQHQD3QI8Kvn/ngkwF8fAAQMmD6gR4BfPfPFIwH++gAkYPD0Ad1FsH88EuCvD+D5AdQHoAfguUD/eCTAXx9AK3Twvg+AuidDedY/nuc/fksfMKwN3vcBdI7GvgOvH3/8lj5AByIH7fsAht0pUD9m/eJ5Rfu2PqCi8QFLgbw7GpbT+kewb+sD9Nz5gKWc5hEQyPWP+LY+gNBAbsBSiXdGc943ghF/fYCXsflgpe5oSP8QSvz0Ab0HBeeEYc/wj0K319imeL9f+7NrUxwP1vrbOF4ffcDgxgfwCPg+vAvx//6AwbOvrBXusDH48QE+/kta4cGPD3BlD3Dxgx8f4OoEdPADHy/gqgT8jXhp8OMFXPVDUzclvsBVPzZ3g+IL/OkvV0ilGxRfgF4BT9ltfIHb+AK38QVu4wvcxhe4jS9wG1/gNr7AbXyB2/gCt/EFbuML3MYX+DeOL/APB6DP6OA/C40AAAAASUVORK5CYII=) After you send a message containing the shortcut link, the link will unfurl and you’ll see a **Start Workflow** button. Click the **Start Workflow** button: ![Starting your new workflow](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAf4AAAF8CAMAAAAZySnEAAAAAXNSR0IArs4c6QAAAFpQTFRFHB0hMjQ4IiQqf4OEi46Omp2eq6+uz9LSv8TD/PnvRYBuAFQ9IWhT/OKo89SE8sdF7LIR0y1JHlRtHpDCHnCUGzpFHyInPD5CRkhLT1BSW11faGlsdXV2GRwgpp0LIAAAG0xJREFUeNrsmN1yozAMhbGBtly0ufMPtt//NddHchglpsmSbne3rT7JTKLBk5l8HJhkyMoPZiBKwcJRgPdMwQmCL7WjFGusO/4ZTHF1d3v37b6rn0EZrMEVcByHjXW/8rUp1ppHsLao/G9AGQoxdIunu6h6RVEURVEURVEURVEURXmEUg4suaffz1zt0X/uPolyFEeUD3Dng/Wv22+sHwxucKr/r+HcH9ZvB03/f0+Yn5ddnufwb9OP+Bf1/5nYebnBbM/2IwhrWivBm1qBRx5lCEvEFSRi5VO8AVaUgM/3fAYRE7YbC5xzQ11WYmo1DJXAqdID2JflJi+26WeneczjmNcIVhZMxADYMs/HcaqVEkxGIhARjQMKHVMGfB0xeQQeci8wEp54Qo7V6e/j5uUO81l/QvIhX+qvq+lHmaY/1xqJvCbsk/5ZfmT5WDmPtWJgpP4t6GbHv6W6tG9V/yHicpe4pT8h/ah30h8NQfO8m/7YOkr/KYNVph+bx3C2bs4H+CX8fvqtpv8Y83KXmfXD/nX6Kf876U85ifSnVaaf7Yvwd+mPIv2mlgB6b6XfWNV/hOdF8vp2qrwtFzxvN//aN9MfvLj59+mPm3xWz4Tt2Y/wy/T7Tb1pbTb8ln4Zfn32H2SRkPz+AnADSMRITD0kLK0gcfoxRAOxbTyTMWHmWlObniuPxgEj8YS41HzwtXkQPDD66+8x/Sei9z8A1/STwZl5ohJkhtPP5ud5orqiaSf6+ZiBGYBhPAoEz88Mz6Nwjeo/QJ/93r+70I9q8juaeWpOPpvneO/rf9rXPzb9xVz4R8irfjRGVT9KoOl/TP/r6YLX99KP6tMPv6yfS9wnJiqhemLNdfGg15+79HvD9tGB7HuaeVIeMMILTf8x+vDL+PfpzxP8wyR1n360TP/UdOO4gSHgS0iM+5t/EdlH1mX6MYFzTf/H9Z+ueDf9yLSIvySz/8TpJ1gq6sCzv0u/iH/4xb7Z7bYNw1BYIwoUu0hS8G+9qN//NadDSq7gdC6WOL1odaJIFCXaQL5QMpz4D9hn9ufin9mf8Gf2H47fE8Nwq74lXK7FUDqApJY+J16jMG3bR9SoIJ2iAozj/f5fa0H1sXzif0T2L+8qlAIZLyEAWaEMHq+R7ak7WF4LwrygX23yhrcxhruzGzk6takoIRzX25EHzSd0H4sf6vhDK+x+e94DNPqw8eqC1YOgBNpEQDwyT6vJByX81NJV5u+9R1767eF3CtIDyLwdn24qvs3+FDob/ORdFK8R/0hzy75n//y5/xj82/QfBjZ5lsv49lMOP+RzFf5et33mp/T98YP/SH/inz/5TPw/Bz90bvAn/h+JPzXxT/w34ncvx2j+Y/dr8Z/O73v/+fQhflZRK7tSPQYmiTWLpcomqkfonf1lo/MVfmdhVqH/x7+Y3IrfWcxt4n8ofsC/0nmDfxH+PI9VP5pwO/6Ck9LE/0D8p8s/dBrxJwmIWLEHGJsqkWp1uzqrUsv+HIfSNBVmRxxTdZm1cTdu1cJcDVV29E2ZxOAkFmVDp53VGZEId53/6rxXjf4u/9LVdn4XjT2A0QKscCFROL2o4h0+KPYL4sBvosZCRRGX4wausZ/UvmKu1L5K4Meor/itRakW2Dj0vDq8V89Bf5f/c+kiFgEvr0U4Pn8SBq9oUQG/CfXl3lULxFIc09w1pkQMlN8cLiZkYu0QaRgc6+KPgAWnrKXaUU18BzzmcdkVHvNY5W4STMgCf9KBQYlKAYVFFYCSu1o0MVqVU9KDRmuJqnkU4w7iSPUBP9pYD1DQzuuB+/WKq749nX+/llEkHHyjyVXAE3/mOooYVCA3FaGGn+GmET+yXplQrfgXjAd+7fhtwF9UTVDNtf8APV0+0dOAvgRvA+rr7I8xsOURjBdqUxN7b1WyI5itHJs7Rb9nv5nwkP0Rhcmcm8Vc+48QvezTf6EVpCF9VQgM7Cr7lVyFQMmrXYwRszBFxrKQIcaJraiEXSDARJWbAzkn6CTev2cLDA6H4pRwzztBx4je9ui/UekCZhG1Qmh0m/0qIBLsDDYn3bQQ4tFRKprDqfWC7y97Z9jjKAiEYRhh6BeBXC6jVfn/f/NA99ZqTHe3V2+3+j4fBmgKTXwyQAwpOU5DvGd/+UF6z/6pt4wTRwmY+59EM4Rt4tCoJSQ5pESy9ZZm/lBkPg5Ec5EUybRDuOkvMoUxTsNLDnLznSRTnd4GxT+4PZeUPvuiPt15SfdZtlbthONhL0yS35/X/+sXHtjB+OiELY7iAwAAAAAAAAAAAAAAAAAAAADA90OkC83j4ZvKOR4zaJL95WtNpEQV5MEwF/+zVLKIxwu0+1U2Cdcl/GhIa7UfAvk/HdKi9gL2fzxCGvbPx/6WROPZvoZ/JP+JIY3kPzFpl90fIflfBCLM/Zj9nwvm/pcB+qF/7zFb4+tNvGmh4OD6ydR3MNgoHFo/8TLhY/TLD+D/yPpXuR8zq/yHhePqb+slIcZQL8H6f1z9pl4Rsn2k/1n0+4/1e2g4rP56jQ/1Gmg4j/4A/afV70OIMQQP/SfU7+M7HvpPpt/HW4KH/jPp93HF7P/rt3k1rVbPQbfNXNeaCPp30D/b3/C/UE/VxV2MVvcQdsOT7vSrnFUT/cVlLPTvoT9usJn97AqDukvWv+1Sqy9i/gpvnTMDQ/8u+kPcIKz1TxY6aSt6KPsvTj+c/ZVjUQb6d9E/6fY+jngfYmFD/9W5dhrFcFkDGq56tk3HnBsddz1z/5b9nWV7VZlSvdiusc4xi+pt7qCUtY25WK0yhrvc5lZpy3rsl9tXHjrui34xXJUlh9tRf2OYK1p2gv5/0D/Z9teUONcCp3T1N9v/Reey/vakJOtwjqV1LlfGhpVhbLhOiv7cYOeK/9Q594e9M1tyG9fBMLgBkKzqOnMRaiH1/q95AFJ27LSrk0xPOao0/kpzAQnl4jMgSLZsFHjqgD42+5yZ+hGOEJ+JAmwyD2rXsXY1tsOyK4p/VfwLEcvE3ztZ9H8C/xHzcd83xf6273uKd+Uf3KuQiB0sK8xEyyJ/mQl9JW6ccqKk+J2iXylBx5u9V3BOnWYoFMUpXHPJ2nwJc6Qwq98mm+Qvrk5cxW3rRYA2GWXgmeq9k+H/BP7/dVXB7g/8ORzGH/Hv4AuSonBbFS4NaaKoJH0lBKjEil/miEh8vGQ4uI6/qJ0JMwvUTFQAQMZz4kQOaSnEoPZN8GeAQKIA3/F7WeqDOyfD/wn8LfV37LMm/0UGeFf9PSnHWw7nn+APogKiXFuaP/CzmOt2jx8iVYqF1PWKvwh+UPyciN17/JjvnAz/Z5L/wR9rfNP+LdQr/R+T/+7Slj1S2jTPM62P+BXdffKH2UNTFphu1r3iMIs9g+JfjkJyI5k5aWJfV/uBv4V5fEj+AbS5c7LK/zP4Nf0/1/vKvxdmLUfXRO/wU1Kj4m9xix1N4SCz7Igw5NTsNTNxZEJQedLD9EpR15kSdPyxj2Yd3Gbizw48X50M/3+Ef236AL8PTIQreCSK4YY/HPgZiQso/p7x09xCX30WgCqd91HtDpiw9U1J4fY0ruscPNyiHwAJv+OHdrQZIF+dvOH/HP7bXZ99927fn+N/fPJs752OoKuhgO8ssux89IHsW+uytExbfg5OzPnDnw3y2W77/Jf4327R/4/oefT/XHslfMD4ZKjad7UIfntL73Q3ff/5R5p/hx/mWuCXVetsfE+C/+3D0u+Xte+/vtV+v/M0+DX8n8k+7vF349/fPuRvH/T+Io95POdvj3l8kYe8np//7SGvr/SI59uPGcCC/ys94K16u5fM0R7wtq93MNmXu5j+RvyqHZ7IbszYN3uZDL/J8JsMv8nwmwy/yfCbDL/J8JsMv8nwmwy/yfCbDL/J8JsMv+lU+F//c4I+G9KT4S8J4wIfKqbnMH+bPq3QlCsyoz3j9+fxV64l8ock83P8M8LvKd/wV168I/sg4Z/Hz0Ua/2+if2X4TbkrfqwAhv8E+DNXaPI1xlmYbnOIzoVUxBR8icEd0V9i7PCO4RK5lgyu6hYoyxKO9SVIs1ZptiJ/MZYs3bbEIvh1xReOZVb8u6spzABhAcg68MEK05dGf6QwS5cxlsgOCqeSEtbKBTxhKMgeFH/CrfB2pO5tS25LHGqeOayBnSynEkjXe1gjOWkKBK4FMUNA7PhXXnKlVBt+x+LFW/sPVooAG1tx+FL8PjDhIj1A5gKFPTiqAAHBU4HdcVE6K/tbuk9Bmh0qS5uiGiokzAARj9TeuMJMbiY9NBcINLfSb+W1n3F2xa/YoXLeyENInCHZI2WvxK/KK7JT+8IVihLWKK94FOoYckw5cIwxkQdRobhmaPiBUeyYGrfurK65pJJaTyCSxYCg+BNv14JD8Pczz0LO05p55sXzasxfjB9amBdM8YqfFD/f8GuQBpzneZmhaY2M/sBf1e4e8M/k0ubJY+2WPaUr/oCx49fJor1un8V9QQhhtdz/6uTfsGwrixmf4M89+ZeHa8PseOv4MUA+AvyW/AEre0iVHKzk+qYD/zpzucOP6qWZf+NQYeFguf+l+PPKxc2JveJduePPt+QfvQ+99PMcfV4riPbNgeMFCrtZfLbs6wKJ5yxjaAqcQJYRIGPyvrI78PfS75b8hfoKM0Zd4VnMlvtffeFXkCjN4JAxxB/P/ZEJl37hNyMRXit/5grgmFDjljk5SIhq7Jpp02WdukSMC3T8jlb1djf8UPpXOAIkXQ+W+1+MH3K/Eb+DyzfT3U06D4f2Pd/G3eUw7K7hi2q8aVf/6+arZd/3Nti7oTf2APk53vLZn9+k+7kUql2x/X3v+OUwwy9qL/YVnX8ZfpU94W/v95sMv8nwmwy/yfCbDL/J8JsMv8nwmwy/yfCbDL/J8JsMv8nwmwy/6QT4X/yzDfbxkjPj3/O3y1mU7QXwYvy7wD+RsvF/Kf7LyWSf/H4B/pPGvsoe9XgZ/j1fzidL/y/D/+2E+A37y/BfTihv4f+V8X8z7i/Cr3W/1X6G32o/w3+ZaODJ7vx8VfxMgYY0/elzv+H/Q/jxMqWBxkfrNAaxfKRx7N3UJ9OjO8brMKSU4mTRf1L8hIqLH18A46AaL9P7tDCmw29QhzCQToZhetw04O3UQiSrdu4/L37VSAN2SB1nGCNNl/AeHA7XXh1Z0CptujzHH4eomyz6T45fSQmqrmmgK+OW1cfeTvqPhmk6EoRubE5pSDLuuybdJfjVME2sB+j4p7Z839h1/1nwqybs/BvV0BO8aBypQx6YBx5UR4YYJTu07E8yjGJXLyIcWPFPYiU1NvyTDnmaBtYXGuora7Lr/vPgVzEfAxRWODWCwphH4TgJb0rjKNSnY09SsDwI00Hzv+4aL9R3oayFyyTNqLvEHKY0sPQX+ZNmIDv3nzL6VaOGeQfXlGQytImiUylwjeUwxNBgj2pKFzqKAZTxRc3aTKNGva7JgcRLm2Tn/jPhD0Thzj7xgAf+wDQ0/Jd7/BLzcQjSkTLu1Ht460A0PeDHPhuHFAZhH4fRrvtPWvl3y4FfGhzxAX8Xd8QkXUPcXQ78OA70Dj/JJiLmCxMPVvmfBv+EA0/35KMW7aHRa9UaP8EfBl3Rmg+1xbbrhr9dDtzwi9skVmplRVRXtOv+s+BPA4XHwFexLgg2GohI8dMR83R/faDd2K8SlOmBv4/GG349JOlGHUzNx6L/DPhxiCRx+qgpYGp8IobLlHCc4nRJUS06u3TFNPXu/+2d3XKjuhKFpxdqXezat0c/rc77v+YJyKbsAQw748zYzPogydhSJVN8LKmDsNPPGdWxIejU6Z/+6Z+xy/Tp3xDD1F2H6VIw1/tfZcUv/sv1fq73c72f+rneT/1c76d+pp/6WfqdTv//qJ8v8mLhz5d48jVef6H+D77Am2/vwLL/r9X/Sm/uAr65y+/U3/3/8iH/mPgx7jfb3RPjfvv0/HH3bSifb+xGvlU/i6s3Ad+hH9T/HbyLKXD0fxMk/Xg+Hxz9Tzz275MY/7cJP+N/XvYt0T+H/qeT6P/FSd9boAtPgFcG36vn4yOJ8ALAawKI9JWvbz0BIOQlQfodix8fXGJ5NboWLnsSQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBDyWnysbGI+kDPiJrem1/Qn8yogZ0SqW7q1vSA142E6MdbS7HpFv9H+uTGbrY+D/93HhzgP0Mlx6d7X5n6rPD4np9rm3P/DWfWdHfEfm3P/wMNzeoZN/T+o//wMTP/fzPDx5/Xnwx1f5ieeX/8P6gcy537q59xP/Uz/b9Uvpco76pdzpj9ZHAmGTXKcsM32oBrk2DEW1RDtUUcJhoeoQYJ28qOfqIJcJuSX9adQAUjJZ6v8Wxxa8xgrNhHRQWSzVb0Uj+WIftEGFJVHHUtDDtopWGIClAsPv1EUBJ2wI/pLwAZtgEQJDU3VT1b5t2j9JMA2Eg2bDN4P0RH9ZgAQbEc/culgh7Sj/0I6or/5ln0VWICoARZONvd3/RYd25RYsMlg65Nj3pz3tTzomEMwHGdf/37Hfu6qYWhYIXg/Y4O/of79wb+VYhoztrEo2KSFqTHt6hdVjQY0fdwx4bfq7/GGRIFow5KsNjZ6kFOmf0IF2wwBW1QzVbeR+tiqaAOqoan8UiHWCsQ/GdxddvRXs/3/mqmgi93wL3lsrIJTpt9EcouasUkYtptmfu6Ul/at9qjt6c/xQsUSz5B2YU//of9aU7kWJKKGNXJGp55z7ofH4WuVXzo4wmoDLKrqbpEggFzY/YE7+pH6tj/4S5St8KMIOudMPzCZ+VrllwQjzR/LKDqZrUX2rIUgv7/06169YUl/+tzpF43Dw8pvr/K3Hf09Nma7MkS8/G79C/OLthOnP3yicbP0zzKoCDYp2kpuWh8e4yRagKayKyM1B0TytGEFE6Ba59hlHz+gPxRs4i4iedyzhZNd9bM4oqFsRn8iAw8v+pa9iFkMqrKfxRyD5XihbF30DcO054eziKBMyAH9gm2Kxhk72VW/hMckpJ1O6dgIK7XIoY4p4bqtkj4Bxj0duoCQEn79mn/fuOJ3mIwFiQu+XO+nft7pS3inL/mu0o/6qZ+DPwd/6qd+Dv7Uz/Qz/dTP9BOmnzD9hOknvOpHeNWPcO4nnPsJ00+YfsLKn7DyJ5z7Ced+wvSTd09/NUwksYKvYIJsm6/FGcn15hmqean0m9ebf3wBB2rFPTLuE9KBQG6a+BcFXiX95i6TFv+afmmAFQBZsuQrZdwnyrTdMj/mSfAC6bdm09fWKoBilgFkswJItSr9S58nbHTW+3SKD9PmMukXuZHcKSM/2y9X/QmcDf5s+k1cgOwy6rdWihdkL8UEzXKtyF5LM4yPiptc+1yoFeKY0i9ZbhTf6l74l3Fj+v/8VT8zWAOaoVXkfibArM8Hs6Ds/dFNnwstIzcAPf15pjv+Kf1yn36G/49f9TODeB6dWkV1+8SR3TKA5lUwIsUFnoFWrn0Wld809Msi/aWMe39809jTT/t/fu4Hamt1SnZtMgLkXhGW5hUwN3NB9uaGuc+I+NC3DMjE/VAveRXpm1D+C8z9ANwBWEVxdBLQaq/tYNbngVbk8swNxYAmAGb9Pdvd8IK5SbJw7n+R9EME3bcbkCuyQLygALVNz9uo382qzH06tQIOzPov4b/Kl1n6z9Fn6fcq6e+kUbM095ZRvHmFmHubBv3Wa4NazeXap2MCMQA/Df5y2W+Q6xem/5XX+6V/XjxVGgB4XrQCy/QvR36m/73X+7PXnK1hgyQTF8X7MPxvtuInZlawSVplox8Xf0633p+QgNnttH2yaOvwis8Z1/vTAvQN6bpdAON/pvTPdu+Z3d/7R2L6T5j+hf6L+2kfAcPPe/0I7/UjTD/hq3wIX+VDOPcTzv2E6SdMP2HlT1j5E879hHM/YfrJ89Iv4K13f2/6J/UCngB/Y/q7/Cv4nWQcgH+Xcx15SvpF5vTjy/5F9tqp/9nIM9Ivcpd+fMF/GTRGHcpmu4/tXo8be8q5JP7J4B3B6ZAnXPWTOfaQjQqwCB4hIQarxUJ02WvfNVauPPx5Mci+flENM6qC9yXn5+hfDv5ypdaN+OcYC7YR1UtzVZXH7UEeG8sarzRsMWgpGvb1u/abijGhjvelWM3Pn/vn8I/74DIPA7jFgzq2cRXMpsN/bM9bfT1CmmEFiQWoUfb1B3RyAxAc70s1s5q/Ye6/Bn7SPz/ALWpNMZLW6sISKyAAIOODstqeQ4yhtN5+xBhKNI1RtvSX/6Jf/d31F+snwLPT3/N+k/4L6dZf7tYkxBh98VMCYFEBjQaEAVhKEA022uwSDuqPvhZxmfWLHNTfopwh/dNHfmr605z3Of3oG2ZcL/nRUHOLDXekaECLUUSjAxax0h4UAEJEanpMf5KhrEY82FV/02P6JTa8u34xq/MJsNQ/PZnLf6/8u/o6fKI6fq6L9KsDTYGqAsB1ZSiWVoBq0ufkZXs0ALC4bM/LoaSTgHX9Dm1Ai93ugW/mirfXj0lTLnZ3Aghu/Of63yv/1PNevblr8E+q4L72yz1sBWlskZ/inSRWzHRji/ZZf2/fC2xnQ39TsTgMsUH9UPot1vfX3+1LtrquX2qt8qW5/670g4xgTv8cHnVAQhzBPWpAGaaeBphipT1MIjQWuOJY+sVlVb+ow8JgaCr7+nv/99cvk3dbDv4z9cuVP7rywVcXfkKcUEBDLaVFLI2VOI3Heav0y6pmIapqNBwv/WxFPyy6AOLRcER/UzmB/tLl75R+T0s/ZnJs9kmLpcSMldquxDKqjxobUGPBSnv/xS+LecEv/+JXNapGrTiiX6LhBPq7+5qf/3s/unNs/N5vCqQEqNdRf9GInwgqgJjleaRdtifM7Az+MoccYoZVSggFOKbfgVOkv1rN33HVb05/rbKy5hd8LrlUW4hL/aLXJJati752abc9Y0XjFQdSwhoiwyBH9Ctu0PdOf83fteIHuXiHLMZ+XHOWg0jQUIriZ8RjsFItxJ7d1fY6tg84vOSTsUXy+Inv689nW/J5ZvoXxd8v3PFThvjJUJAeLviWJy34lk/k4ILvcPlwwQl4fvrT/c0ed/ZTbivkL93u8eQ7NBJv93jKej8S7tOPGyyuYKs6Em/2ehf9P4ZFdOeybydkfP/9t+D43N+NygjlnoTjcz//vt6Lwlf5kO9Jv/PlHGdHfLvyt8rjc3KqbesX5/E5OS5L/TNmPECnxuzjgf7U6P+8JLGWNvR3knll/XdOpLqlj039HTEfyBlxk8nwrJ/8ZVA/oX5C/dRPqJ9QP6F+Qv2E+gn1E+onJ+X/83yopbjb0NUAAAAASUVORK5CYII=) You should see a new direct message from your app: ![A new direct message from your app](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAj4AAAC8CAMAAABPC1RkAAAAAXNSR0IArs4c6QAAADZQTFRFHw8jIhEmNyI6Tj9SaFpsmYievajCq5ywz6Heg3WG5uXm+fn5zs3QM7KBO8Tv7bM04CJcHRwd60ITegAAHMZJREFUeNrsnOtypCAQRumDtuX7v/DWLBXnQqDBHV0s+4s6BqXyY071jSZhYEn6cY2rwSDJYUmDTpHj8w9oBXFbtFukS/kUdp4yFCavuGxy01MX9RM5TFzI+ohbn99waRPND2k/HxrP8iS59fkUBgcmNBSOt2dXjH1svO6di7HPfW1cpJtOYXwKw+DzxMOD5HdhPePLf4R35Mg+t5uLZF4O1afofA3bTLHPQA2Cidue7GunPk6FCwo+xxYGfOmD8WIfuWcdmuxyuDLu8kfUCXpCNJqzCm6Bvh4evUUulGHAAi2XZ10DuCdTNJsRnr8V5prDhotMx3j4uOX5VZSTdGsCFi0Fo4M1fZs5ks/qQcwLivsjHXLO0kkOjwGgL1YMLBodWM1eUCzuWMpf5fUo4OMdQP9f7J9DhkxFFBjNaOQSS6biDqoXGWwGMd/H9F2FieOR09sDdNdFUfa7wReQaOHzas5LsgULd1YZY9lgdtOafRWcF4bR4rKxT/AeVltY5T8KpeZ2DY9JcEzqZUZ2NF0INi1kdiw7Bot9pAuu7c7bw7KvvIoXpd8p+CrGXHG3efItOzXlOZUNCXbY02HkwtXkHUD9fGFVfDCS+VLiJYMjst3fKumikwlqEY9tgQzvJwxfNtxfJHL7Y5UK0zXHzs7NbWfoixWXaoo/pmCNFYYXOjZyfHznxZhEYRJFUzRMU5s8rdReqVTohWdjEsUqoJ3NCw10cmq/D9MUz3ds4a5E2UIKW8BoXoxFTmuVn1VVvrwGlpTu77shlaNIo1BGLMTc4UChqjqFY+WLGkWQ+DjrK1y0uDTOS9xlUl10dgd1ukujVk+i4WXamhuPxWfReVKN/j+AusUBs2itWJLjRk4UnfjE6VU2FBJVJ1TXczYsB0/eW2o31BJ4uhENrULfRUvgLOmSBI87YkR+BkIa4IW5aXscfOeFCN9cki/vsCjCRHk6h+KD6ppMUEhadQ1xVlWd0+RFY5iWx8Aqf0GQSR9aYoZIU5NquHWyzmeWRIU1u2XxKSpWLDRr0ldNLe/HFABt+MxRdZkfgJDwWVfV+TEwS3pD53VdVKMvsR6FI42U0rQVtSeR6qJHFl0kIRF/8FFdCeEPe2ew6zoIA1HPAGP5/3/4LSh9QSglpM1dMeqiiRCrI3scHAchRcVHyjBjqRumemkpY/cAfRR/ZZ241Dg/bLOCD0JNYbCJWtaiFG98As0VseKTDDCD5DDLzSdhsjt2B9BNzDgNQGy/sfD6bsIGUFRVYDM1RIDSoIjXHzRD1PIaLCQaspR2gnqLNzqbzw+9fhbS7h9aoFymh1J5e6Dc4WNgj0/zSUnyzDujgDBgto9UZ3iNZnlcwWnhviLQq++d6RBJAKl0+ACQouLTZ7qQpJKxe4DWwVhqypiIZ4XXwJytiX6JHoPrqNTw6Yga8HnV9Z5Www+64n4P6l20QWfxZy5bFPMlepI6RYePsY8+QJJS27/oxAINbWK7zLrif7g6E4oznPh4u1iREptcQodPb51rquMx78XHnLVnAH0nTkAZHNK4ho+0i43GuZnnDp+QUpe8XMKRvXLzpeWmXZMRnXi9BJtYZj7crNqAGWAKKXVwuZz/oxEO+IRNhc9I7Ty2OAv8LNDw75MXXMLwDCgkZRqSvzhyyROMWXV59oxX0bZ7gL4Qr70Rdlq18xqufAqfJIWhv64nWpJLDQ+Xe73hNFS+SnEp5n2r19Daw11WZ0p9DEkcgLQnVLrqvj15DgnZJQ8aKj4FcbiB13l82h8t+F1C4gk0nCyfuJ9nx0Nh3BnVOv9j72yXnAVhKJxzhCrgH+7/Zt8d29ktthjhrfSLZ7pWi+P+8EwSDCbgMniRj0BIuYKEAA9YWLbQ46AzhB5TK+ufn/6S8iIfQCBX8lkDoFdfvQdbX51Qa2a2l8+FRD6H1IDu6dVfsqmuvECoSofvIx9dTx85ZWfVGcxogZkYWvVsWZ4vn17g5Sl+jIWDzH1JO0hi/UOXyWEwu0ojQZ+XceNLvoEurK0wiGqR+fpugr1pwRv5MhbYIr2M4dEzr15W8z1hwS9UZl4twfKpZ8ci1W5pcjZDvxDXR0TrnFdv8PR0i8JSq6N7NebU+I62p6dL6+Fmhj096jOvD199yMpTudM4qdH4E3q1N5FJtz4Fq3ZuLRILcl5fQV/YsSWxusiHaH4HRdCbFhymAep583zsQ6RQD8TlNegVyJpCKBZGGyXONL2BUQTnT5dJu67LWtf3PHrHHflM+muGpQ3es51w+ULOC5ftwaCXS8jCnT2Y9cQFF/l8joJ0x/a713NblVP89JyGosGyictRb5ny3/AoR1bSXFBaEdsbm+97xZCFMuPmOHM7bG99VjcWPWHRoLyP1jQwH+3kYyWWvqgDxLsQFSJqCT5/cQdbd5xLzyvRTr2AsH7HBn314bE6Ypk2dM9WmfNC3KYm/olNFhui58AANQCmseN0w2gN92hOFBgVIDvBOvXVW3cfDrUxY6cs1mS6cnN37BN1KDqxZ+BfUVbDOG0yDtr0vsBz1dof/G2SZ3eCljJBT5auLZKbVJxiw8qMzzAYMwxxxcs+/4Ggvwefg3bawcjqjjoRMcHY2YfgZ2sqwp/kLHRHdXCxXn05/DjtYmQa/dxxXnFP3Ew3hwuzTUeknNhjn+cyTjuxW51301ua/km8ZvThD5sMFT73wXmf7odBNgFxYBfCFOH3LEKEm3bjajvqJJ7Lhyu82/Ze8zz75XMaswoZwg9OstCdfAjBjwbVytkNz455NN9RZtNMBQy4B0vkM8wh4cRN65NobR40+YCEpMD45AIqAFH9JNr5cMEbLJD4TJh3XSfnTtMt41WJ+sxzn6jIx4QUbxT5JAzb8uF8cwrHcI0TFRfCqdY8ufCHB4DBh4AHIzgMPsT4WIjY6Q4m+w9T6eTlY8MKq8vHe/97Sy6RT1y2a/m4tcKAOSQY0YDfkA82J+kyhAW/bC0AOYUQ8HD+UXduO6rDMBT1JY5J6hf+/2eP5KROC41K0WEEWzMipKldqUsbx6jia6qhQ0gKwAQfwZ2u1z6UlwdlOsPHiSCZ2s/AR/crwg6sMHGpyyJwJo+W4T250ykjJlsqATqLC+An/Af+khOaHD+0GJjic5v+COFkS/2083rCx17DB8CCkhk+vpqfDi1KgAiARRBOVQKfq53oZnW1dcGYwo7wF0T/q3BmAKQZPmn2ZOqenCvuc38RHxn4IBc1LbzDh5KvUJFBiWez/hb9NUlfQCIiCdqcWhYGQE6OgB/ZJ+pnEJC03yrE5Cc9Xm3FgIqS25GItD0rF81a2MceC5ElW+HrlQ98BUpyVB4DKc/wkVkLElyfqX3YY+R1iGQrerTBR5cQRR0z6p2VIVkXkGcHQMoRjpaQAACviSwS1VR9Ii5CNrfT2kTXpu43RIg0svpSJY1N2o+ITluGwvk2xUdncIbvfGDn5fj4iQboo1Ca47MSUmGnZ3xsILPFZ5eo9kQhtYNqvNMg3PzHtvjwgCkjoseKqYv+A4jj/+8LoHhzm2iKz23WjZwaz0nfx/gcn6yaLUyAqg/bTKXAh9X6YiVwYUNu94zGfcUHV3yKnyVal4og6lFVNR0lclldmqzRMr6D56CtECIm9ZWqKt2ZrKgTNzZp1pa8yM036X189g9+Xes6l1nXeY7PvmmjfYh+3yXw6SBwWGCfUEenC5/dRztimAiwZcx+o3oixDUMd4Qx+6UzkPVgXSgbu0LoITxYadggpkYc90UdUrpgPP4X3vNj+Ex0tOO6x9RWUjf0CF3AZ1ECwHCUttE5wCckftLGfI7cJzsKfcmKDwBGInxIhM3VNDKOYhnKuOC0EhhlkUVFRNhiDbB+cS8G7+MT8a+0DV1U6sbj7y/gYzln81GlgGWUMXN8dh9ek9LZo/yj7ux2IwdhKIx/iAXhpu//sqsacKAMS8hKO1OrUpNpNL7wpxP7jDM9zxTwynh0fgAMieyABysSwtFYnEV9KovyHVFpMXzs6F6Y/Fza497GlezhY63z+Ez8zrYYSywtZj5ft85f9dMHaSsW8uEcHyplVOWZqQ9G07ZOfYZEC3xsBbJupNClPl3X/Qof+ZWukN/Fx8MkVq1zH8gcCO10hU87RoUNfOBsyzsb3LnyE7HB50oEa3wsCqdHucB6Hzy7oOf4DN/u+saumnfx4ZFJHPd9dhfl1/gYC2ClK70zzfGp4w5Udl6qj8mFnhk+Y6IpPuMOEOsFbe+TfQGE/ANuuHn9xsBdfHDy4NfGzQuJiDWICO/jQ4pCFg25PGWY41OdG4Fy5nH0fTSA9OXU4nMlAku0UB/wDNd0SOW3SlLKb2+IPW6dq/C4Tnn0lf/yPc9r31A8ORe8HOt7l+E0Ex59qWUnSKrGSYxJAuENfMwBlvxSNPlI3eRlxeyNvMTogESn7tCV3rdXKjax1HlMtMJHxSYxuGIRdpOXvyBhBnBkBvXO4D6ahe7TtsXE1Qgv71246n3mgzsG9eC6SEfA1eSVKnGRSslioFDR6ouZgqCzQBvyquFLeuZZTmudY8Bs4Xj7ZNbzmGipPtzZit6cg+CrvSPMIarqUL6GOBlH2wA5cI0L/ZZ/zyQ7+Mi/LavSEc8XERPdXRfjohMW3orZsCJNH0vx5xt0APvqAVY4zfnDIdESH382kcABcE2rhyXM97GIuL+u8RlBD5dVcXdZ1fwei9F7hsWyKv/0dj1YMWv9hulLmjzcAJWy3mDq4cSqU0Oide/DsVljUp8gGR+u3WPtJ/lIj/Z9rAF6jwv9fFUem4PRNvwabmL4TY+cfwnRS9wcn5iEoQxPLstYPDIm7YgUlTLonspgibmiAfIqc8p+N6UyuCdNICU9JdsRahKt8dHShaxliUuBUZRU5QMl6olH630kJ37ygemnhL9Lj8wfKFuqj9Kz4gc2HnhAwvFvAIAE7g91V6DbOAxCwVl32lU76f3/z97JyuEyhjCkXlJSuY5D3Eo8PYMTY/NvuG1bY9Ei3jYeAOvnrRtlV9ga9+Zvf0h6Ny28/1aTvkdnvbf+Qzry2lrpZUMhHhpcdIKklgk6gVzsOlPD7R7IDaDDInzu5CKLQMnSh5u7jL2WHX06rKavRqf9a4cPURo5dApY2sFFyjfpIP++D6EPEJ6IQxAITCW+I2N/KYwqJ9IBGW22EJZpw2zsLdokh5r/oTOg9FFdIhi7zhLThvJWw0N8HdZ+KjeHn8dZMJVeharbrAh8qOY0k4HTOU/CaglabBfaYHYMiwevJy9vZ/+yQzAsBjd8UqUfH5rb73/ywWkx20QZ9jlhBIvSQ8WdBfaEWkEnEy2J9XuImkGWb/ihRehkB4STLMhSSS+S7KPPrVDRzsO5W0w+TX2FEien6zpuosM48eD2fnflfZtGDqgqkMKjDrZDWoJ+5G7Oppqqsw89AkgisssIzdJZbHjQ1tMH2Enn25b1ZpzTMLbS3APaS9EBO0ySYR9DYm4+aDoUs2tKermteloUedUFUqZZiOe618TCGOzTyxL9qHR5i9iHTOilZqF/HAvtOT2RL0hG2jiSBgoaQ4yHMUUhhF1YgInzzo/SX7VpATFbzNH116O2qAMzLwOpLhSomjnlXAJnZXlYlbgTjZ8F+RCHLlhz2NfKtaWypwUyVxHUy8JRrC0jmhwZ9unVNQns5E7DaCcTTqukl29O5JWPmDA3lsFVr04mWhM79p/2fdayz1AlOXT9hHd/niEJS/aP1DIIqAvvbhBDz/0whr2hUGCOQFxPie1RFDIYkuKiaGnOfYaEPCNjkf8Dt39GmjgwQOMLJ7iHF232NHCjvB71JSqvtOynThcWajCoeK4XxHBnDEnHZwymSUAMLV61M6F5N1Vq14BLczknJKGZiT4cjK2ynje4FxlPGmzZLME+LEd+R+Z0do1ehzQyXT/yal4juU4uwncukInKMJdxnt3nCazPdYMDhBlACE+t2bTAwETk3MGqHYCNHbxQ8FvWC4K2gCMS7CM3rMpOT/ItmlLK56cx0xZEXjjuDEXtKPYncIBmJ/koLRDl2GfdI1NihRrlEr2KNClGpR50z0IE89SGSCFuZTyQicArgZ/le2MI+lSdeDRccC/LdIoEpwVB7BWbHDNwgWYbxS46urLkY99OnEFPMkjPGVkD7Hyo1O+wi5QR2N+0ohSWIaVW74SdkLxMPzG2EsG9AQ+pj3GoLw6uRZsloahq4zHM2I8hhbhEGdtz4r3oejQm5KOQczoomqfUYnd7xq7t7fPPf7nfP0W2NgsPlIHJ4UW22hBYqEr03H7hRrt2ApGkrsa1F5t3plBax86Az6/PB2lkBfSXuqvRdRaEobT8GO+GCe//st9kWWepWIbGT092F6E1uYknp6WFqWU3qUGBoDqRgMUl9pH9B508dMsRabJeOrxOyRn35z8yxymu7LTE8/F4xiXszjaZuJ2+hO3npj8s6pGwW+nIvHtxv7SSO//cBVp/yk2SPgy2OSil7tpj7b+UpgS1epD+vPsDV7uUkL8pi4m3AjYe1LETx5+gT7R1qhxfpYaWSdBv7fI4Rn0o90nktZik0T1WX5vd0YRTgccLsQAq1NkVziApuywMZMfyfDvZyKDnMxr298FE4fk6h3Sw1YTtZcO4Rp8hFthZpu72pSFwE1HoaPWBgyrRpnbeSyTXZzMH5Xx/3QcnGbtk9IqpWqeWqPgmJdRxWixrQSCdkrIoV9SnL1DpasQFRyjSmVTBoxobmwrgpPisRS/bpy/t2z8SVEx6SSg17/uB094Qnye5vbzBwD22PW8+5ykjuvgVn6X8IKK184Xe/AQENSsCxGKodhF46YefLk3APPWnvV99mmDEdwIDNwNWglcq6ONwhiXxySD2ZFuMq7ejdeNo0WT4MBgNYwjAztYHu3crELDl/DnyI+lVl5+i+PydIYdL0UViMyudosU3iD0L/uAH0azAhTc8FvRJjfRxjD4iscmuiHlG6ULQWkwJNZ34dT80xSv6YxOXADaGt80z6RN+wNmT+UPUQrfOhTB4H17AbfVJivqQg4QNvvXRnig/OpXKQ6bKoffLpNXSarYQMcPamBOfBf7iC85apj6JPfssO+DCAII+XeojAZk+7A3x7HS82AAE9Z7XedvFWHZsmPhwrzugljqQ+thIeU/Bnww3exgBH+jNkrg396lKELjg9xeTGMmOhb72gkoC9B3+R6Bu3TxXM03OTZI9Yvkua8VD8CWdlFZGj/qMwSu738XPk4Hy61B9AEGZdvWRhefzoxf2+pst5Cbp34zHCrIhL+LldvYg6UMRx1lkEchaKOiDiB/1Acu9cTn2wffkMqBsHTq2+lOnkBQf8jeXpIzEpvjPGc9zdD48VjG4MTyfq12LIQRco4/1YcYIHzYMtDwj+swX9q0+mN2HjwrhGGgM6If5+gWkajS1uMqKT6rqDzsV1omfV14G/lV3tluOgjAYJgFx9XRPm/u/2S3YvluIiFWXZXKqFbD9MX0m5AMDxUNU7v0EOu1Bq1TYeNIzO7b+tsqPZx7mcb6Nv1ftmNFSjg9NAZVhhG5yAYBpeJ54wQf0RO3jRj9GSOzC3vj25iYK42/h8/YPJrhGsR9d3Sc2UoMab/3K1sOgtDASfsc1fBzzNLtAllFCUTM4zvAZvaNI0QKEjdyEjgnah5z37o3WwPFi8d54jHqHaFruoDBF6si5qnuAlloUn71TC6cdtIATXGG8Z4XD+x33iE8QFzfc8tA70y3iEwT4pD8fDz7I4JLJi9nQgtH06qXcdA70wPcPo+h6Q2doeGkc7ydDdDBdT2ld6EaRZxCkzeYOVjzzlTkveoy/ZsvMcV+UidmNt/l2G4PaCaZPGLHzfL+vOFJEdliPOqMZp6fM84qo5J4XB0xgjuOD8O/SLDyhkZZJzF5K+5yKPB/xvJTdXJEeedpcSfN4PGbvrB2W/ZIiLhEnt+zdZ63zT3rupcK6LhCEsGFKCgD57GQXSFFxn4gJf8D2Igk3HxSAU1kUfTFS0Dp4F/3QRddPX/Cutc6PpwR9szjus59sJGjySzuO3J+y+gWwdl2KD1sXsHrxkuLjoWESuMLtFHECTyMo2l6wQUmXjvSg9eprkLQAHO93dHY+X3HFdIYAH8i7FLjHhrj3v2LKwvEnBj7sRr+IihOG5jB6MGITfIATOiJF03fVHUhKRjLhTA2sH7OWMtXpDxyNgdqPWEFxAB9IHjm8f8jWQp/Rj8AnOlGTs7akfYgG9Llk8gJO6ID2Uct/cOCNhFRqgpKXct0vBmYDHurCP+dDZVnX2AEHj5yfeX6+4sVd4VOQEdoHLlTZ9qHonVOmfcj7CbYPOj5sH1jKaFX2eSmk+q4AiHYjlSmfBfJsauu9jovZkofm5ynQPRD9awELhu0TzwNIgUeV4gN/HXBFq8dFZkaDDnheEKnX6lBeV+6TQRrooKLpA176xIa3PS/J8YGomauofZwfGCEaBj7eT+jFYBBi4AMVBbeeQngIPKEDmq0gKpIjVSUBhppl3oGOrh+FBpRUp9pHKtoHAnoyUVn0mFiwbN3L8YL2GXlxxxZ82HtvydCzw6qkhQvfwYZ4eH0HhS9jQxYdQ/i4dUbJalxHMUJCaJI0s3301IWjw6JjfCxsCHy0+TwrfEpLVZEdRZzQT9PoBxfxWTp8gGmgBR9orDjm4iAyrDy87kZkUeW8aLdlIsVhaq19cGR7XeDUAU18oLqYzflZVz7OaOEp/taDTXJedkmFEY8RHyKeIjyOPv34xXy2flgi14PN4pATo718na4UrlnRjhklyKDZUvsAlz4rg9cZ2tyFiXN8bqvKhwv/zsz6z8rMqoe2FAUTflG0CZMSMX9VjFq21A2h3WbdD5SOavVe8Yd12FB2GD8eykfNXa1K9hKVGkI5GRKPnBShVBNJahtRY9sHtCTp9x9YnE4LaetH40Pmp4su73K9HKg9hpeysLvBqLK4wSp81Nxl16o59SWU2D+rNaDRRY2R0YXCf5JsPl6s+fn1zLzndnMZFvnnE5qcAlYwYeHqv2kfKBoh8NRFzWfealaD/DY3fpTu2VnZR3CovsJxQEiq++0S7lNbERCaDXCBqAzpD5JKFTnlfmX4sBH1qapcrI6knrfoRXbUO1zTOj16YAx8asJ2HR/HZ/CQ6znTH6XskLWVzXrbHrq8VMtuJ0z19gMLk5JMc0jdbSZCS2qzluC4SmTnXAlcVJ/6XDOhivYp7nAK/YOBHiev9oXlNWS642sCZaNb78GEk+BR+fBqaf+AkHiZdPculzgxEKkwcjml2uJqZIFdP6Gt2z4qAtRX3dWTFqdscycX7Hoip/Zi2b89JeFo67Tr5fOFsR6L1h3ym7SIJkoP6WYbS0RIIYROXGuUGntfaf40Df9gEDd1QlF1mimPyyEjdzdYu/dklvKowkb5XqSftUC7sf2DsykOdlcocz8C5+2iQyrptMVMGD0YK2ivfQoxZ8VYV8+4i5qEqlL/kBzGUZKTfPPBY4ItVC5PnO7MfOV7NWGkozXzXA8bSmWu2m1Dy7//Z5eq0V7WSBXym5vQBm/QQWkDrW7k9E9XH5WvvLj4Ko+fBS/TLlTeK76l3ZMDY9Qqsk4XPe9LHF0YR5batHYl0HI0Wy+NY8/gpFRwFVSlQJn+bJ86JXWdILvJqVs+10Qg63dol50aJCtUyFBUBQUc/Yk5K+cCR3KFPyf7vTjpPeuemzpCJtU+feG0G4xv/R9zYPmOfDkgRzQN6TE88UWvcVy3zXvhrPAwHdc2vG470v03yIFbzojsquUsrTWQ0QTp8A+u8eop56X9HTSlsgajpJW+BURUtxRuli27Sr61m+njJiqcW637McACBPWfe/8DkvxY6I0nTAAAAAAASUVORK5CYII=) The message from your app asks you to click the **Complete step** button: ![A new direct message from your app](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgIAAABSBAMAAADUeqEhAAAAAXNSR0IArs4c6QAAACpQTFRFWlxe+Pj40NHStre4np+hiImL3Tjnb3FzgyuMQ0VKMDI2HB0hGhwhERMYMfOhqAAAEWVJREFUeNrklt9rG1cWxxWCCdsi0B3JiOZpx7FZrLxUacziQiBxXIJVFpI6Iay9fi3LPllpQ2kavy7L9iXpD9rINlipjNFYgtSNCdE5AtEftJnzHTCJTbA1+l96752xk5q2YIeqAX258vCZc+GY75xz70nASkQAgRD/8Sz0m2Lhbv8/rQQLBMxMwgKw/PH8Ow7s398FvpcQETAsQQTUFSYiISYN5ie7vH9/N7iVgADMYNIEBrrM/Cfnx70ES2SGQFhA3GusHYAGwAYARo+xrQGGdUUAEkGPsXYAENIQRQDuNW4lQIgPJGGQoNdYOwCIMJOJCMC9xq2E7JnCWuBeY1sDgEAEYBFGr7GeCYl270Zoca9xKxGPSDCLBBwx9jEfhmUf42VkMxVbIqFfyu4VIIoHEjHHDIqZnzF+yeI90+r+/S8NmxrQ0rzfAEbkC0IT5+q8ZbQpiPbDsghkl0MKgOe4UfVKe4v3739puJXQIIAI7RMzg8cGTkyFEATFHIhB7XcGXme7H/GFAkLEzaM6JLzH3PCqnlme+Utg5uf3vyxsZ0IWElCkNu0J4MBVyqkxJLiUe5oKme8qpZIsEEC2h0KGkWXe0KH6WG23dJh0DdypeolS9VbJ85gJaALMYUhBIOBOxzAFcX4TZ7O6zboLomanWE2KxWIcyC2ruSDs6Bp4oh1APpM4X4NWgHCrPwzCQNBBGIbAd86dt9pune0r88IveZOvrJwev3V6/JUqA4J7NYF/7FUszbJ+ztYMLxEEVgKGgKTLbByAgOPv3wyp2TZPjSLUdM9sq7kfT50NisPnnFHZUDMdeTrSPp+snJh6x3l9+/xI/f7ZN/4yNtHGWrrT+VSdrr03uFiZGCsIdBeMTy2NfnZx9POCxwLxJxfQXJ69unO176b4F/qmvvoX5DJH+QUiBAiky9xKGAbF2pkcGEwKWQFoun896tTPKTVTHHbVcLilFglP0m132h1I5dXfbiuVKys1oFsFm2po8YgaqCh3WL9KMzeqK2cnVgrLF0dXCiUWxvr1BeCL4PNHN7en4b8dXr0/Ia1xtvlNHNHA1mXWDjDvdUHzmlIqXScrEMw5kNpQH106XszdzhI2nRrwvXbgQ/Xq/FqG8m9+0r+W+Up9oZJ45Kr+DTV/JPVD5uvMilpEw6vcuLAyMpkYmbzhMQBvfeHRNOGYcOsmGoXOB/f7eL2PmRGJAGbgwPzl/w6p/4PtOSAsiE+ALWWUa+/WALtDk6rPad5OFU+WUyF0DTA2tQM31ODit/07am4zfTe75fgqyc0HebWk6pecU+lyVke44R29Mr4yMrs0MuuVmFmgHfgIywX4l2vsTyxN3F9O9lXY5o98IGg4KP/07qH1MYTtbciEqASKyijNlsASuGc67ojTLKeKOe2APFUz6JgumLmmUo+fOdBQc0BnQyXU4qUTb/zdOoCGdznx3rGCtzTqlWwN8PoCQjSus6xMs//WlcXKw4mpCtv8cU0KQAfl/x7egf+IvQ2JJe6CHVdZzZERoB04+VCdVYtHXtM1kA3Fd7Pe+xXHd2fwSeZxpu3+Yy2zZh1I4ofCj+qW3nq8Uy9nN1SSG9UL3vLFCeOAZ88BaAfgBRWvtlOAX2BU6u/PVdjmh4mLFuOg/O7h9W8xNRANumT0VEU6KYYEYNdx1bw76J4p5u46BTSPKFd9oE6pG4PXsk+cN4uZ/GtrcQ2UlZt+pIa+cc73l52xNEujWvKqd8xAaGtARNYX/IU+7mtNt6bRKCCo1JbrFbb5TTw6meSg/CIO2JOQIRQPxZsq0vE2NYkhQF45o/qOy9SLuS01DGzn1RDl0/kPXTW94Wa3XCdpu8BN4oGrCjjneHk1WnbUaBu+/vQlLx6LSYDW5auttx9c+adcv7IgfgGo1AIsx/kFEBgdmF/EAdguEABsHXisIqXahhggz/MCQbUObzWo1kCBf6cOf77BD0shV2tBdTVsrPqeeCxBowT25wO/FJSzVRb4+tPviQSsLZFVs7WxKiKrjB2CNDjKL2CARMMB+QW7wM4DTPJrDtgcccfZFTIEYSgAKAj1DwiETFxAYFiZqD4yoJmek2ZBCCNiFjZPWEX5bZwNHZhfrAak9XM7V9/SRtbFW21ZbBU2tkX2gQc0tUhSFhpjCBoLtk0orRtwfUF8me+wNRVZ3XyHrbYssQbcWCkmE1iqUpp7B+TRpZh7BsLThJDMne+y597ZxOlQ3XXtH822xxjmd8+5Z8gv50zu5JzccyDDnQh557LkGwOBTDcUqUd5H7PjMbDdBxIz4UU+CHXaf0QsXsk/mS/mIRafhrKY6LgSGoSivbD8Uw+nwDqnFiYM/wS7TvuPiWsMsKMghL+D5TxZL6AiqYiQXJdLiiIRMGkt9eL4E8W1GCCnEgpMzgPALJAi3iyifS8JaKcExVlv/2Sx9UpyidNJRjJg6x8g5Ojj0GNoAjjq7Z8slvlMEhlyGskl5Dxb/4B1Y8BEELQnrTtlZ739E8VWNL9RgQhhlNTkREwPVTHP1j/A5Ismlbuu65cNaeOst3+yWMZAGmQ3BhVPTEQIJeQkDGxVzLP3DxApmpHWOLEEmFRRZ/0dTlefZ+w09uxEPXxovnwvn3/InpyE02KerX+AiNTXTEMs30wZBBbBOjEhLY4oywmM/3m1Vn/PZ47q8XBMfZ7ZMfoQFjm1rpe4rkffJ9b7SQbs9tK6xsBp+we2xDxb/wCTAXCxt8vVfv1+VlIgeNGf+odopNAPwPSYCjiWowfRWv29NGzV43GQZY7q8whQb2HYGbbV67WYeP2wo9T1TOC6nh7MnljvrwwIzFSw8MGs0EsG1k7bPwCSAVv/gCCgetdlyZUlA7GwfHt9YTQeLvRzAA0ZANAvQDHKLFJpKQSSUrqhVoYlsxbOP6ytpgEZALDsLQaoPg/bylF0IDYI1PCO0DD24Xo/sIIHMauELCysEVsx4LSXmNuwU/9GzLP1DxBCqndcNbnSxgwCjOrLM2YlG+aM6DrGgM4wEsyDKOeg6xygNMg55ahb0QqDeGQyNAMClZAAYj6gFR5yHGfAuR6joI+b24rJ0dSgQqdPmGDqoEt8oKBHRGjLgMlJJtGFIOC84EFTVrjNhV7nRUVwZcUAoL2WV7m9PyB/Sej1OqbM1j8gY8DWP0CItuw6kmtAKEHNPUp1CFen4VkkGcvutsKeH7/fDLfSzXArYaUH40v5adjcujsxfmO6Mj4Em5sRFeDpjekqAoC98OLbnw7HJxd/eXafHo4/QAboa//k9oj0kASWHx9CTC9E1N2XiGEHNQxRZXwKiPYsspYXTp8NvY2svf453IoMXIxkL4hzTfG9cGy2HgNrBAiUfVd+Alt/QKGDo36d1vDh2lH/AJUM2PoHGCm47DLMCJqxgEEZhCueSvdFJabeU6lgoHu+sxr+6pZBS93zPRWPuZy4O/k4sLQyNNq2HIiFBAOLG/dH41Rr/mGqGN0YGZ2Zuz+nbIzcy1oM7AQee6p9X/UD3Rmaf+1feBu8MJwKPPYCPeie9xSCsdmNkUlgeNJZ4ScVjIVjof3AfGfBU0bTCzeWVkbGkk0jowoFyiwGKDDt++5RL5jyLtYUUYoM6FyfozgG4ul/Cuic4RgiiwFb/wDTzrvsctUQvOR7OIBgoBg1tdjmAJqNm8VBHiiHzBhlpQFz7I0HlpNNWnmQh81SKBUveABQO6aVvaCN6fwg2qztt81lS6Gc+apNZEHY3I4aPaVBcxwgFed6xNyIG7deKUaQw853RsDIFm4uJzmmedTU0c9AKl68WfEcfMfDBe82mpZv47mKs33mu1lGa1nAGFTas1W1ei57uNpS+RESv10udJjrl/O9LfALJkn+nPrkQXb9El9dbxUpIa8D9v6BfJfrPYmLsMkHOGUUGdiIU33OnwXQx+AgCuFiYOKeSkqDetOWYGBOLYVY0Kh4U8lDDyPlAQwePNCXg+pBdGVtJRlTywP6VlMcYwDCfFthAfTQR+H37ssswpcjE8FXcQgDHCgQgfxF705gjSE9wAL80JNqKw3mO98p2tieV5iWQ3n/xNh0p4HWQFn907CEQa83uXpK7e2jrniv70qho9x+Zcd1tdh+Fehye3+Xq6XLnXR3XTOA2bNALrFY0fW+eGXG+IHKGEi1gT43FicMGShGabj4ILGKMRASDJgWA/kgF3aHHg6CAZ7v5CQ31l+MlvqCumBgt2/UxsA79ACEXeimEXO5JfE8JRigOwqLVMPjXva4B2iqjbAg+kEGQoIBXTDwY2IVGQgkEulOvjPL6lkAQItfG4y5077NjtTXT771Ld051/Fr6FfFnTmvnG/T7zxfOz/8yvvqdtclXxKoJhmw9w+kHAxc45Qho3GzmkUG9hVdj732GDILMAYwzk3KMJLHcp3IgAhyHuBlr2DAoOUQD/OCl4FpBPA6cEkTDIRi2X2LAVMwUIqiB9DNJjViptpMnoojazIGDoYrXt2co3RbAemnzgBmwUbc5JhxEdOsBs19zIL6lVAwwFnlmvno4jfv/vP/b33ZJz90nL/uu92r3fH7FO3RjewT5cn13ptu81Fb7Tpg7x/YcTDgAcHLbndLTEEGyp3rs7HsmIoxkBBZUA2kF4CVetZ7jL6tu8mmltee502Lzcpy22GnwQqdz5cXV2aBntsKIgN9keQcMjCWnkMGAMYSgoHKjfQkwMvnsey9ROnW+vR2/6YXYF+BwP7MC+/L9DiFcs/6TNPiimIxULz1UlwJO7emyuJcz9pGW0bFiqgeA1C+yvX3GBjpOH9/IYkMTC6o8NbnRQaCC5cFAwT0oxVRrZ4+57LLlaxVX2++HtQilX4j5o83qzszABvdu1GY4E/9D4D+HvEvwUrfheSL7nTvwz1/kG6s5fs55O8+LPgDOL/ZP/O78nQhFmpGBl70zccfU6ArARHrlodd/y1oDrCYf2kj7F+Ta8JIwT/08IVQQsz/054/oG20lYbztw7C/tbDATSdke77si/88woAkNqKiLKu6ZcP3dS3aTGQ/B6zIFrJurOP4ocUMqVvnnz36mZVdas+sXqQDNj7BygWTdvr0t3KibV+SmtAGAGW4fhE8DjDpH0atUzPcIoDOAg5uTYm4iGOdMToOM2JFjErXgbCyKAER4kqPKBnnTLIacBUht5TrRnhkQLBMaKndQKAwxr6se5q95UMOiEGnjJHcZhBBq3rnwWUgFjRhB65rxUFA//1ua8W8Iv8rvidG/vt7qQ22htKtf/c3hXtcl8zmP2+oF5P5zlSF8pr9XV8/Nkrglj8iyNCQQfrmEBNGCCk79fnBYuxSbFyAYtAQhiK4BS41COSpqm4VQqQpyGgMyr9oc56kktpRuSNUc23VNazgDHj4gitTLQWFgtLb+O++aXqlI4jm1PG/BQm3H31cDL7bJK651vFZMmAvX+AMUR1+Yj1eTicGPlre/p67WR/8FvrMXorCwSmpsl0k+tc45rpy3LKQYzgMyeoYJopsNvkFOe/qX9TaqunM0ookYvBj1mvB27+DXsd/sKfdoy+tiJy+ptXAaCGwWY/IfW2GPhQfZ45MHzK2GLAqWf6cfamxFYMUHEdcNbjGw5bWXDq+TIGZP+Asx7fcFgysHrq+Wk5DzAGnPX4hsPyvdyC085frfcPOOvxDYdlPr9R4XTy5ujbcmc9vuGwlc+JTCZHjh7OQydOr9L6mtBZj284LGPgtFWzVRXq/QPOenzDYYsBACLLyUzWRAjq/wrX+wec9fVGws7+Aajr4QTs7B9w1uMbDp+lh0Suip31+IbDZ2FA9g846/GNhs/EwDH7DzQYPgsDx+w/0Fj47B2Vznp8w+Gz9BVTgL3G33/gLL3l/479B8gZfl/w79h/gPzz+exfsf8Awi/7D3zZf+DL/gNf9h/4sv/AR95/4DPDyIDz9/ifGUYGnL/H/8wwXgecv8f/zDAy4Pw9/ueG9/4AoXlExBqpnZIAAAAASUVORK5CYII=) Once you click the button, the direct message to you will be updated to let you know that the step interaction was successfully completed: ![Sample step finished successfully](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARkAAABDBAMAAABaXGwBAAAAAXNSR0IArs4c6QAAACdQTFRFGhwh+vr64+PjyMnKsbK0mJmbgoOFZmdqSkxPMzQ4HB0hGhodERIXi+8jLwAAC5VJREFUeNrFWUtXG0cWrjND4lDVmxYPe5YiD9vZMWOM8GzwIMQjWcQBmUdYhBgbY2uRxCCwrUXMyxC0wBYCJLGoExOwqrXoc0JiqVsLnTO2JNT9o+beask8TuQ450ysS5dKX90q1df33nr4mnBKCKfwSQnnXNC/FnOSjFWXdcIJpURQ+OAExvzlmBmimnCdEAHdCMFx0F3QvxxzKrgQVMhPrEUFYzcOAzinzkvQd4TF72K0JBYu2QPvmmIUQdF83HExrSnGR35Q4ZCtLQaItXDsRWuM0WFORTkXRNQUO1sSp6/J1hRDoY7vkCSltcXICVuEcNZ+TTGKoICcrRI+a4uhphwqqRGE1xST8g4Ngkb7/2FSxlT8mfFcNuKpZRiGMIXQhCEEtFF8ynrTOIlJRc/5a0xOYhE7kvjp/lUwSNl5hqlpGhTTMIEVx8CilDOLo347TghglhNZUjGp9LcgFWyJ7HF88h7jBOqRvgrGCm1KNRRTPgbwkRHOuv51aUoHvTJ+hQp89xstvRywJFIxh4PZdEsvEUeYnmAj0HHH+lfFVEBxTGMAEcMxj2O5VlVt0IA4sMlfKBEtoqpqmEgVKXyS48T5EawK0JX/h9NKtKBtVmLbK6vwxGIyVk18aVMXpgnIsqkC2KRy/orbnGNdoE1QTElHgK+Qc2tHVA0pug1s/nsuR8TVpsdeTecmyTLrVXNOy2UJs01J/lnjSs+hS+OWYjBLzxG0zeDqZrd/q9u/GkNTa1uc08zsD2xrjojM7BxitlKeX9pIwAOQU8PhYomUJdBKpkCerZ8X1dB+13B2vONaQ59eUAO28Zuv5A0vXZr6sqG3OODTNoa8K10jOnvaZNuLareYaNvZuN01qvNkbLt7anNkcbZ3aSqGB1DGnyAsOjt8ODSzRtI9wTsbIZK5rsv5ZZGCxATMD8/hREvbGoYNxjF6qn26IXHNpYbGPW7XBf2VK8FZXeOh+7675Wyr6/y8S+1/oqot4CPyd/Xyzrj64a7q7njkcjcZYJvNyZHNkehkX3QEPEW16Mw6V2btYCFUuMvTY6Xh6Ij+4rou5wc9WkgeWPDnrKhvIC4adzQBSBAuGMTN+YL64Nqn41fmzwpy4Fon5ADYzLp+/OFps2jtm29+2hxxLasJkXar5/Ou+HcXdpufNP3sSkDcLN7v2fQNPvZN3I9hyCejiRcPBJ00yN5Dkv5KH44GzWhQl/NLDwl5zQERaA7zNxWlQzIzsFfrJxPqckPq0TlgA3EDthEEbTOnfhz+qflQDR00Pj37qqGorpHU5lXXvit17cN/Nj05V1TD4KnpW75N39yW7zZEMREiFU2kv2VLd0j6Oufpka2RpZ/Dk8u6nJ8T9IUUqDFuzNS4itKombi2OKEQNzm3vyE177CheTWkW2ib0D31wk/NMKdk0whsKLPz6mNg85G35xGwCQEb/+zE8khsq0+ucA4xs67penLSNDYf0rTv1s5S4fYosMH5Ue8cEDJ0TLBHxq1KCaGdBBXgqSvP3ZOune/+IdmIjPt88uYusqHzzX9rPnQHfmp6AmzSYIq9qffVLdfO+KfWzqNzeVeCJrd7Y9EgsBkBNrhAtWhCM+LZpSQvfkHSo5oeNW6Gl/XKFgzzVW7pFAnkVUf+Ldc4alob3K54a5t7bPzK04ZRkvoOIjaodqlzH39z9hdX33hT68WypxiGbkG9HGkYOPuk4ctGHaJ4Nba9igXYyJeOrtcnbolg/s7+XZL+grAltpxC2whaOXTKRWCovFQduYjLy+ACPKW6eq1F9eMU7H7uiwYvtqqXk1cbr861ur4uuM//5m4Io6fSLWtkr9U1alxt/OCq2vfIpfYhm9eyjfsN3785Wbi76Z/K3Ly+TiUborNlQ85PQY/eku7iuIrMX4CJC8o5C5CMKjjvslTfTujJONvGPTm5mmLJeDKVXM1p2+vKdhw09TEK7w5NFoFu9avZR+dijPD6E6cmzAn0jHUa07RkXOd03TGHeH0XRehsO+gpUwM2KMgGhAId09Shb1YXzCAmvgADTHRGmUVIlvCsyZnOddAJwkxKhM6ZyR6dswDr4pgApsw0CegJBxXhOuXQRgWV84Me5yufEQwixXztKaTG8cip/IH8CQy7GmIhhWLhbzneoSMgbmQUy1V1BYChSxWljp4DeGus5CSWPChFQm87njhXUrBNeYVDCRgGmOp0/uWd4HJ+BZxjZp3dr0GD74ZxOv/yTrCTv2Ea2MP81TkZSnL3O51/eRe4nL8RhrxtlU9NFOPd5nMcXLmTGihmZqDlo4fACwGvXf6GavLgzpaSKUvy0gSRRP8o/0L045i+uT9CGMElJtXzN859L2vgcWubhglYCEoUqlDy5nxLXOr1Mq5ff0N+hgkOmMbr1/8gf0ORTGmmq8Xl+tCXsJAMdnnh7eHJN+Zb0h0G4ngZ7wbekJ9Je8OA0578Z4C16FrV/A0F2xx+qTry0QNL0wQHivcmJ8Izx/Mr/Hi+hQAqdug4Q7+jJ7sBQaRI/VF/KbsjeL1DNriKI+Gq+RthmECmIg1hCB0k67MPU9M2zyoGy5o5GAPftJzNlaypOG3AxlZIsTPHchZJmQcByhWWY5TmRFbRoZFCpUFlKUpkx9SpWfTkO01OrUi4av6GaakF9UiaTdNAtt22fqbrlrZ0PbH32P+QULbk/yFzc0jfW+553y/bih0maBcv3yn6R/S0fzpAyJ7/QXStfsSEvmn/MKozN4fZzPXtgetbX7MgsOmf1OtvA5tq+RthFtzqMRk1BJ663w+lzngnn19e6qvzzXgMkm4Lfr0xdCMU8U34p/vrfEFPsaPQvtQPbBZuT+zcm7wRINrE7FQknPEU24J3F4ZupS8v9703FEy3B1dv3NrsVAbBU5/f4IWOSLhq/kak5tXj0mQJ3Br3u9qVCXs3YPW8HMt158ivY7Y2qMAvhV5dKXoOvrK6ix3PQqWeQqflt1/d7bbrApz6bAvZvByzSbedw8HPQla+01YWdvKdykQR4iYS2g1shKvmb0SmYhq3Kr+FBPZUng+EB82I3+97GUhNcO1ZiFBfLuOJhPL9mfaDMWXig44Fv789329c8g9OtcPcnN0b0pAN9M20l1ik2+/Lt/1YvDRlLqyV2XS++mxhZ2Otev7m1zKVilw0oFHoym5gkEXmVlbrAsoE5xFkU8KZKmyedyyAFthcXlmJeXJ1EDfPB/pes8nJwXSmTdvsCgObzxzbFD3+UpJXzd+wp8jleBznCGgT9rPAIHjKtnYDZneO1Y1ldb+FnnLYmH70lG3l+3Pdtm20QzfC7cP2Z6FDT13ALPlyWRys2Athu24M2FzJDiIbMtibM43q+Zv3VCkufFx4kOucinTbysDOjZUz7dvDB72bHoMW2rfvLEwtflVm49mEKM63b48UPD8u3F8KD6xAFNO5LU9d35Kn0L515979IA7eW53ev78RWlhLt29dQk+l5jvZ4lr1/A37Xj0ujQn0p77UNaxvtGnT3qk6v3cN6E97H6S9kAoI5UczvQfd3ofpPmPaeyfTNfrC60tELwUDFPp8m+4aGi5Bve/tITB4z9tb9PpSiwl6wzeT7s1/wSIhthCqnr8hkBA4kra1kuynC0aNdVKKgwuEQTgvxQ1GNV3AKCqwDUYb8RxLUqVe05kocQpYZ0kmuKw1WpKQ1WtUQJsmL+bKNFKQt5vfzd8QZokjUUrlfIoAvQ4jxd8DEnM8siv5lt0xuRoIjiaU4RqVkFDE8p8FjOB4yqicBn8PFRQWpvP7lP9e/gYLPOX/ReMSECll/Zm1kxjL/tpJDPJHmDq4/r5evf9R/gTb6Cks9YpxEqNeMU5iIt4WE6uKnpzKn9QYn8if0Bpjcip/QmuMj/InqKspRhGVRnl9riWGciJ/wmlt8fH8CahqiUGO5U9QS2uJyel8Sk0xOZ1PqSFGOZ1PqSk+nU+pLSan8yk1xf8DmegZGetPfTUAAAAASUVORK5CYII=) Now that we’ve gotten a feel for how we will use the custom step, let’s learn more about how function listeners work. ## Discovering listeners {#listeners} Now that we’ve seen how custom steps are used in Workflow Builder, let’s understand how the function listener code works to respond to an event when the step is triggered. We’ll first review the step definition in the `manifest.json`, then we’ll look at the two listener functions in our app code: one to let us know when the step starts, and one to let us know when someone clicks or taps one of the buttons we sent over. ### Defining the custom step {#define-custom-step} Opening the `manifest.json` file included in the sample app shows a `functions` property that includes a definition for our `sample_step`: ``` // manifest.json... "functions": { "sample_step": { "title": "Sample step", "description": "Runs sample step", "input_parameters": { "user_id": { "type": "slack#/types/user_id", "title": "User", "description": "Message recipient", "is_required": true, "hint": "Select a user in the workspace", "name": "user_id" } }, "output_parameters": { "user_id": { "type": "slack#/types/user_id", "title": "User", "description": "User that completed the step", "is_required": true, "name": "user_id" } } } } ``` From the step definition, we can see an input parameter and an output parameter defined. ### Inputs and outputs {#inputs-outputs} The custom step will take the following input: Message recipient (as a Slack User ID). The custom step will produce the following output: The user that completed the step. * When the step is invoked, a message will be sent to the user who invoked the workflow with a button to complete the step. * When the button is clicked, a message is posted indicating the step's completion. ### Implementing the function listener {#function-listener} The first thing we’ll do when adding a custom workflow step to our Bolt app is register a new **function listener**. In Bolt, a function listener allows developers to execute custom code in response to specific Slack events or actions by registering a method that handles predefined requests or commands. We register a function listener via the `function` method provided by our app instance. 1. Open your project’s `app.py` file in your code editor. 2. Between the initialization code for the app instance and the `sample_step` registration, you'll see a listener defined for our custom step: ``` # app.py...@app.function("sample_step")def handle_sample_step_event(inputs: dict, say: Say, fail: Fail, logger: logging.Logger): user_id = inputs["user_id"] try: say( channel=user_id, # sending a DM to this user text="Click the button to signal the step has completed", blocks=[ { "type": "section", "text": {"type": "mrkdwn", "text": "Click the button to signal the step has completed"}, "accessory": { "type": "button", "text": {"type": "plain_text", "text": "Complete step"}, "action_id": "sample_click", }, } ], ) except Exception as e: logger.exception(e) fail(f"Failed to handle a step request (error: {e})") ``` #### Anatomy of a .function() listener {#function-listener-anatomy} The function decorator (`function()`) accepts an argument of type `str` and is the unique callback ID of the step. For our custom step, we’re using `sample_step`. Every custom step you implement in an app needs to have a unique callback ID. The callback function is where we define the logic that will run when Slack tells the app that a user in the Slack client started a workflow that contains the `sample_step` custom step. The callback function offers various utilities that can be used to take action when a step execution event is received. The ones we’ll be using here are: * `inputs` provides access to the workflow variables passed into the step when the workflow was started * `fail` indicates when the step invoked for the current workflow step has an error * `logger` provides a Python standard logger instance * `say` calls the `chat.Postmessage` API method #### Understanding the function listener's callback logic {#function-listener-callback-logic} When our step is executed, we want a message to be sent to the invoking user. That message should include a button that prompts the user to complete the step. When Slack tells your Bolt app that the `sample_step` step was invoked, this step uses `chat.postMessage` to send a message to the `user_id` channel (which means this will be sent as a DM to the Slack user whose ID == `user_id`) with some text and blocks. The Block Kit element being sent as part of the message is a button, labeled 'Complete step' (which sends the `sample_click` action ID). Once the message is sent, your Bolt app will wait until the user has clicked the button. As soon as they click or tap the button, Slack will send back the action ID associated with the button to your Bolt app. In order for your Bolt app to listen for these actions, we’ll now define an action listener. ### Implementing the action listener {#action-listener} The message we send to the user will include the button prompting them to complete the step. To listen for and respond to this button click, you'll see an `.action()` listener to `app.py`, right after the function listener definition: ``` # app.py...@app.action("sample_click")def handle_sample_click( ack: Ack, body: dict, context: BoltContext, client: WebClient, complete: Complete, fail: Fail, logger: logging.Logger): ack() try: # Since the button no longer works, we should remove it client.chat_update( channel=context.channel_id, ts=body["message"]["ts"], text="Congrats! You clicked the button", ) # Signal that the step completed successfully complete({"user_id": context.actor_user_id}) except Exception as e: logger.exception(e) fail(f"Failed to handle a step request (error: {e})") ``` #### Anatomy of an .action() listener {#action-listener-anatomy} Similar to a function listener, the action listener registration method (`.action()`) takes two arguments: * The first argument is the unique callback ID of the action that your app will respond to. * The second argument is an asynchronous callback function, where we define the logic that will run when Slack tells our app that the user has clicked or tapped the button. Just like the function listener’s callback function, the action listener’s callback function offers various utilities that can be used to take action when an action event is received. The ones we’ll be using here are: * `client`, which provides access to Slack API methods * `action`, which provides the action’s event payload * `complete`, which is a utility method indicating to Slack that the step behind the workflow step that was just invoked has completed successfully * `fail`, which is a utility method for indicating that the step invoked for the current workflow step had an error #### Understanding the action listener's callback logic {#action-listener-callback-logic} Recall that we sent over a message with the button back in the function listener. When the button is pressed, we want to complete the step, update the message, and define `outputs` that can be used for subsequent steps in Workflow Builder. Slack will send an action event payload to your app when the button is clicked or tapped. In the action listener, we extract all the information we can use, and if all goes well, let Slack know the step was successful by invoking `complete`. We also handle cases where something goes wrong and produces an error. ## Next steps {#next-steps} That's it — we hope you learned a lot! In this tutorial, we added custom steps via the manifest, but if you'd like to see how to add custom steps in the [app settings](https://api.slack.com/apps) to an existing app, follow along with the [Create a custom step for Workflow Builder: existing Bolt app](/tools/bolt-python/tutorial/custom-steps-workflow-builder-existing) tutorial. If you're interested in exploring how to create custom steps to use in Workflow Builder as steps with our Deno Slack SDK, too, that tutorial can be found [here](/tools/deno-slack-sdk/tutorials/workflow-builder-custom-step/). --- Source: https://docs.slack.dev/tools/bolt-python/tutorial/custom-steps # Custom Steps This feature requires a paid plan If you don't have a paid workspace for development, you can join the [Developer Program](https://api.slack.com/developer-program) and provision a sandbox with access to all Slack features for free. With custom steps for Bolt apps, your app can create and process workflow steps that users later add in Workflow Builder. This guide goes through how to build a custom step for your app using the [app settings](https://api.slack.com/apps). If you're looking to build a custom step using the Deno Slack SDK, check out our guide on [creating a custom step for Workflow Builder with the Deno Slack SDK](/tools/deno-slack-sdk/tutorials/workflow-builder-custom-step/). You can also take a look at the template for the [Bolt for Python custom workflow step](https://github.com/slack-samples/bolt-python-custom-step-template) on GitHub. There are two components of a custom step: the step definition in the app manifest, and a listener to handle the `function_executed` event in your project code. ## Opt in to org-ready apps {#org-ready-apps} Before we create the step definition, we first need to opt in to organization-ready apps. The app must opt-in to org-ready apps to be able to add the custom step to its manifest. This can be done in one of two ways: * Set the manifest `settings.org_deploy_enabled` property to `true`. * Alternatively, navigate to your [apps](https://api.slack.com/apps), select your app, then under the **Features** section in the navigation, select **Org Level Apps** and then **Opt-In**. Whichever method you use, the following will be reflected in the app manifest as such: ``` "settings": { "org_deploy_enabled": true, ... } ``` Next, the app must be installed at the organization level. While it is possible to install the app at a workspace level, doing so means that the custom steps will not appear in Workflow Builder. To remedy this, install the app at the organization level. If you are a developer who is not an admin of their organization, you will need to request an Org Admin to perform this installation at the organization level. To do this: * Navigate to your [apps](https://api.slack.com/apps) page and select the app you'd like to install. * Under **Settings**, select **Collaborators**. * Add an Org Admin as a collaborator. The Org Admin can then install your app directly at the org level from the [app settings](https://api.slack.com/apps) page. ## Defining the custom step {#define-step} A workflow step's definition contains information about the step, including its `input_parameters`, `output_parameters`, as well as display information. Each step is defined in the `functions` object of the manifest. Each entry in the `functions` object is a key-value pair representing each step. The key is the step's `callback_id`, which is any string you wish to use to identify the step (max 100 characters), and the value contains the details listed in the table below for each separate custom step. We recommend using the step's name, like `sample_step` in the code example below for the step's `callback_id`. Field Type Description Required? `title` String A string to identify the step. Max 255 characters. Yes `description` String A succinct summary of what your step does. No `input_parameters` Object An object which describes one or more [input parameters](#inputs-outputs) that will be available to your step. Each top-level property of this object defines the name of one input parameter available to your step. No `output_parameters` Object An object which describes one or more [output parameters](#inputs-outputs) that will be returned by your step. Each top-level property of this object defines the name of one output parameter your step makes available. No Once you are in your [app settings](https://api.slack.com/apps), navigate to **Workflow Steps** in the left nav. Click **Add Step** and fill out your step details, including callback ID, name, description, input parameters, and output parameters. ### Defining input and output parameters {#inputs-outputs} Step inputs and outputs (`input_parameters` and `output_parameters`) define what information goes into a step before it runs and what comes out of a step after it completes, respectively. Both inputs and outputs adhere to the same schema and consist of a unique identifier and an object that describes the input or output. Each input or output that belongs to `input_parameters` or `output_parameters` must have a unique key. Field Type Description `type` String Defines the data type and can fall into one of two categories: primitives or Slack-specific. `title` String The label that appears in Workflow Builder when a user sets up this step in their workflow. `description` String The description that accompanies the input when a user sets up this step in their workflow. `dynamic_options` Object For custom steps dynamic options in Workflow Builder, define this property and point to a custom step designed to return the set of dynamic elements once the step is added to a workflow within Workflow Builder. Dynamic options in Workflow Builder can be rendered in one of two ways: as a drop-down menu (single-select or multi-select), or as a set of fields. Refer to custom steps dynamic options for Workflow Builder using [Bolt for JavaScript](/tools/bolt-js/concepts/custom-steps-dynamic-options/) or [Bolt for Python](https://docs.slack.dev/tools/bolt-python/concepts/custom-steps-dynamic-options/) for more details. `is_required` Boolean Indicates whether or not the input is required by the step in order to run. If it’s required and not provided, the user will not be able to save the configuration nor use the step in their workflow. This property is available only in v1 of the manifest. We recommend v2, using the `required` array as noted in the example above. `hint` String Helper text that appears below the input when a user sets up this step in their workflow. In addition, the `dynamic_options` field has two required properties: Property Type Description `function` String A reference to the custom step that should be used as a dynamic option. `inputs` Object Maps the inputs from the custom step consuming the dynamic option to the inputs required by the step used as a dynamic option. For example: ``` "inputs": { "category": { "value": "{{input_parameters.category}}" }} ``` Once you've added your step details, save your changes, then navigate to **App Manifest**. Notice your new step configuration reflected in the `function` property! #### Sample manifest {#sample-manifest} Here is a sample app manifest laying out a step definition. This definition tells Slack that the step in our workspace with the callback ID of `sample_step` belongs to our app, and that when it runs, we want to receive information about its execution event. ``` "functions": { "sample_step": { "title": "Sample step", "description": "Runs sample step", "input_parameters": { "properties": { "user_id": { "type": "slack#/types/user_id", "title": "User", "description": "Message recipient", "hint": "Select a user in the workspace", "name": "user_id" } }, "required": [ "user_id" ] }, "output_parameters": { "properties": { "user_id": { "type": "slack#/types/user_id", "title": "User", "description": "User that received the message", "name": "user_id" } }, "required": [ "user_id" ] }, }} ``` ### Adding steps for existing apps {#existing-apps} If you are adding custom steps to an existing app directly to the app manifest, you will also need to add the `function_runtime` property to the app manifest. Do this in the `settings` section as such: ``` "settings": { ... "function_runtime": "remote"} ``` If you are adding custom steps in the **Workflow Steps** section of the [App Config](https://api.slack.com/apps) as shown above, then this will be added automatically. ## Listening to function executions {#listener} When your custom step is executed in a workflow, your app will receive a `function_executed` event. The callback provided to the `function()` method will be run when this event is received. The callback is where you can access `inputs`, make third-party API calls, save information to a database, update the user’s Home tab, or set the output values that will be available to subsequent workflow steps by mapping values to the `outputs` object. Your app must call `complete()` to indicate that the step’s execution was successful, or `fail()` to signal that the step failed to complete. Notice in the example code here that the name of the step, `sample_step`, is the same as it is listed in the manifest above. This is required. ``` @app.function("sample_step")def handle_sample_step_event(client: WebClient, inputs: dict, fail: Fail, complete: Complete, logger: logging.Logger): user_id = inputs["user_id"] try: client.chat_postMessage( channel=user_id, text=f"Greetings <@{user_id}>!" ) complete({"user_id": user_id}) except Exception as e: logger.exception(e) fail(f"Failed to complete the step: {e}") ``` Here's another example. Note in this snippet, the name of the step, `create_issue`, must be listed the same as it is listed in the manifest file. ``` @app.function("create_issue")def create_issue_callback(ack: Ack, inputs: dict, fail: Fail, complete: Complete, logger: logging.Logger): ack() JIRA_BASE_URL = os.getenv("JIRA_BASE_URL") headers = { "Authorization": f'Bearer {os.getenv("JIRA_SERVICE_TOKEN")}', "Accept": "application/json", "Content-Type": "application/json", } try: project: str = inputs["project"] issue_type: str = inputs["issuetype"] url = f"{JIRA_BASE_URL}/rest/api/latest/issue" payload = json.dumps( { "fields": { "description": inputs["description"], "issuetype": {"id" if issue_type.isdigit() else "name": issue_type}, "project": {"id" if project.isdigit() else "key": project}, "summary": inputs["summary"], }, } ) response = requests.post(url, data=payload, headers=headers) response.raise_for_status() json_data = json.loads(response.text) complete(outputs={ "issue_id": json_data["id"], "issue_key": json_data["key"], "issue_url": f'https://{JIRA_BASE_URL}/browse/{json_data["key"]}' }) except Exception as e: logger.exception(e) fail(f"Failed to handle a step request (error: {e})") ``` ### Anatomy of a function listener {#anatomy} The first argument (in our case above, `sample_step`) is the unique callback ID of the step. After receiving an event from Slack, this identifier is how your app knows which custom step handler to invoke. This `callback_id` also corresponds to the step definition provided in your manifest file. The second argument is the callback function, or the logic that will run when your app receives notice from Slack that `sample_step` was run by a user—in the Slack client—as part of a workflow. Field Description `client` A `WebClient` instance used to make things happen in Slack. From sending messages to opening modals, `client` makes it all happen. For a full list of available methods, refer to the [Web API methods](/reference/methods). Read more about the `WebClient` for Bolt Python [here](https://docs.slack.dev/tools/bolt-python/concepts/web-api/). `complete` A utility method that invokes `functions.completeSuccess`. This method indicates to Slack that a step has completed successfully without issue. When called, `complete` accepts an optional `outputs` object that matches your step definition in [`output_parameters`](#inputs-outputs). `fail` A utility method that invokes `functions.completeError`. True to its name, this method signals to Slack that a step has failed to complete. The `fail` method requires an argument of `error` to be sent along with it, which is used to help users understand what went wrong. `inputs` An alias for the `input_parameters` that were provided to the step upon execution. ## Responding to interactivity {#interactivity} Interactive elements provided to the user from within the `function()` method’s callback are associated with that unique `function_executed` event. This association allows for the completion of steps at a later time, like once the user has clicked a button. Incoming actions that are associated with a step have the same `inputs`, `complete`, and `fail` utilities as offered by the `function()` method. ``` # If associated with a step, step-specific utilities are made available @app.action("sample_click")def handle_sample_click(context: BoltContext, complete: Complete, fail: Fail, logger: logging.Logger): try: # Signal the step has completed once the button is clicked complete({"user_id": context.actor_user_id}) except Exception as e: logger.exception(e) fail(f"Failed to handle a step request (error: {e})") ``` ## Deploying a custom step {#deploy} When you're ready to deploy your steps for wider use, you'll need to decide _where_ to deploy, since Bolt apps are not hosted on the Slack infrastructure. ### Control step access {#access} You can choose who has access to your custom steps. To define this, refer to the [custom function access](/tools/deno-slack-sdk/guides/controlling-access-to-custom-functions) page. ### Distribution {#distribution} Distribution works differently for Slack apps that contain custom steps when the app is within a standalone (non-Enterprise Grid) workspace versus within an Enterprise Grid organization. * **Within a standalone workspace**: Slack apps that contain custom steps can be installed on the same workspace and used within that workspace. We do not support distribution to other standalone workspaces (also known as public distribution). * **Within an organization**: Slack apps that contain custom steps should be org-ready (enabled for private distribution) and installed on the organization level. They must also be granted access to at least one workspace in the organization for the steps to appear in Workflow Builder. Apps containing custom steps cannot be distributed publicly or submitted to the Slack Marketplace. We recommend sharing your code as a public repository in order to share custom steps in Bolt apps. ## Related tutorials {#tutorials} * [Custom steps for Workflow Builder (new app)](/tools/bolt-python/tutorial/custom-steps-workflow-builder-new) * [Custom steps for Workflow Builder (existing app)](/tools/bolt-python/tutorial/custom-steps-workflow-builder-existing/) --- Source: https://docs.slack.dev/tools/bolt-python/tutorial/modals # Modals If you're learning about Slack apps, modals, or slash commands for the first time, you've come to the right place! In this tutorial, we'll take a look at setting up your very own server using GitHub Codespaces, then using that server to run your Slack app built with the [**Bolt for Python framework**](https://github.com/SlackAPI/bolt-python). GitHub Codespaces GitHub Codespaces is an online IDE that allows you to work on code and host your own server at the same time. While Codespaces is good for testing and development purposes, it should not be used in production. At the end of this tutorial, your final app will look like this: ![announce](/assets/images/announce-300d499a5bd993554af552f555596931.gif) And will make use of these Slack concepts: * [**Block Kit**](/block-kit/) is a UI framework for Slack apps that allows you to create beautiful, interactive messages within Slack. If you've ever seen a message in Slack with buttons or a select menu, that's Block Kit. * [**Modals**](/surfaces/modals) are a pop-up window that displays right in Slack. They grab the attention of the user, and are normally used to prompt users to provide some kind of information or input in a form. * [**Slash Commands**](/interactivity/implementing-slash-commands) allow you to invoke your app within Slack by just typing into the message composer box. e.g. `/remind`, `/topic`. If you're familiar with using Heroku you can also deploy directly to Heroku with the following button. [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://www.heroku.com/deploy?template=https://github.com/wongjas/modal-example) * * * ## Setting up your app within App Settings {#setting-up-app-settings} You'll need to create an app and configure it properly within App Settings before using it. 1. [Create a new app](https://api.slack.com/apps/new), click `From a Manifest`, and choose the workspace that you want to develop on. Then copy the following JSON object; it describes the metadata about your app, like its name, its bot display name and permissions it will request. ``` { "display_information": { "name": "Intro to Modals" }, "features": { "bot_user": { "display_name": "Intro to Modals", "always_online": false }, "slash_commands": [ { "command": "/announce", "description": "Makes an announcement", "should_escape": false } ] }, "oauth_config": { "scopes": { "bot": [ "chat:write", "commands" ] } }, "settings": { "interactivity": { "is_enabled": true }, "org_deploy_enabled": false, "socket_mode_enabled": true, "token_rotation_enabled": false }} ``` 2. Once your app has been created, scroll down to `App-Level Tokens` and create a token that requests for the [`connections:write`](/reference/scopes/connections.write) scope, which allows you to use [Socket Mode](/apis/events-api/using-socket-mode), a secure way to develop on Slack through the use of WebSockets. Copy the value of your app token and keep it for safe-keeping. 3. Install your app by heading to `Install App` in the left sidebar. Hit `Allow`, which means you're agreeing to install your app with the permissions that it is requesting. Be sure to copy the token that you receive, and keep it somewhere secret and safe. ## Starting your Codespaces server {#starting-server} 1. Log into GitHub and head to this [repository](https://github.com/wongjas/modal-example). 2. Click the green `Code` button and hit the `Codespaces` tab and then `Create codespace on main`. This will bring up a code editor within your browser so you can start coding. ## Understanding the project files {#understanding-files} Within the project you'll find a `manifest.json` file. This is a a configuration file used by Slack apps. With a manifest, you can create an app with a pre-defined configuration, or adjust the configuration of an existing app. The `simple_modal_example.py` Python script contains the code that powers your app. If you're going to tinker with the app itself, take a look at the comments found within the `simple_modal_example.py` file! The `requirements.txt` file contains the Python package dependencies needed to run this app. This repo contains optional Heroku-specific configurations The `app.json` file defines your Heroku app configuration including environment variables and deployment settings, to allow your app to deploy with one click. `Procfile` is a Heroku-specific file that tells Heroku what command to run when starting your app — in this case a Python script would run as a `worker` process. If you aren't deploying to Heroku, you can ignore both these files. ## Adding tokens {#adding-tokens} 1. Open a terminal up within the browser's editor. 2. Grab the app and bot tokens that you kept safe. We're going to set them as environment variables. ``` export SLACK_APP_TOKEN=export SLACK_BOT_TOKEN= ``` ## Running the app {#running-app} 1. Activate a virtual environment for your Python packages to be installed. ``` # Setup your python virtual environmentpython3 -m venv .venvsource .venv/bin/activate ``` 2. Install the dependencies from the `requirements.txt` file. ``` # Install the dependenciespip install -r requirements.txt ``` 3. Start your app using the `python3 simple_modal_example.py` command. ``` # Start your local serverpython3 simple_modal_example.py ``` 4. Now that your app is running, you should be able to see it within Slack. Test this by heading to Slack and typing `/announce`. All done! 🎉 You've created your first slash command using Block Kit and modals! The world is your oyster; play around with [Block Kit Builder](https://app.slack.com/block-kit-builder) and create more complex modals and place them in your code to see what happens! ## Next steps {#next-steps} If you want to learn more about Bolt for Python, refer to the [Getting Started guide](https://docs.slack.dev/tools/bolt-python/getting-started). --- Source: https://docs.slack.dev/tools/bolt-python/tutorial/order-confirmation # Create a Salesforce order confirmation app In this tutorial, you'll use the [Bolt for Python](/tools/bolt-python/) framework and [Block Kit Builder](https://app.slack.com/block-kit-builder) to create an order confirmation app that links to a system of record, like Salesforce. The Slack app will: * allow users to enter order numbers from within Slack, along with some additional order information, * post that information to a Slack channel, and * send the information to the system of record. End users will be able to enter information across devices, as many will likely be using a mobile device. Along the way, you'll learn how to use the Bolt for Python starter app template as a jumping off point for your own custom apps. Let's begin! Consider the following This tutorial was created for educational purposes within a Slack workshop. As a result, it has not been tested quite as rigorously as our sample apps. Proceed carefully if you'd like to use a similar app in production. ## Getting started {#getting-started} ### Installing the Slack CLI {#installing-the-slack-cli} If you don't already have the Slack CLI, install it from your terminal: navigate to the installation guide ([for Mac and Linux](/tools/slack-cli/guides/installing-the-slack-cli-for-mac-and-linux) or [for Windows](/tools/slack-cli/guides/installing-the-slack-cli-for-windows)) and follow the steps. ### Cloning the starter app {#cloning-the-starter-app} Once installed, use the command `slack create` to get started with the Bolt for Python [starter template](https://github.com/slack-samples/bolt-python-starter-template). Alternatively, you can clone the template using Git. You can remove the portions from the template that are not used within this tutorial to make things a bit cleaner for yourself. To do this, open your project in VS Code (you can do this from the terminal with the `code .` command) and delete the `commands`, `events`, and `shortcuts` folders from the `/listeners` folder. You can also do the same to the corresponding folders within the `/listeners/tests` folder as well. Finally, remove the imports of these files from the `/listeners/__init__.py` file. ## Creating your app {#creating-your-app} We’ll use the contents of the `manifest.json` file below. This file describes the metadata associated with your app, like its name and permissions that it requests. These values are used to create an app in one of two ways: * **With the Slack CLI**: Save the contents of the file to your project's `manifest.json` file then skip ahead to [starting your app](#starting-your-app). * **With app settings**: Copy the contents of the file and [create a new app](https://api.slack.com/apps/new). Next, choose **From a manifest** and follow the prompts, pasting the manifest file contents you copied. ``` { "_metadata": { "major_version": 1, "minor_version": 1 }, "display_information": { "name": "Delivery Tracker App" }, "features": { "bot_user": { "display_name": "Delivery Tracker App", "always_online": false } }, "oauth_config": { "scopes": { "bot": [ "channels:history", "chat:write" ] } }, "settings": { "event_subscriptions": { "bot_events": [ "message.channels" ] }, "interactivity": { "is_enabled": true }, "org_deploy_enabled": false, "socket_mode_enabled": true, "token_rotation_enabled": false }} ``` ### Tokens {#tokens} Once your app has been created, scroll down to **App-Level Tokens** on the **Basic Information** page and create a token that requests the [`connections:write`](/reference/scopes/connections.write) scope. This token will allow you to use [Socket Mode](/apis/events-api/using-socket-mode), which is a secure way to develop on Slack through the use of WebSockets. Save the value of your app token and store it in a safe place (we’ll use it in the next step). ### Install app {#install-app} Still in the app settings, navigate to the **Install App** page in the left sidebar. Install your app. When you press **Allow**, this means you’re agreeing to install your app with the permissions that it’s requesting. Copy the bot token that you receive as well and store this in a safe place as well for subsequent steps. ## Saving credentials {#saving-credentials} Within a terminal of your choice, set the two tokens from the previous step as environment variables using the commands below. Make sure not to mix these two up, `SLACK_APP_TOKEN` will start with “xapp-“ and `SLACK_BOT_TOKEN` will start with “xoxb-“. For macOS: ``` export SLACK_APP_TOKEN=export SLACK_BOT_TOKEN= ``` For Windows Command Prompt: ``` set SLACK_APP_TOKEN=set SLACK_BOT_TOKEN= ``` For Windows PowerShell: ``` $env:SLACK_APP_TOKEN="YOUR-APP-TOKEN-HERE"$env:SLACK_BOT_TOKEN="YOUR-BOT-TOKEN-HERE" ``` ## Starting your app {#starting-your-app} Run the following commands to activate a virtual environment for your Python packages to be installed, install the dependencies, and start your app. ``` # Setup your python virtual environmentpython -m venv .venvsource .venv/bin/activate# Install the dependenciespip install -r requirements.txt# Start your local serverslack run ``` If you're not using the Slack CLI, a different `python` command can be used to start your app instead: ``` python app.py ``` Now that your app is running, you should be able to see it within Slack. In Slack, create a channel that you can test in and try inviting your bot to it using the `/invite @Your-app-name-here` command. Check that your app works by saying “hi” in the channel where your app is, and you should receive a message back from it. If you don’t, ensure you completed all the steps above. ## Coding the app {#coding-the-app} We'll make four changes to the app: * Update the “hi” message to something more interesting and interactive * Handle when the wrong delivery ID button is pressed * Handle when the correct delivery IDs are sent and bring up a modal for more information * Send the information to all of the places needed when the form is submitted (including third-party locations) For all of these steps, we will use [Block Kit Builder](https://app.slack.com/block-kit-builder), a tool that helps you create messages, modals and other surfaces within Slack. Open [Block Kit Builder](https://app.slack.com/block-kit-builder), take a look, and play around! We’ll create some views next. ### Updating the "hi" message {#updating-the-hi-message} The first thing we want to do is change the “hi, how are you?” message from our app into something more useful. Here’s a `blocks` object built with Block Kit Builder: ``` "blocks": [ { "type": "section", "text": { "type": "mrkdwn", "text": "Confirm *{delivery_id}* is correct?" } }, { "type": "actions", "elements": [ { "type": "button", "text": { "type": "plain_text", "text": "Correct", "emoji": true }, "style": "primary", "action_id": "approve_delivery" }, { "type": "button", "text": { "type": "plain_text", "text": "Not correct", "emoji": true }, "style": "danger", "action_id": "deny_delivery" } ] } ] ``` Take the function below and place your blocks within the blocks dictionary `[]`. ``` def delivery_message_callback(context: BoltContext, say: Say, logger: Logger): try: delivery_id = context["matches"][0] say( blocks=[] # insert your blocks here ) except Exception as e: logger.error(e) ``` Update the payload: * Remove the initial blocks key and convert any boolean true values to `True` to fit with Python conventions. * If you see variables within `{}` brackets, this is part of an f-string, which allows you to insert variables within strings in a clean manner. Place the `f` character before these strings like this: ``` { "type": "section", "text": { "type": "mrkdwn", "text": f"Confirm *{delivery_id}* is correct?", # place the "f" character here at the beginning of the string },}, ``` Place all of this in the `sample_message.py` file. Next, you’ll need to register this listener to respond when a message is sent in the channel with your app. Head to `messages/__init__.py` and overwrite the function there with the one below, which registers the function. Don’t forget to add the import to the callback function as well! ``` from .sample_message import delivery_message_callback # import the function to this filedef register(app: App): # This regex will capture any number letters followed by dash # and then any number of digits, our "confirmation number" e.g. ASDF-1234 app.message(re.compile(r"[A-Za-z]+-\d+"))(delivery_message_callback) ## add this line! ``` Now, restart your server to bring in the new code and test that your function works by sending an order confirmation ID, like `HWOA-1524`, in your testing channel. Your app should respond with the message you created within Block Kit Builder. ### Handling an incorrect delivery ID {#handling-an-incorrect-delivery-id} Notice that if you try to click on either of the buttons within your message, nothing will happen. This is because we have yet to create a function to handle the button click. Let’s start with the `Not correct` button first. 1. Head to Block Kit Builder once again. We want to build a message that lets the user know that the wrong order ID has been submitted. Here's a [section](/reference/block-kit/blocks/section-block) block to get you started: ``` "blocks": [ { "type": "section", "text": { "type": "mrkdwn", "text": "Delivery *{delivery_id}* was incorrect ❌" } } ] ``` View this block in Block Kit Builder [here](https://app.slack.com/block-kit-builder/#%7B%22blocks%22:%5B%7B%22type%22:%22section%22,%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22Delivery%20*%7Bdelivery_id%7D*%20was%20incorrect%20%E2%9D%8C%22%7D%7D%5D%7D). 2. Once you have something that you like, add it to the function below and place the function within the `actions/sample_action.py` file. Remember to make any strings with variables into f-strings! ``` def deny_delivery_callback(ack, body, client, logger: Logger): try: ack() delivery_id = body["message"]["text"].split("*")[1] # Calls the chat.update function to replace the message, # preventing it from being pressed more than once. client.chat_update( channel=body["container"]["channel_id"], ts=body["container"]["message_ts"], blocks=[], # Add your blocks here! ) logger.info(f"Delivery denied by user {body['user']['id']}") except Exception as e: logger.error(e) ``` This function will call the [`chat.update`](/reference/methods/chat.update) Web API method, which will update the original message with buttons, to the one that we created previously. This will also prevent the message from being pressed more than once. 3. Make the connection to this function again within the `actions/__init__.py` folder with the following code: ``` from slack_bolt import Appfrom .sample_action import sample_action_callback # This can be deletedfrom .sample_action import deny_delivery_callbackdef register(app: App): app.action("sample_action_id")(sample_action_callback) # This can be deleted app.action("deny_delivery")(deny_delivery_callback) # Add this line ``` Test out your app by sending in a confirmation number into your channel and clicking the `Not correct` button. If the message is updated, then you’re good to go onto the next step. ### Handling a correct delivery ID {#handling-a-correct-delivery-id} The next step is to handle the `Confirm` button. In this case, we’re going to pull up a modal instead of just a message. 1. Using the following modal as a base; create a modal that captures the kind of information that you need. ``` { "title": { "type": "plain_text", "text": "Approve Delivery" }, "submit": { "type": "plain_text", "text": "Approve" }, "type": "modal", "callback_id": "approve_delivery_view", "private_metadata": "{delivery_id}", "blocks": [ { "type": "section", "text": { "type": "mrkdwn", "text": "Approving delivery *{delivery_id}*" } }, { "type": "input", "block_id": "notes", "label": { "type": "plain_text", "text": "Additional delivery notes" }, "element": { "type": "plain_text_input", "action_id": "notes_input", "multiline": true, "placeholder": { "type": "plain_text", "text": "Add notes..." } }, "optional": true }, { "type": "input", "block_id": "location", "label": { "type": "plain_text", "text": "Delivery Location" }, "element": { "type": "plain_text_input", "action_id": "location_input", "placeholder": { "type": "plain_text", "text": "Enter the location details..." } }, "optional": true }, { "type": "input", "block_id": "channel", "label": { "type": "plain_text", "text": "Notification Channel" }, "element": { "type": "channels_select", "action_id": "channel_select", "placeholder": { "type": "plain_text", "text": "Select channel for notifications" } }, "optional": false } ]} ``` View this modal in Block Kit Builder [here](https://app.slack.com/block-kit-builder/#%7B%22type%22:%22modal%22,%22callback_id%22:%22approve_delivery_view%22,%22title%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Approve%20Delivery%22%7D,%22private_metadata%22:%22%7Bdelivery_id%7D%22,%22blocks%22:%5B%7B%22type%22:%22section%22,%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22Approving%20delivery%20*%7Bdelivery_id%7D*%22%7D%7D,%7B%22type%22:%22input%22,%22block_id%22:%22notes%22,%22label%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Additional%20delivery%20notes%22%7D,%22element%22:%7B%22type%22:%22plain_text_input%22,%22action_id%22:%22notes_input%22,%22multiline%22:true,%22placeholder%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Add%20notes...%22%7D%7D,%22optional%22:true%7D,%7B%22type%22:%22input%22,%22block_id%22:%22location%22,%22label%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Delivery%20Location%22%7D,%22element%22:%7B%22type%22:%22plain_text_input%22,%22action_id%22:%22location_input%22,%22placeholder%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Enter%20the%20location%20details...%22%7D%7D,%22optional%22:true%7D,%7B%22type%22:%22input%22,%22block_id%22:%22channel%22,%22label%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Notification%20Channel%22%7D,%22element%22:%7B%22type%22:%22channels_select%22,%22action_id%22:%22channel_select%22,%22placeholder%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Select%20channel%20for%20notifications%22%7D%7D,%22optional%22:false%7D%5D,%22submit%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Approve%22%7D%7D). 2. Within the `actions/sample_action.py` file, add the following function, replacing the view with the one you created above. Again, any strings with variables will be updated to f-strings and also any booleans will need to be capitalized. ``` def approve_delivery_callback(ack, body, client, logger: Logger): try: ack() delivery_id = body["message"]["text"].split("*")[1] # Updates the original message so you can't press it twice client.chat_update( channel=body["container"]["channel_id"], ts=body["container"]["message_ts"], blocks=[ { "type": "section", "text": { "type": "mrkdwn", "text": f"Processed delivery *{delivery_id}*...", }, } ], ) # Open a modal to gather information from the user client.views_open( trigger_id=body["trigger_id"], view={} # Add your view here ) logger.info(f"Approval modal opened by user {body['user']['id']}") except Exception as e: logger.error(e) ``` Similar to the `deny` button, we need to hook up all the connections. Your `actions/__init__.py` should look something like this: ``` from slack_bolt import Appfrom .sample_action import deny_delivery_callbackfrom .sample_action import approve_delivery_callbackdef register(app: App): app.action("approve_delivery")(approve_delivery_callback) app.action("deny_delivery")(deny_delivery_callback) ``` Test your app by typing in a confirmation number in channel, click the confirm button and see if the modal comes up and you are able to capture information from the user. ### Submitting the form {#submitting-the-form} Lastly, we’ll handle the submission of the form, which will trigger two things. We want to send the information into the specified channel, which will let the user know that the form was successful, as well as send the information into our system of record, Salesforce. 1. Here’s a simple example of a message that you can use to present the information in channel. ``` "blocks": [ { "type": "section", "text": { "type": "mrkdwn", "text": "✅ Delivery *{delivery_id}* approved:" } }, { "type": "section", "text": { "type": "mrkdwn", "text": "*Delivery Notes:*\n{notes or 'None'}" } }, { "type": "section", "text": { "type": "mrkdwn", "text": "*Delivery Location:*\n{loc or 'None'}" } } ] ``` View this in Block Kit Builder [here](https://app.slack.com/block-kit-builder/?1#%7B%22blocks%22:%5B%7B%22type%22:%22section%22,%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22%E2%9C%85%20Delivery%20*%7Bdelivery_id%7D*%20approved:%22%7D%7D,%7B%22type%22:%22section%22,%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22*Delivery%20Notes:*%5Cn%7Bnotes%20or%20'None'%7D%22%7D%7D,%7B%22type%22:%22section%22,%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22*Delivery%20Location:*%5Cn%7Bloc%20or%20'None'%7D%22%7D%7D%5D%7D). Modify it however you like and then place it within the code below in the `/views/sample_views.py` file. ``` def handle_approve_delivery_view(ack, client, view, logger: Logger): try: ack() delivery_id = view["private_metadata"] values = view["state"]["values"] notes = values["notes"]["notes_input"]["value"] loc = values["location"]["location_input"]["value"] channel = values["channel"]["channel_select"]["selected_channel"] client.chat_postMessage( channel=channel, blocks=[], ## Add your message here ) except Exception as e: logger.error(f"Error in approve_delivery_view: {e}") ``` 2. Making the connections in the `/views/__init__.py` file, we can test that this works by sending a message once again in our test channel. ``` from slack_bolt import Appfrom .sample_view import handle_approve_delivery_viewdef register(app: App): app.view("sample_view_id")(sample_view_callback) # This can be deleted app.view("approve_delivery_view")(handle_approve_delivery_view) ## Add this line ``` 3. Let’s also send the information to Salesforce. There are [several ways](https://github.com/simple-salesforce/simple-salesforce?tab=readme-ov-file#examples) for you to access Salesforce through its API, but in this example, we’ve utilized `username`, `password` and `token` parameters. If you need help with getting your API token for Salesforce, take a look at [this article](https://help.salesforce.com/s/articleView?id=xcloud.user_security_token.htm&type=5). You’ll need to add these values as environment variables like we did earlier with our Slack tokens. You can use the following commands: ``` export SF_USERNAME=export SF_PASSWORD=export SF_TOKEN= ``` 4. We’re going to use assume that order information is stored in the Order object and that the confirmation IDs map to the 8-digit Order numbers within Salesforce. Given that assumption, we need to make a query to find the correct object, add the inputted information, and we’re done. Place this functionality before the last excerpt within the `/views/sample_views.py` file. ``` # Extract just the numeric portion from delivery_id delivery_number = "".join(filter(str.isdigit, delivery_id)) # Update Salesforce order object try: sf = Salesforce( username=os.environ.get("SF_USERNAME"), password=os.environ.get("SF_PASSWORD"), security_token=os.environ.get("SF_TOKEN"), ) # Assuming delivery_id maps to Salesforce Order number order = sf.query(f"SELECT Id FROM Order WHERE OrderNumber = '{delivery_number}'") # noqa: E501 if order["records"]: order_id = order["records"][0]["Id"] sf.Order.update( order_id, { "Status": "Delivered", "Description": notes, "Shipping_Location__c": loc, }, ) logger.info(f"Updated order {delivery_id}") else: logger.warning(f"No order found for {delivery_id}") except Exception as sf_error: logger.error(f"Update failed for order {delivery_id}: {sf_error}") # Continue execution even if Salesforce update fails ``` You’ll also need to add the two imports that are found within this code to the top of the file. ``` import osfrom simple_salesforce import Salesforce ``` With these imports, add `simple_salesforce` to your `requirements.txt` file, then install that package with the following command once again. ``` pip install -r requirements.txt ``` ![Image of delivery tracker app](/assets/images/delivery-tracker-main-0b8fa992cb37cb21403ac452dbc8915c.png) ## Testing your app {#testing-your-app} Test your app one last time, and you’re done! Congratulations! You’ve built an app using [Bolt for Python](/tools/bolt-python/) that allows you to send information into Slack, as well as into a third-party service. While there are more features you can add to make this a more robust app, we hope that this serves as a good introduction into connecting services like Salesforce using Slack as a conduit. --- Source: https://docs.slack.dev/tools/python-slack-sdk # Python Slack SDK The Slack Python SDK has corresponding packages for Slack APIs. They are small and powerful when used independently, and work seamlessly when used together, too. The Slack platform offers several APIs to build apps. Each Slack API delivers part of the capabilities from the platform, so that you can pick just those that fit your needs. ## Features {#features} Feature Use Package [Web API](/tools/python-slack-sdk/web) Send data to or query data from Slack using any of over 200 methods. `slack_sdk.web`, `slack_sdk.web.async_client` [Webhooks](/tools/python-slack-sdk/webhook) / `response_url` Send a message using Incoming Webhooks or `response_url` `slack_sdk.webhook`, `slack_sdk.webhook.async_client` [Socket Mode](/tools/python-slack-sdk/socket-mode) Receive and send messages over Socket Mode connections. `slack_sdk.socket_mode` [OAuth](/tools/python-slack-sdk/oauth) Setup the authentication flow using V2 OAuth, OpenID Connect for Slack apps. `slack_sdk.oauth` [Audit Logs API](/tools/python-slack-sdk/audit-logs) Receive audit logs API data. `slack_sdk.audit_logs` [SCIM API](/tools/python-slack-sdk/scim) Utilize the SCIM APIs for provisioning and managing user accounts and groups. `slack_sdk.scim` [RTM API](/tools/python-slack-sdk/rtm) Listen for incoming messages and a limited set of events happening in Slack, using WebSocket. `slack_sdk.rtm_v2` Request Signature Verification Verify incoming requests from the Slack API servers. `slack_sdk.signature` UI Builders Construct UI components using easy-to-use builders. `slack_sdk.models` You can also view the [Python module documents](https://docs.slack.dev/tools/python-slack-sdk/reference)! ## Getting help {#getting-help} These docs have lots of information on the Python Slack SDK. There's also an in-depth Reference section. Please explore! If you get stuck, we're here to help. The following are the best ways to get assistance working through your issue: * [Issue Tracker](http://github.com/slackapi/python-slack-sdk/issues) for questions, bug reports, feature requests, and general discussion related to the Python Slack SDK. Try searching for an existing issue before creating a new one. * [Email](mailto:support@slack.com) our developer support team: `support@slack.com`. ## Release notes {#release-notes} Check out the [Python Slack SDK release notes](https://github.com/slackapi/python-slack-sdk/releases) for all the latest happenings. ## Contributing {#contributing} These docs live within the [Python Slack SDK](https://github.com/slackapi/python-slack-sdk) repository and are open source. We welcome contributions from everyone! Please check out our [Contributor's Guide](https://github.com/slackapi/python-slack-sdk/blob/main/.github/contributing.md) for how to contribute in a helpful and collaborative way. --- Source: https://docs.slack.dev/tools/python-slack-sdk/audit-logs # Audit Logs API client The [Audit Logs API](/admins/audit-logs-api) is a set of APIs that you can use to monitor what's happening in your [Enterprise Grid](/enterprise) organization. The Audit Logs API can be used by Security Information and Event Management (SIEM) tools to provide an analysis of how your Slack organization is being accessed. You can also use this API to write your own apps to see how members of your organization are using Slack. You'll need a valid token in order to use the Audit Logs API. In addition, the Slack app using the Audit Logs API needs to be installed in the Enterprise Grid organization, not an individual workspace within the organization. * * * ## AuditLogsClient {#auditlogsclient} An OAuth token with [the admin scope](/reference/scopes/admin) is required to access this API. You'll likely use the `/logs` endpoint as it's the essential part of this API. To learn about the available parameters for this endpoint, check out [using the Audit Logs API](/admins/audit-logs-api). You can also learn more about the data structure of `api_response.typed_body` from [the class source code](https://github.com/slackapi/python-slack-sdk/blob/main/slack_sdk/audit_logs/v1/logs.py). ``` import osfrom slack_sdk.audit_logs import AuditLogsClientclient = AuditLogsClient(token=os.environ["SLACK_ORG_ADMIN_USER_TOKEN"])api_response = client.logs(action="user_login", limit=1)api_response.typed_body # slack_sdk.audit_logs.v1.LogsResponse ``` If you would like to access `/schemes` or `/actions`, you can use the following methods: ``` api_response = client.schemas()api_response = client.actions() ``` ## AsyncAuditLogsClient {#asyncauditlogsclient} If you are keen to use asyncio for Audit Logs API calls, we offer AsyncAuditLogsClient for it. This client relies on the aiohttp library. ``` from slack_sdk.audit_logs.async_client import AsyncAuditLogsClientclient = AsyncAuditLogsClient(token=os.environ["SLACK_ORG_ADMIN_USER_TOKEN"])api_response = await client.logs(action="user_login", limit=1)api_response.typed_body # slack_sdk.audit_logs.v1.LogsResponse ``` * * * ## RetryHandler {#retryhandler} With the default settings, only `ConnectionErrorRetryHandler` with its default configuration (=only one retry in the manner of [exponential backoff and jitter](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/)) is enabled. The retry handler retries if an API client encounters a connectivity-related failure (e.g., connection reset by peer). To use other retry handlers, you can pass a list of `RetryHandler` to the client constructor. For instance, you can add the built-in `RateLimitErrorRetryHandler` this way: ``` import osfrom slack_sdk.audit_logs import AuditLogsClientclient = AuditLogsClient(token=os.environ["SLACK_ORG_ADMIN_USER_TOKEN"])# This handler does retries when HTTP status 429 is returnedfrom slack_sdk.http_retry.builtin_handlers import RateLimitErrorRetryHandlerrate_limit_handler = RateLimitErrorRetryHandler(max_retry_count=1)# Enable rate limited error retries as wellclient.retry_handlers.append(rate_limit_handler) ``` You can also create one on your own by defining a new class that inherits `slack_sdk.http_retry.RetryHandler` (`AsyncRetryHandler` for asyncio apps) and implements required methods (internals of `can_retry` / `prepare_for_next_attempt`). Check out the source code for the ones that are built in to learn how to properly implement them. ``` import socketfrom typing import Optionalfrom slack_sdk.http_retry import (RetryHandler, RetryState, HttpRequest, HttpResponse)from slack_sdk.http_retry.builtin_interval_calculators import BackoffRetryIntervalCalculatorfrom slack_sdk.http_retry.jitter import RandomJitterclass MyRetryHandler(RetryHandler): def _can_retry( self, *, state: RetryState, request: HttpRequest, response: Optional[HttpResponse] = None, error: Optional[Exception] = None ) -> bool: # [Errno 104] Connection reset by peer return error is not None and isinstance(error, socket.error) and error.errno == 104client = AuditLogsClient( token=os.environ["SLACK_ORG_ADMIN_USER_TOKEN"], retry_handlers=[MyRetryHandler( max_retry_count=1, interval_calculator=BackoffRetryIntervalCalculator( backoff_factor=0.5, jitter=RandomJitter(), ), )],) ``` For asyncio apps, `Async` prefixed corresponding modules are available. All the methods in those modules are async/await compatible. Check [the source code](https://github.com/slackapi/python-slack-sdk/blob/main/slack_sdk/http_retry/async_handler.py) for more details. --- Source: https://docs.slack.dev/tools/python-slack-sdk/installation # Installation This package supports Python 3.7 and higher. We recommend using [PyPI](https://pypi.python.org/pypi) for installation. Run the following command: ``` pip install slack-sdk ``` Alternatively, you can always pull the source code directly into your project: ``` git clone https://github.com/slackapi/python-slack-sdk.gitcd python-slack-sdkpython3 -m venv .venvsource .venv/bin/activatepip install -U pippip install -e . # install the SDK project into the virtual env ``` Create a `./test.py` file with the following: test.py ``` # test.pyimport sys# Enable debug loggingimport logginglogging.basicConfig(level=logging.DEBUG)# Verify it worksfrom slack_sdk import WebClientclient = WebClient()api_response = client.api_test() ``` Then, run the script: ``` python test.py ``` It's also good to try on the Python REPL. ## Access tokens {#handling-tokens} Making calls to the Slack API often requires a [token](/authentication/tokens) with associated scopes that grant access to resources. Collecting a token can be done from app settings or with an OAuth installation depending on your app's requirements. **Always keep your access tokens safe.** The OAuth token you use to call the Slack Web API has access to the data on the workspace where it is installed. Depending on the scopes granted to the token, it potentially has the ability to read and write data. Treat these tokens just as you would a password — don't publish them, don't check them into source code, and don't share them with others. Never do the following ``` # don't do this!token = 'xoxb-111-222-xxxxx' ``` We recommend you pass tokens in as environment variables, or store them in a database that is accessed at runtime. You can add a token to the environment by starting your app as follows: ``` SLACK_BOT_TOKEN="xoxb-111-222-xxxxx" python myapp.py ``` Then, retrieve the key as follows: ``` import osSLACK_BOT_TOKEN = os.environ["SLACK_BOT_TOKEN"] ``` Refer to our [best practices for security](/concepts/security) page for more information. ## Installing on a single workspace {#single-workspace} If you're building an application for a single Slack workspace, there's no need to build out the entire OAuth flow. Once you've set up your features, click the **Install App to Team** button on the **Install App** page. If you add new permission scopes or Slack app features after an app has been installed, you must reinstall the app to your workspace for the changes to take effect. Refer to the [Slack quickstart](/quickstart) guide for more details. ## Installing on multiple workspaces {#multi-workspace} If you intend for an app to be installed on multiple Slack workspaces, you will need to handle this installation via the industry-standard OAuth protocol. Read more about [installing with OAuth](/authentication/installing-with-oauth). The OAuth exchange is facilitated via HTTP and requires a webserver; in this example, we'll use [Flask](https://flask.palletsprojects.com/). To configure your app for OAuth, you'll need a client ID, a client secret, and a set of one or more scopes that will be applied to the token once it is granted. The client ID and client secret are available from the [app page](https://api.slack.com/apps). The scopes are determined by the functionality of the app — every method you wish to access has a corresponding scope, and your app will need to request that scope in order to be able to access the method. Review the full list of [OAuth scopes](/reference/scopes). ``` import osfrom slack_sdk import WebClientfrom flask import Flask, requestclient_id = os.environ["SLACK_CLIENT_ID"]client_secret = os.environ["SLACK_CLIENT_SECRET"]oauth_scope = os.environ["SLACK_SCOPES"]app = Flask(__name__) ``` ### The OAuth initiation link {#oauth-link} To begin the OAuth flow that will install your app on a workspace, you'll need to provide the user with a link to the Slack OAuth page. This can be a simple link to `https://slack.com/oauth/v2/authorize` with the `scope` and `client_id` query parameters. This link directs the user to the OAuth acceptance page, where the user will review and accept or decline the permissions your app is requesting as defined by the scope(s). ``` @app.route("/slack/install", methods=["GET"])def pre_install(): state = "randomly-generated-one-time-value" return '' \ 'Add to Slack' ``` ### The OAuth completion page {#oauth-completion} Once the user has agreed to the permissions you've requested, Slack will redirect the user to your auth completion page, which includes a `code` query string parameter. You'll use the `code` parameter to call the [`oauth.v2.access`](/reference/methods/oauth.v2.access) API method that will grant you the token. ``` @app.route("/slack/oauth_redirect", methods=["GET"])def post_install(): # Verify the "state" parameter # Retrieve the auth code from the request params code_param = request.args['code'] # An empty string is a valid token for this request client = WebClient() # Request the auth tokens from Slack response = client.oauth_v2_access( client_id=client_id, client_secret=client_secret, code=code_param ) ``` A successful request to the `oauth.v2.access` API method will yield a JSON payload with at least one token: a bot token that begins with `xoxb`. ``` @app.route("/slack/oauth_redirect", methods=["GET"])def post_install(): # Verify the "state" parameter # Retrieve the auth code from the request params code_param = request.args['code'] # An empty string is a valid token for this request client = WebClient() # Request the auth tokens from Slack response = client.oauth_v2_access( client_id=client_id, client_secret=client_secret, code=code_param ) print(response) # Save the bot token to an environmental variable or to your data store # for later use os.environ["SLACK_BOT_TOKEN"] = response['access_token'] # Don't forget to let the user know that OAuth has succeeded! return "Installation is completed!"if __name__ == "__main__": app.run("localhost", 3000) ``` Once your user has completed the OAuth flow, you'll be able to use the provided tokens to call any of the Slack Web API methods that require an access token. Refer to the [basic usage](/tools/python-slack-sdk/legacy/basic_usage) page for more examples. ## Installation troubleshooting {#troubleshooting} We recommend using [virtualenv (venv)](https://docs.python.org/3/tutorial/venv.html) to set up your Python runtime. ``` # Create a dedicated virtual env for running your Python scriptspython -m venv .venv# Run .venv\Scripts\activate on Windows OSsource .venv/bin/activate# Install slack_sdk PyPI packagepip install "slack_sdk>=3.0"# Set your token as an env variable (`set` command for Windows OS)export SLACK_BOT_TOKEN=xoxb-*** ``` Then, verify the following code works on the Python REPL: ``` import osimport loggingfrom slack_sdk import WebClientlogging.basicConfig(level=logging.DEBUG)client = WebClient(token=os.environ["SLACK_BOT_TOKEN"])res = client.api_test() ``` As the `slack` package is deprecated, we recommend switching to `slack_sdk` package. That being said, the code you're working on may be still using the old package. If you encounter an error saying `AttributeError: module 'slack' has no attribute 'WebClient'`, run `pip list`. If you find both `slack_sdk` and `slack` in the output, try removing `slack` by `pip uninstall slack` and reinstalling `slack_sdk`. --- Source: https://docs.slack.dev/tools/python-slack-sdk/legacy # Overview The [`slackclient`](https://pypi.org/project/slackclient/) PyPI project is in maintenance mode and the [slack-sdk](https://pypi.org/project/slack-sdk/) project is its successor. The v3 SDK provides additional features such as Socket Mode, OAuth flow, SCIM API, Audit Logs API, better async support, retry handlers, and more. Refer to the [migration guide](/tools/python-slack-sdk/v3-migration) to learn how to smoothly migrate your existing code. Slack APIs allow anyone to build full featured integrations that extend and expand the capabilities of your Slack workspace. These APIs allow you to build applications that interact with Slack just like the people on your team. They can post messages, respond to events that happen, and build complex UIs for getting work done. To make it easier for Python programmers to build Slack applications, we've provided this open source SDK that will help you get started building Python apps as quickly as possible. The current version is built for Python 3.7 and higher. ## Slack platform basics {#platform-basics} If you're new to the Slack platform, we have a general purpose [quickstart guide](/quickstart) that isn't specific to any language or framework. Its a great place to learn all about the concepts that go into building a great Slack app. Before you get started building on the Slack platform, you need to set up [your app's configuration](https://api.slack.com/apps/new). This is where you define things like your apps permissions and the endpoints that Slack should use for interacting with the backend you'll build using Python. The app configuration page is also where you'll acquire the OAuth token you'll use to call Slack API methods. Treat this token with care, just like you would a password, because it has access to your workspace and can potentially read and write data to and from it. ## Installation {#installation} We recommend using [PyPI](https://pypi.python.org/pypi) to install as follows: ``` pip install slackclient ``` Of course, you can always pull the source code directly into your project like this: ``` git clone https://github.com/slackapi/python-slackclient.git ``` And then, save a few lines of code as `./test.py` like so: ``` # test.pyimport sys# Load the local source directlysys.path.insert(1, "./python-slackclient")# Enable debug loggingimport logginglogging.basicConfig(level=logging.DEBUG)# Verify it worksfrom slack import WebClientclient = WebClient()api_response = client.api_test() ``` Run the code as follows: ``` python test.py ``` --- Source: https://docs.slack.dev/tools/python-slack-sdk/legacy/auth # Tokens & installation The [`slackclient`](https://pypi.org/project/slackclient/) PyPI project is in maintenance mode and the [slack-sdk](https://pypi.org/project/slack-sdk/) project is its successor. The v3 SDK provides additional features such as Socket Mode, OAuth flow, SCIM API, Audit Logs API, better asyncio support, retry handlers, and more. ## Access tokens {#handling-tokens} **Always keep your access tokens safe.** The OAuth token you use to call the Slack Web API has access to the data on the workspace where it is installed. Depending on the scopes granted to the token, it potentially has the ability to read and write data. Treat these tokens just as you would a password — don't publish them, don't check them into source code, and don't share them with others. Never do the following: ``` token = 'xoxb-111-222-xxxxx' ``` We recommend you pass tokens in as environment variables, or store them in a database that is accessed at runtime. You can add a token to the environment by starting your app as follows: ``` SLACK_BOT_TOKEN="xoxb-111-222-xxxxx" python myapp.py ``` Then, retrieve the key as follows: ``` import osSLACK_BOT_TOKEN = os.environ["SLACK_BOT_TOKEN"] ``` Refer to our [best practices for security](/concepts/security) page for more information. ## Installing on a single workspace {#single-workspace} If you're building an application for a single Slack workspace, there's no need to build out the entire OAuth flow. Once you've set up your features, click the **Install App to Team** button on the **Install App** page. If you add new permission scopes or Slack app features after an app has been installed, you must reinstall the app to your workspace for the changes to take effect. Refer to the [quickstart](/quickstart) guide for more details. ## Installing on multiple workspaces {#multi-workspace} If you intend for an app to be installed on multiple Slack workspaces, you will need to handle this installation via the industry-standard OAuth protocol. Read more about [installing with OAuth](/authentication/installing-with-oauth). The OAuth exchange is facilitated via HTTP and requires a webserver; in this example, we'll use [Flask](https://flask.palletsprojects.com/). To configure your app for OAuth, you'll need a client ID, a client secret, and a set of one or more scopes that will be applied to the token once it is granted. The client ID and client secret are available from the [app page](https://api.slack.com/apps). The scopes are determined by the functionality of the app — every method you wish to access has a corresponding scope, and your app will need to request that scope in order to be able to access the method. Review the full list of [OAuth scopes](/reference/scopes). ``` import osfrom slack import WebClientfrom flask import Flask, requestclient_id = os.environ["SLACK_CLIENT_ID"]client_secret = os.environ["SLACK_CLIENT_SECRET"]oauth_scope = os.environ["SLACK_SCOPES"]app = Flask(__name__) ``` ### The OAuth initiation link {#oauth-link} To begin the OAuth flow that will install your app on a workspace, you'll need to provide the user with a link to the Slack OAuth page. This can be a simple link to `https://slack.com/oauth/v2/authorize` with the `scope` and `client_id` query parameters. This link directs the user to the OAuth acceptance page, where the user will review and accept or decline the permissions your app is requesting as defined by the scope(s). ``` @app.route("/slack/install", methods=["GET"])def pre_install(): state = "randomly-generated-one-time-value" return '' \ 'Add to Slack' ``` ### The OAuth completion page {#oauth-completion} Once the user has agreed to the permissions you've requested, Slack will redirect the user to your auth completion page, which includes a `code` query string parameter. You'll use the `code` parameter to call the [`oauth.v2.access`](/reference/methods/oauth.v2.access) API method that will grant you the token. ``` @app.route("/slack/oauth_redirect", methods=["GET"])def post_install(): # Verify the "state" parameter # Retrieve the auth code from the request params code_param = request.args['code'] # An empty string is a valid token for this request client = WebClient() # Request the auth tokens from Slack response = client.oauth_v2_access( client_id=client_id, client_secret=client_secret, code=code_param ) ``` A successful request to the `oauth.v2.access` API method will yield a JSON payload with at least one token: a bot token that begins with `xoxb`. ``` @app.route("/slack/oauth_redirect", methods=["GET"])def post_install(): # Verify the "state" parameter # Retrieve the auth code from the request params code_param = request.args['code'] # An empty string is a valid token for this request client = WebClient() # Request the auth tokens from Slack response = client.oauth_v2_access( client_id=client_id, client_secret=client_secret, code=code_param ) print(response) # Save the bot token to an environmental variable or to your data store # for later use os.environ["SLACK_BOT_TOKEN"] = response['access_token'] # Don't forget to let the user know that OAuth has succeeded! return "Installation is completed!"if __name__ == "__main__": app.run("localhost", 3000) ``` Once your user has completed the OAuth flow, you'll be able to use the provided tokens to call any of the Slack Web API methods that require an access token. Refer to the [basic usage](/tools/python-slack-sdk/legacy/basic_usage) page for more examples. --- Source: https://docs.slack.dev/tools/python-slack-sdk/legacy/basic_usage # Basic usage The [`slackclient`](https://pypi.org/project/slackclient/) PyPI project is in maintenance mode and the [slack-sdk](https://pypi.org/project/slack-sdk/) project is its successor. The v3 SDK provides additional features such as Socket Mode, OAuth flow, SCIM API, Audit Logs API, better async support, retry handlers, and more. The Slack Web API allows you to build applications that interact with Slack in more complex ways than the integrations we provide out of the box. Accessing Slack API methods requires an OAuth token — read more about [installing with OAuth](/authentication/installing-with-oauth). Each of these [API methods](/reference/methods) is fully documented on our developer site at [docs.slack.dev](/). ## Sending a message {#sending-messages} One of the primary uses of Slack is posting messages to a channel using the channel ID, or as a DM to another person using their user ID. This method will handle either a channel ID or a user ID passed to the `channel` parameter. ``` import logginglogging.basicConfig(level=logging.DEBUG)import osfrom slack import WebClientfrom slack.errors import SlackApiErrorslack_token = os.environ["SLACK_API_TOKEN"]client = WebClient(token=slack_token)try: response = client.chat_postMessage( channel="C0XXXXXX", text="Hello from your app! :tada:" )except SlackApiError as e: # You will get a SlackApiError if "ok" is False assert e.response["error"] # str like 'invalid_auth', 'channel_not_found' ``` Sending an ephemeral message, which is only visible to an assigned user in a specified channel, is nearly the same as sending a regular message but with an additional `user` parameter. ``` import osfrom slack import WebClientslack_token = os.environ["SLACK_API_TOKEN"]client = WebClient(token=slack_token)response = client.chat_postEphemeral( channel="C0XXXXXX", text="Hello silently from your app! :tada:", user="U0XXXXXXX") ``` See the [`chat.postEphemeral`](/reference/methods/chat.postEphemeral) API method for more details. ## Formatting messages with Block Kit {#block-kit} Messages posted from apps can contain more than just text; they can also include full user interfaces composed of blocks using [Block Kit](/block-kit). The [`chat.postMessage method`](/reference/methods/chat.postMessage) takes an optional blocks argument that allows you to customize the layout of a message. Blocks are specified in a single object literal, so just add additional keys for any optional argument. To send a message to a channel, use the channel's ID. For DMs, use the user's ID. ``` client.chat_postMessage( channel="C0XXXXXX", blocks=[ { "type": "section", "text": { "type": "mrkdwn", "text": "Danny Torrence left the following review for your property:" } }, { "type": "section", "text": { "type": "mrkdwn", "text": " \n :star: \n Doors had too many axe holes, guest in room " + "237 was far too rowdy, whole place felt stuck in the 1920s." }, "accessory": { "type": "image", "image_url": "https://images.pexels.com/photos/750319/pexels-photo-750319.jpeg", "alt_text": "Haunted hotel image" } }, { "type": "section", "fields": [ { "type": "mrkdwn", "text": "*Average Rating*\n1.0" } ] } ]) ``` You can use [Block Kit Builder](https://app.slack.com/block-kit-builder/) to prototype your message's look and feel. ## Threading messages {#threading-messages} Threaded messages are a way of grouping messages together to provide greater context. You can reply to a thread or start a new threaded conversation by simply passing the original message's `ts` ID in the `thread_ts` attribute when posting a message. If you're replying to a threaded message, you'll pass the `thread_ts` ID of the message you're replying to. A channel or DM conversation is a nearly linear timeline of messages exchanged between people, bots, and apps. When one of these messages is replied to, it becomes the parent of a thread. By default, threaded replies do not appear directly in the channel, but are instead relegated to a kind of forked timeline descending from the parent message. ``` response = client.chat_postMessage( channel="C0XXXXXX", thread_ts="1476746830.000003", text="Hello from your app! :tada:") ``` By default, the `reply_broadcast` parameter is set to `False`. To indicate your reply is germane to all members of a channel and therefore a notification of the reply should be posted in-channel, set the `reply_broadcast` parameter to `True`. ``` response = client.chat_postMessage( channel="C0XXXXXX", thread_ts="1476746830.000003", text="Hello from your app! :tada:", reply_broadcast=True) ``` While threaded messages may contain attachments and message buttons, when your reply is broadcast to the channel, it'll actually be a reference to your reply and not the reply itself. When appearing in the channel, it won't contain any attachments or message buttons. Updates and deletion of threaded replies works the same as regular messages. Refer to the [threading messages](/messaging#threading) page for more information. ## Updating a message {#updating-messages} Let's say you have a bot that posts the status of a request. When that request changes, you'll want to update the message to reflect it's state. ``` response = client.chat_update( channel="C0XXXXXX", ts="1476746830.000003", text="updates from your app! :tada:") ``` See the [`chat.update`](/reference/methods/chat.update) API method for formatting options and some special considerations when calling this with a bot user. ## Deleting a message {#deleting-messages} Sometimes you need to delete things. ``` response = client.chat_delete( channel="C0XXXXXX", ts="1476745373.000002") ``` See the [`chat.delete`](/reference/methods/chat.delete) API method for more details. ## Opening a modal {#opening-modals} Modals allow you to collect data from users and display dynamic information in a focused surface. Modals use the same blocks that compose messages, with the addition of an `input` block. ``` # This module is available since v2.6from slack.signature import SignatureVerifiersignature_verifier = SignatureVerifier(os.environ["SLACK_SIGNING_SECRET"])from flask import Flask, request, make_responseapp = Flask(__name__)@app.route("/slack/events", methods=["POST"])def slack_app(): if not signature_verifier.is_valid_request(request.get_data(), request.headers): return make_response("invalid request", 403) if "payload" in request.form: payload = json.loads(request.form["payload"]) if payload["type"] == "shortcut" \ and payload["callback_id"] == "open-modal-shortcut": # Open a new modal by a global shortcut try: api_response = client.views_open( trigger_id=payload["trigger_id"], view={ "type": "modal", "callback_id": "modal-id", "title": { "type": "plain_text", "text": "Awesome Modal" }, "submit": { "type": "plain_text", "text": "Submit" }, "close": { "type": "plain_text", "text": "Cancel" }, "blocks": [ { "type": "input", "block_id": "b-id", "label": { "type": "plain_text", "text": "Input label", }, "element": { "action_id": "a-id", "type": "plain_text_input", } } ] } ) return make_response("", 200) except SlackApiError as e: code = e.response["error"] return make_response(f"Failed to open a modal due to {code}", 200) if payload["type"] == "view_submission" \ and payload["view"]["callback_id"] == "modal-id": # Handle a data submission request from the modal submitted_data = payload["view"]["state"]["values"] print(submitted_data) # {'b-id': {'a-id': {'type': 'plain_text_input', 'value': 'your input'}}} return make_response("", 200) return make_response("", 404)if __name__ == "__main__": # export SLACK_SIGNING_SECRET=*** # export SLACK_API_TOKEN=xoxb-*** # export FLASK_ENV=development # python3 app.py app.run("localhost", 3000) ``` See the [`views.open`](/reference/methods/views.open) API method for more details and additional parameters. To run the above example, the following [app configurations](https://api.slack.com/apps) are required: * Enable **Interactivity** with a valid Request URL: `https://{your-public-domain}/slack/events` * Add a global shortcut with the callback ID: `open-modal-shortcut` ## Updating and pushing modals {#updating-pushing-modals} You can dynamically update a view inside of a modal by calling the `views.update` API method and passing the view ID returned in the previous `views.open` API method call. ``` private_metadata = "any str data you want to store"response = client.views_update( view_id=payload["view"]["id"], hash=payload["view"]["hash"], view={ "type": "modal", "callback_id": "modal-id", "private_metadata": private_metadata, "title": { "type": "plain_text", "text": "Awesome Modal" }, "submit": { "type": "plain_text", "text": "Submit" }, "close": { "type": "plain_text", "text": "Cancel" }, "blocks": [ { "type": "input", "block_id": "b-id", "label": { "type": "plain_text", "text": "Input label", }, "element": { "action_id": "a-id", "type": "plain_text_input", } } ] }) ``` See the [`views.update`](/reference/methods/views.update) API method for more details. If you want to push a new view onto the modal instead of updating an existing view, see the [`views.push`](/reference/methods/views.push) API method. ## Emoji reactions {#emoji} You can quickly respond to any message on Slack with an emoji reaction. Reactions can be used for any purpose: voting, checking off to-do items, showing excitement, or just for fun. This method adds a reaction (emoji) to an item (`file`, `file comment`, `channel message`, `group message`, or `direct message`). One of `file`, `file_comment`, or the combination of `channel` and `timestamp` must be specified. ``` response = client.reactions_add( channel="C0XXXXXXX", name="thumbsup", timestamp="1234567890.123456") ``` Removing an emoji reaction is basically the same format, but you'll use the `reactions.remove` API method instead of the `reactions.add` API method. ``` response = client.reactions_remove( channel="C0XXXXXXX", name="thumbsup", timestamp="1234567890.123456") ``` See the [`reactions.add`](/reference/methods/reactions.add) and [`reactions.remove`](/reference/methods/reactions.remove) API methods for more details. ## Listing public channels {#listing-public-channels} At some point, you'll want to find out what channels are available to your app. This is how you get that list. ``` response = client.conversations_list(types="public_channel") ``` Archived channels are included by default. You can exclude them by passing `exclude_archived=1` to your request. ``` response = client.conversations_list(exclude_archived=1) ``` See the [`conversations.list`](/reference/methods/conversations.list) API method for more details. ## Getting a channel's info {#get-channel-info} Once you have the ID for a specific channel, you can fetch information about that channel. ``` response = client.conversations_info(channel="C0XXXXXXX") ``` See the [`conversations.info`](/reference/methods/conversations.info) API method for more details. ## Joining a channel {#join-channel} Channels are the social hub of most Slack teams. Here's how you hop into one: ``` response = client.conversations_join(channel="C0XXXXXXY") ``` If you are already in the channel, the response is slightly different. The `already_in_channel` attribute will be true, and a limited `channel` object will be returned. Bot users cannot join a channel on their own, they need to be invited by another user. See the [`conversations.join`](/reference/methods/conversations.join) API method for more details. * * * ## Leaving a channel {#leave-channel} Maybe you've finished up all the business you had in a channel, or maybe you joined one by accident. This is how you leave a channel. ``` response = client.conversations_leave(channel="C0XXXXXXX") ``` See the [`conversations.leave`](/reference/methods/conversations.leave) API method for more details. ## Listing team members {#list-team-members} ``` response = client.users_list()users = response["members"]user_ids = list(map(lambda u: u["id"], users)) ``` See the [`users.list`](/reference/methods/users.list) API method for more details. ## Uploading files {#uploading-files} ``` response = client.files_upload_v2( channel="C3UKJTQAC", file="./files.pdf", title="Test upload") ``` See the [`files.upload`](/reference/methods/files.upload) API method for more details. ## Calling API methods {#calling-API-methods} This library covers all the public endpoints as the methods in `WebClient`. That said, you may see a bit of a delay with the library release. When you're in a hurry, you can directly use the `api_call` method as below. ``` import osfrom slack import WebClientclient = WebClient(token=os.environ['SLACK_API_TOKEN'])response = client.api_call( api_method='chat.postMessage', json={'channel': '#random','text': "Hello world!"})assert response["message"]["text"] == "Hello world!" ``` ## Rate limits {#rate-limits} When posting messages to a channel, Slack allows apps to send no more than one message per channel per second. We allow bursts over that limit for short periods; however, if your app continues to exceed the limit over a longer period of time, it will be rate limited. Different API methods have other limits — be sure to check the [rate limits](/apis/web-api/rate-limits) and test that your app has a graceful fallback if it should hit those limits. If you go over these limits, Slack will begin returning _HTTP 429 Too Many Requests_ errors, a JSON object containing the number of calls you have been making, and a _Retry-After_ header containing the number of seconds until you can retry. Here's an example of how you might handle rate limited requests: ``` import osimport timefrom slack import WebClientfrom slack.errors import SlackApiErrorclient = WebClient(token=os.environ["SLACK_API_TOKEN"])# Simple wrapper for sending a Slack messagedef send_slack_message(channel, message): return client.chat_postMessage( channel=channel, text=message )# Make the API call and save results to `response`channel = "#random"message = "Hello, from Python!"# Do until being rate limitedwhile True: try: response = send_slack_message(channel, message) except SlackApiError as e: if e.response.status_code == 429: # The `Retry-After` header will tell you how long to wait before retrying delay = int(e.response.headers['Retry-After']) print(f"Rate limited. Retrying in {delay} seconds") time.sleep(delay) response = send_slack_message(channel, message) else: # other errors raise e ``` Refer to the [rate limits](/apis/web-api/rate-limits) page for more information. --- Source: https://docs.slack.dev/tools/python-slack-sdk/legacy/changelog # Changelog ## v3.0.0 (2020-11-09) {#v300-2020-11-09} This is the first stable version of [slack\_sdk](https://pypi.org/project/slack-sdk/) v3. The remarkable updates in this major version are: * Newly added OAuth flow support * Better async/sync separation for `WebClient` and `WebhookClient` * Renamed packages (from `slack` to `slack_sdk`) with deprecation warnings Refer to [v3.0.0 milestone](https://github.com/slackapi/python-slack-sdk/milestone/10?closed=1) and [the docs website](/tools/python-slack-sdk/) for details. If you're a `slackclient` user, the migration guide for `slackclient` v2.x users is available at [http://localhost:3000/python-slack-sdk/v3-migration](http://localhost:3000/python-slack-sdk/v3-migration). ## v2.9.3 (2020-10-20) {#v293-2020-10-20} Refer to [v2.9.3 milestone](https://github.com/slackapi/python-slackclient/milestone/20?closed=1) to know the complete list of the issues resolved by this release. **Updates** * \[Block Kit\] #851 #852 Set default\_type for HeaderBlock text - Thanks @fwump38 * \[Block Kit\] #853 #854 Enable to use input blocks in Home tab views - Thanks @fwump38 * \[RTMClient\] #857 #846 RTMClient does not pass timeout value to WebClient - Thanks @Luden @seratch ## v2.9.2 (2020-10-09) {#v292-2020-10-09} Refer to [v2.9.2 milestone](https://github.com/slackapi/python-slackclient/milestone/19?closed=1) to know the complete list of the issues resolved by this release. **Updates** * \[Block Kit\] #841 Dispatch Action in Input blocks - Thanks @seratch * \[WebClient\] #838 Add apps.event.authorizations.list and other APIs - Thanks @seratch * \[WebClient\]\[WebhookClient\] #829 Improve error body parser to handle no charset responses - Thanks @adamchainz @seratch * \[Block Kit\] #824 Correct text field validation in Header blocks - Thanks @seratch ## v2.9.1 (2020-09-23) {#v291-2020-09-23} Refer to [v2.9.1 milestone](https://github.com/slackapi/python-slackclient/milestone/18?closed=1) to know the complete list of the issues resolved by this release. **Updates** * \[WebClient\]\[WebhookClient\] #820 #821 #822 The proxy option in WebClient/WebhookClient no longer works - Thanks @seratch ## v2.9.0 (2020-09-17) {#v290-2020-09-17} Refer to [v2.9.0 milestone](https://github.com/slackapi/python-slackclient/milestone/17?closed=1) to know the complete list of the issues resolved by this release. **Updates** * \[WebClient\] #811 Add workflows.\* API support - Thanks @misscoded * \[WebClient\] #810 #809 Only set default filename in files\_upload if file is an instance of str - Thanks @csaska ## v2.8.2 (2020-09-04) {#v282-2020-09-04} Refer to [v2.8.2 milestone](https://github.com/slackapi/python-slackclient/milestone/16?closed=1) to know the complete list of the issues resolved by this release. **Updates** * \[WebClient\] #795 #794 Add admin.conversations.\* API methods in WebClient/AsyncWebClient - Thanks @ruberVulpes * \[WebClient\] #796 Fix a link to the Static options documentation - Thanks @Jamim ## v2.8.1 (2020-08-28) {#v281-2020-08-28} Refer to [v2.8.1 milestone](https://github.com/slackapi/python-slackclient/milestone/15?closed=1) to know the complete list of the issues resolved by this release. **Updates** * \[WebClient\] #778 #779 Adding support for View objects for views.push/update/publish - Thanks @ruberVulpes * \[WebClient\] #786 Fix admin.conversations.restrictAccess.\* methods to match documentation - Thanks @ruberVulpes ## v2.8.0 (2020-08-06) {#v280-2020-08-06} Refer to [v2.8.0 milestone](https://github.com/slackapi/python-slackclient/milestone/14?closed=1) to know the complete list of the issues resolved by this release. **New Features** * \[WebClient\] #765 #766 Introduce AsyncWebClient/AsyncWebhookClient providing coroutines - Thanks @seratch * \[Block Kit\] #767 #768 Add "header" block support - Thanks @mwbrooks **Updates** * \[WebClient\] #738 Add HTTP\_PROXY, HTTPS\_PROXY env variable support in async WebClient - Thanks @iamtofr @seratch * \[WebClient\] #769 #773 Enable User-Agent to have additional info part - Thanks @seratch * \[WebClient\] #770 #771 Fix a bug where `files.upload`'s file param doesn't accept bytes data - Thanks @seratch ## v2.7.3 (2020-07-20) {#v273-2020-07-20} Refer to [v2.7.3 milestone](https://github.com/slackapi/python-slackclient/milestone/13?closed=1) to know the complete list of the issues resolved by this release. **Updates** * \[WebClient\] #754 Fix #729 Add admin.conversations.restrictAccess.\*, conversations.mark API - Thanks @ruberVulpes @kian2attari * \[WebClient\] #758 Fix #757 Add admin.usergroups.addTeams, calls.participants.remove API - Thanks @seratch * \[WebClient\] #727 Fix #645 Unclosed client session - Thanks @NoAnyLove @jourdanrodrigues * \[WebClient\] #745 Fix #744 a validation logic bug in DatePickerElement - Thanks @dzudi941 * \[WebClient\] #752 Fix #733 Better error handling when getting TimeoutError in RTMClient#start() - Thanks @liorblob @seratch * \[WebClient\] #751 Fix #718 by handling unexpected response body format - Thanks @jeffbuswell @seratch ## v2.7.2 (2020-06-23) {#v272-2020-06-23} Refer to [v2.7.2 milestone](https://github.com/slackapi/python-slackclient/milestone/12?closed=1) to know the complete list of the issues resolved by this release. **Updates** * \[WebClient\] Fix #728 by adding bytearray support in files\_upload (sync mode) - Thanks @sofya-salmanova @seratch * \[WebClient\] #726 Fix InputBlock.hint validation failure - Thanks @jourdanrodrigues * \[WebClient\] #723 Correct the default value of InputBlock.label, hint - Thanks @jourdanrodrigues ## v2.7.1 (2020-06-04) {#v271-2020-06-04} This release includes the fixes for regression bugs in `WebClient` since v2.6.0. Refer to [v2.7.1 milestone](https://github.com/slackapi/python-slackclient/milestone/11?closed=1) to know the complete list of the issues resolved by this release. **Updates** * \[WebClient\] #716 #712 Support timeout in sync sync web clients - Thanks @DanialErfanian @seratch * \[WebClient\] #713 Support custom SSL context in sync sync web clients - Thanks @austinbutler * \[WebClient\] #715 #714 Support proxy in sync sync web clients - Thanks @austinbutler @seratch ## v2.7.0 (2020-06-02) {#v270-2020-06-02} Refer to [v2.7.0 milestone](https://github.com/slackapi/python-slackclient/milestone/6?closed=1) to know the complete list of the issues resolved by this release. **New Features** * \[WebhookClient\] #707 #270 #531 Add `WebhookClient` for Incoming Webhooks & response\_url - Thanks @seratch @chubz @Ambro17 **Updates** * \[WebClient\] #704 #695 Add `calls\_\*` methods to `WebClient` and `CallBlock` in Block Kit classes - Thanks @seratch * \[WebClient\] #710 #536 Allow Tokens to be specified per request - Thanks @seratch * \[WebClient\] #709 #708 Add default\_to\_current\_conversation in conversations\_select elements - Thanks @seratch ## v2.6.2 (2020-05-28) {#v262-2020-05-28} Refer to [v2.6.2 milestone](https://github.com/slackapi/python-slackclient/milestone/9?closed=1) to know the complete details of this release. **Updates** * \[WebClient\] #705 WebClient's paginated API calls may fail with no params - Thanks @seratch ## v2.6.1 (2020-05-24) {#v261-2020-05-24} This patch release is a quick fix for #701, a major issue that affected RTMClient users in v2.6.0. The malfunction was introduced by #667 trying to address #558 #619. Those issues were reopened and will be resolved by another approach. Refer to [v2.6.1 milestone](https://github.com/slackapi/python-slackclient/milestone/8) to know the complete list of the issues resolved by this release. **Updates** * \[RTMClient\] #701 RTMClient drops some messages when they come in rapid succession - Thanks @pbrackin @seratch ## v2.6.0 (2020-05-21) {#v260-2020-05-21} Refer to [v2.6.0 milestone](https://github.com/slackapi/python-slackclient/milestone/5?closed=1) to know the complete list of the issues resolved by this release. **New Features** * \[Block Kit\] #659 Add complete supports for Block Kit components and fixed a few existing bugs as well (#500 #519 #623 #632 #635 #639 #676 #699) - Thanks @seratch @diurnalist @ruberVulpes @jeremyschulman @e271828- @RodneyU215 * \[Signature\] #686 Add slack.signature.SignatureVerifier for request verification - Thanks @seratch * \[WebClient\] #682 Add missing Grid admin APIs (`admin.usergroups.\*`, `admin.users.\*`, `admin.apps.\*`) - Thanks @stevengill @seratch **Updates** * \[WebClient\]\[RTMClient\] Fixed a bunch of the currency issues this SDK had (#429 #463 #492 #497 #530 #569 #605 #613 #626 #630 #631 #633 #669) - Thanks @seratch @aaguilartablada @aoberoi @stevengill @marshallino16 * \[WebClient\] #681 #560 Enable using bool values for request parameters - Thanks @roman-kachanovsky @seratch * \[WebClient\] #661 #678 Improve handling of required "ids" parameters (e.g., channel\_ids, users) - Thanks @seratch * \[WebClient\] #680 Add non-conversation API deprecation warnings - Thanks @seratch * \[WebClient\] #671 #670 Enable passing None values for request parameters (they used to result in errors) - Thanks @yuji38kwmt @seratch * \[WebClient\] #673 Fix #672 files.upload fails with a filepath containing multi byte chars - Thanks @yuji38kwmt @seratch * \[WebClient\] #656 Fix #594 preview\_image for files.remote.add API method is not properly supported - Thanks @Eothred @seratch * \[Maintenance\] #618 Add py.typed file to package distribution - Thanks @JKillian * \[WebClient\] #599 Strip token string parameters of whitespace - Thanks @TheFrozenFire * \[WebClient\] #692 Fix superfluous\_charset warnings since v2.4.0 - Thanks @seratch * \[WebClient\] #652 Update oauth\_v2\_access to include redirect\_uri (as optional) - Thanks @tomasreimers ## v2.5.0 (2019-12-09) {#v250-2019-12-09} **New Features** * \[WebClient\] Adding new oauth.v2.access Web API method. #577 ## v2.4.0 (2019-11-27) {#v240-2019-11-27} **New Features** * \[WebClient\] Adding new admin.\* Web API methods. #571 **Updates** * \[WebClient\] We're no longer validating token types for Web API methods. Improves compatibility with granular bot permissions. #568 (Thanks @Smotko) * \[WebClient\] Correcting typos in descriptions #554 (Thanks @phamk) * \[WebClient\] Fixed 'iteracting' typo in library file headers #564 (Thanks @acabey) * \[Message Builders\] Remove value from LinkButtonElement #563 (Thanks @pedroma) ## v2.3.1 (2019-10-29) {#v231-2019-10-29} **Updates** * \[WebClient\] Fixing a regression that causes the client to close sessions prematurely. #544 (Thanks @fatih-acar!) * \[WebClient\] Adding required missing view param to views.update Web API method. #542 ## v2.3.0 (2019-10-22) {#v230-2019-10-22} **New Features** * \[WebClient\] Adding new views.publish Web API method. #540 **Updates** * \[WebClient\] Some server responses don't return json. Correcting initial assumption. #540 * \[Maintenance\] Add `py.typed` to mark the library to support type hinting #524s ## v2.2.1 (2019-10-08) {#v221-2019-10-08} **Updates** * \[Docs\] Fix Indentation of Code Snippets in README.md #525 (Thanks @abhishekjiitr) * \[WebClient\] Fix Web Client custom iterator #521 (Thanks @smaeda-ks) * \[WebClient\] Oauth previously failed to pass along credentials properly. This is fixed now. #527 * \[WebClient\] When a SlackApiError occurs we're now passing the entire SlackResponse into the exception. #527 ## v2.2.0 (2019-09-25) {#v220-2019-09-25} **New Features** * \[WebClient\] Adding new admin and remote files API methods. #501 * \[WebClient\] Adding new view API methods. #517 **Updates** * \[Message Builders\] Update BlockAttachment to not send invalid JSON due to fields attribute #473 (Thanks @paul-griffith) * \[Docs\] Add RTM section for docs v2 #477 (Thanks @shanedewael) * \[Docs\] Fix typo; recieved -> received #478 (Thanks @joakimnordling) * \[Docs\] Fix block kit link & update docs #484 (Thanks @clavin) * \[RTMClient\] Return callback from `RTMClient.run_on` #490 (Thanks @clavin) * \[Docs\] Fix link to Auth Guide in readme #498 (Thanks @asherf) * \[Docs\] Fix missing word and typo #512 (Thanks @marks) * \[Message Builders\] bugfix for value length in button elements #514 (Thanks @avanderm) * \[Docs\] Fixes formatting #515 (Thanks @vpetersson) * \[Docs\] Improve a code snippet on README #516 (Thanks @seratch) * \[WebClient\] Fixed an OAuth Headers bug and made the `token` param optional. #517 ## v2.1.0 (2019-07-01) {#v210-2019-07-01} **New Features** * Type-hinted helper classes for building messages in v2 #400 (Thanks @paul-griffith) **Breaking Changes** * \[RTMClient\] Converted the `RTMClient#typing()` function to async #446 **Updates** * \[RTMClient\] Handle case in which aiohttp closes the websocket due to lack of ping responses. #453 (Thanks @flyte) * Modify package identifier in user agent to match v1.x identifier #418 (Thanks @aoberoi) * \[WebClient\] Fixed typo in Scheduled message #428 & #435 (Thanks @splinterific) * Transform install\_requires of 'aiodns' into extras\_require. #440 (Thanks @staticdev) **Thank you!** To everyone who has opened, commented or reacted to an issue; this project is better because of you! Thank you for helping the Slack community! ## v2.0.0 (2019-04-29) {#v200-2019-04-29} [Original RFC](https://github.com/slackapi/python-slackclient/issues/384) [v2 PR](https://github.com/slackapi/python-slackclient/pull/394) **New Features** * Client Decomposition: We've split the client into two. * WebClient: A HTTP client focused on Slack's Web API. * RTMClient: A websocket client focused on Slack's RTM API. * RTMClient: Completely redesigned, this client allows you to link your application's callbacks to corresponding Slack events. * WebClient: The WebClient now provides built-in methods for Slack's Web API. These methods act as helpers enabling you to focus less on how the request is constructed. Here are a few things this provides: * Basic information about each method through the docstring. * Easy File Uploads: You can now pass in the location of a file and the library will handle opening and retrieving the file object to be transmitted. * Token type validation: This gives you better error messaging when you're attempting to consume an API method that your token doesn't have access to. * Constructs requests using Slack's preferred HTTP methods and content-types. **Breaking Changes:** If you're migrating from v1.x of slackclient to v2.x, Please follow our [migration guide](https://github.com/slackapi/python-slackclient/wiki/Migrating-to-2.x) to ensure your app continues working after updating. **Thank you!** This release would not have been possible without the support of our community. Thank you to everyone who has contributed to this release. ## v1.3.1 (2019-02-28) {#v131-2019-02-28} * Lock websocket-client version to < 0.55.0: temp fix for #385 ## v1.3.0 (2018-09-11) {#v130-2018-09-11} **New Features** * Adds support for short lived tokens and automatic token refresh #347 (Thanks @roach!) **Other** * Update RTM rate limiting comment and error message #308 (Thanks @benoitlavigne!) * Use logging instead of traceback #309 (Thanks @harlowja!) * Remove Python 3.3 from test environments #346 (Thanks @roach!) * Enforced linting when using VSCode. #347 (Thanks @roach!) ## v1.2.1 (2018-03-26) {#v121-2018-03-26} * Added rate limit handling for rtm connections (thanks @jayalane!) ## v1.2.0 (2018-03-20) {#v120-2018-03-20} * You can now tell the RTM client to automatically reconnect by passing `auto_reconnect=True` ## v1.1.3 (2018-03-01) {#v113-2018-03-01} * Fixed another API param encoding bug. It encodes things properly now. ## v1.1.2 (2018-01-31) {#v112-2018-01-31} * Fixed an encoding issue which was encoding some Web API params incorrectly ## v1.1.1 (2018-01-30) {#v111-2018-01-30} * Adds HTTP response headers to `api_call` responses to expose things like rate limit info * Moves `token` into auth header rather than request params ## v1.1.0 (2017-11-21) {#v110-2017-11-21} * Adds new SlackClientError and ResponseParseError types to describe errors - thanks @aoberoi! * Fix Build Error (#245) - thanks @stasfilin! * Include email as user property (#173) - thanks @acaire! * Add http reply into slack login and slack connection error (#216) - thanks @harlowja! * Removed unused exception class (#233) * Fix rtm\_send\_message bug (#225) - thanks @kt5356! * Allow use of custom parameters on rtm\_connect() (#210) - thanks @kamushadenes! * Fix link to rtm.connect docs (#223) - @sampart! ## v1.0.9 (2017-08-31) {#v109-2017-08-31} * Fixed rtm\_send\_message ID bug introduced in 1.0.8 ## v1.0.8 (2017-08-31) {#v108-2017-08-31} * Added rtm.connect support ## v1.0.7 (2017-08-02) {#v107-2017-08-02} * Fixes an issue where connecting over RTM to large teams may result in "Websocket URL expired" errors * A bunch of packaging improvements ## v1.0.6 (2017-06-12) {#v106-2017-06-12} * Added proxy support (thanks @timfeirg!) * Tidied up docs (thanks @schlueter!) * Added tox settings for Python 3 testing (thanks @cclauss!) ## v1.0.5 (2017-01-23) {#v105-2017-01-23} * Allow RTM Channel.send\_message to reply to a thread * Index users by ID instead of Name (non-breaking change) * Added timeout to api calls * Fixed a typo about token access in auth.rst, thanks @kelvintaywl! * Added Message Threads to the docs ## v1.0.4 (2016-12-15) {#v104-2016-12-15} * Fixed the ability to search for a user by ID ## v1.0.3 (2016-12-13) {#v103-2016-12-13} * Fixed an issue causing RTM connections to fail for large teams ## v1.0.2 (2016-09-22) {#v102-2016-09-22} * Removed unused ping counter * Fixed contributor guidelines links * Updated documentation * Fix bug preventing API calls requiring a file ID * Removes files from api\_calls before JSON encoding, so the request is properly formatted ## v1.0.1 (2016-03-25) {#v101-2016-03-25} * Fix for \_\_eq\_\_ comparison in channels using '#' in channel name * Added copyright info to the LICENSE file ## v1.0.0 (2016-02-28) {#v100-2016-02-28} * The `api_call` function now returns a decoded JSON object, rather than a JSON encoded string * Some `api_call` calls now call actions on the parent server object: * `dm.open` * `mpdm.open`, `groups.create`, `groups.createChild` * `channels.create`, `channels.join` ## v0.18.0 (2016-02-21) {#v0180-2016-02-21} * Moves to use semver for versioning * Adds support for private groups and MPDMs * Switches to use requests instead of urllib * Gets Travis CI integration working * Fixes some formatting issues so the code will work for python 2.6 * Cleans up some unused imports, some PEP-8 fixes and a couple bad default args fixes ## v0.17.0 (2016-02-15) {#v0170-2016-02-15} * Fixes the server so that it doesn't add duplicate users or channels to its internal lists, [https://github.com/slackapi/python-slackclient/commit/0cb4bcd6e887b428e27e8059b6278b86ee661aaa](https://github.com/slackapi/python-slackclient/commit/0cb4bcd6e887b428e27e8059b6278b86ee661aaa) * README updates: * Updates the URLs pointing to Slack docs for configuring authentication, [https://github.com/slackapi/python-slackclient/commit/7d01515cebc80918a29100b0e4793790eb83e7b9](https://github.com/slackapi/python-slackclient/commit/7d01515cebc80918a29100b0e4793790eb83e7b9) * s/channnels/channels, [https://github.com/slackapi/python-slackclient/commit/d45285d2f1025899dcd65e259624ee73771f94bb](https://github.com/slackapi/python-slackclient/commit/d45285d2f1025899dcd65e259624ee73771f94bb) * Adds users to the local cache when they join the team, [https://github.com/slackapi/python-slackclient/commit/f7bb8889580cc34471ba1ddc05afc34d1a5efa23](https://github.com/slackapi/python-slackclient/commit/f7bb8889580cc34471ba1ddc05afc34d1a5efa23) * Fixes urllib py 2/3 compatibility, [https://github.com/slackapi/python-slackclient/commit/1046cc2375a85a22e94573e2aad954ba7287c886](https://github.com/slackapi/python-slackclient/commit/1046cc2375a85a22e94573e2aad954ba7287c886) --- Source: https://docs.slack.dev/tools/python-slack-sdk/legacy/conversations # Conversations API The [`slackclient`](https://pypi.org/project/slackclient/) PyPI project is in maintenance mode and the [slack-sdk](https://pypi.org/project/slack-sdk/) project is its successor. The v3 SDK provides additional features such as Socket Mode, OAuth flow, SCIM API, Audit Logs API, better async support, retry handlers, and more. The Slack Conversations API provides your app with a unified interface to work with all the channel-like things encountered in Slack: public channels, private channels, direct messages, group direct messages, and shared channels. Refer to [using the Conversations API](/apis/web-api/using-the-conversations-api) for more information. ## Direct messages {#direct-messages} The `conversations.open` API method opens either a 1:1 direct message with a single user or a multi-person direct message, depending on the number of users supplied to the `users` parameter. (For public or private channels, use the `conversations.create` API method.) Provide a `users` parameter as an array with 1-8 user IDs to open or resume a conversation. Providing only 1 ID will create a direct message. providing more IDs will create a new multi-party direct message or will resume an existing conversation. Subsequent calls with the same set of users will return the already existing conversation. ``` import osfrom slack import WebClientclient = WebClient(token=os.environ["SLACK_API_TOKEN"])response = client.conversations_open(users=["W123456789", "U987654321"]) ``` See the [`conversations.open`](/reference/methods/conversations.open) API method for additional details. ## Creating channels {#creating-channels} Creates a new channel, either public or private. The `name` parameter is required and may contain numbers, letters, hyphens, or underscores, and must contain fewer than 80 characters. To make the channel private, set the optional `is_private` parameter to `True`. ``` import osfrom slack import WebClientfrom time import timeclient = WebClient(token=os.environ["SLACK_API_TOKEN"])channel_name = f"my-private-channel-{round(time())}"response = client.conversations_create( name=channel_name, is_private=True)channel_id = response["channel"]["id"]response = client.conversations_archive(channel=channel_id) ``` See the [`conversations.create`](/reference/methods/conversations.create) API method for additional details. ## Getting conversation information {#more-information} To retrieve a set of metadata about a channel (public, private, DM, or multi-party DM), use the `conversations.info` API method. The `channel` parameter is required and must be a valid channel ID. The optional `include_locale` boolean parameter will return locale data, which may be useful if you wish to return localized responses. The `include_num_members` boolean parameter will return the number of people in a channel. ``` import osfrom slack import WebClientclient = WebClient(token=os.environ["SLACK_API_TOKEN"])response = client.conversations_info( channel="C031415926", include_num_members=1) ``` See the [`conversations.info`](/reference/methods/conversations.info) API method for more details. ## Listing conversations {#listing-conversations} To get a list of all the conversations in a workspace, use the `conversations.list` API method. By default, only public conversations are returned. Use the `types` parameter specify which types of conversations you're interested in. Note that `types` is a string of comma-separated values. ``` import osfrom slack import WebClientclient = WebClient(token=os.environ["SLACK_API_TOKEN"])response = client.conversations_list()conversations = response["channels"] ``` Use the `types` parameter to request additional channels, including `public_channel`, `private_channel`, `mpdm`, and `dm`. This parameter is a string of comma-separated values. ``` import osfrom slack import WebClientclient = WebClient(token=os.environ["SLACK_API_TOKEN"])response = client.conversations_list( types="public_channel, private_channel") ``` See the [`conversations.list`](/reference/methods/conversations.list) API method for more details. ## Getting members of a conversation {#get-members} To get a list of members for a conversation, use the `conversations.members` API method with the required `channel` parameter. ``` import osfrom slack import WebClientclient = WebClient(token=os.environ["SLACK_API_TOKEN"])response = client.conversations_members(channel="C16180339")user_ids = response["members"] ``` See the [`conversations.members`](/reference/methods/conversations.members) API method for more details. ## Leaving a conversation {#leave-conversations} To leave a conversation, use the `conversations.leave` API method with the required `channel` parameter containing the ID of the channel to leave. ``` import osfrom slack import WebClientclient = WebClient(token=os.environ["SLACK_API_TOKEN"])response = client.conversations_leave(channel="C27182818") ``` See the [`conversations.leave`](/reference/methods/conversations.leave) API method for more details. --- Source: https://docs.slack.dev/tools/python-slack-sdk/legacy/faq # FAQs The [`slackclient`](https://pypi.org/project/slackclient/) PyPI project is in maintenance mode and the [slack-sdk](https://pypi.org/project/slack-sdk/) project is its successor. The v3 SDK provides additional features such as Socket Mode, OAuth flow, SCIM API, Audit Logs API, better async support, retry handlers, and more. ## Why can't I install slackclient? {#why-cant-i-install-slackclient} We recommend using [virtualenv (venv)](https://docs.python.org/3/tutorial/venv.html) to set up your Python runtime as follows: ``` # Create a dedicated virtual env for running your Python scriptspython -m venv env# Run env\Scripts\activate on Windows OSsource env/bin/activate# Install slackclient PyPI packagepip install "slackclient>=2.0"# Set your token as an env variable (`set` command for Windows OS)export SLACK_API_TOKEN=xoxb-*** ``` Then, verify the following code works on the Python REPL (you can start it using just `python`): ``` import osimport loggingfrom slack import WebClientlogging.basicConfig(level=logging.DEBUG)client = WebClient(token=os.environ["SLACK_API_TOKEN"])res = client.api_test() ``` If you encounter an error saying `AttributeError: module 'slack' has no attribute 'WebClient'`, run `pip list`. If you find both `slackclient` and `slack` in the output, try removing `slack` by `pip uninstall slack` and reinstalling `slackclient`. ## Should I go with run_async? {#should-i-go-with-run_async} For most cases, we recommend going with `run_async=False` mode. So, the default is `False`. If your application turns `run_async` on, the app should follow efficient ways to use [asyncio](https://docs.python.org/3/library/asyncio.html)'s non-blocking event loops and [aiohttp](https://docs.aiohttp.org/en/stable/). Also, consider using async frameworks and their appropriate runtime. Running event loops along with Flask or similar may not be a good fit. If you have to simultaneously run `WebClient` with `run_async=True` outside an event loop for some reason, sharing a single `WebClient` instance doesn't work for you. Create an instance every time you run the code. The `run_async=False` mode doesn't have such issues. ## What if I found a bug? {#what-if-i-found-a-bug} That's great! Thank you. Let us know by [creating an issue](https://github.com/slackapi/python-slack-sdk/issues/new/choose), or if you're feeling particularly ambitious, why not submit a pull request with a bug fix? Check out our contributor's guide [here](https://github.com/SlackAPI/python-slack-sdk/blob/main/.github/contributing.md). ## What if I have a feature suggestion? {#what-if-i-have-a-feature-suggestion} There's always something more that could be added! Let us know by [creating an issue](https://github.com/slackapi/python-slack-sdk/issues/new/choose) to start a discussion around the proposed feature. If you're feeling particularly ambitious, why not write the feature yourself, and submit a pull request? We love feedback and we also love help from our amazing community of developers! ## How do I contribute? {#how-do-i-contribute} What an excellent question. First of all, please have a look at our contributor's guide [here](https://github.com/SlackAPI/python-slack-sdk/blob/main/.github/contributing.md). All done? Great! While we're super excited to incorporate your new feature, there are a couple of things we want to make sure you've given thought to: * Please include unit tests for your new code. But don't just aim to increase the test coverage, rather, we expect you to have written thoughtful tests that ensure your new feature will continue to work as expected, and to help future contributors to ensure they don't break it! * Please document your new feature. Think about concrete use cases for your feature, and add a section to the appropriate document, including a complete sample program that demonstrates your feature. Including these two items with your pull request will totally make our day - and, more importantly, your future users' days! --- Source: https://docs.slack.dev/tools/python-slack-sdk/legacy/real_time_messaging # Real Time Messaging (RTM) The [`slackclient`](https://pypi.org/project/slackclient/) PyPI project is in maintenance mode and the [slack-sdk](https://pypi.org/project/slack-sdk/) project is its successor. The v3 SDK provides additional features such as Socket Mode, OAuth flow, SCIM API, Audit Logs API, better async support, retry handlers, and more. The [Legacy Real Time Messaging (RTM) API](/legacy/legacy-rtm-api) is a WebSocket-based API that allows you to receive events from Slack in real time and to send messages as users. If you prefer events to be pushed to your app, we recommend using the HTTP-based [Events API](/apis/events-api) instead. The Events API contains some events that aren't supported in the Legacy RTM API (such as the [app\_home\_opened event](/reference/events/app_home_opened)), and it supports most of the event types in the Legacy RTM API. If you'd like to use the Events API, you can use the [Python Slack Events Adapter](https://github.com/slackapi/python-slack-events-api). The RTMClient allows apps to communicate with the Legacy RTM API. The event-driven architecture of this client allows you to simply link callbacks to their corresponding events. When an event occurs, this client executes your callback while passing along any information it receives. We also give you the ability to call our web client from inside your callbacks. In our example below, we watch for a [message event](/reference/events/message) that contains "Hello" and if it's received, we call the `say_hello()` function. We then issue a call to the web client to post back to the channel saying "Hi" to the user. ## Configuring the RTM API {#configuration} Events using the Legacy RTM API **must** use a Slack app with a plain `bot` scope. If you already have a Slack app with a plain `bot` scope, you can use those credentials. If you don't and need to use the Legacy RTM API, you can create a Slack app [here](https://api.slack.com/apps?new_classic_app=1). Even if the Slack app configuration pages encourage you to upgrade to a newer permission model, don't upgrade it and continue using the "classic" bot permission. ## Connecting to the RTM API {#connecting} ``` import osfrom slack import RTMClient@RTMClient.run_on(event="message")def say_hello(**payload): data = payload['data'] web_client = payload['web_client'] if 'Hello' in data['text']: channel_id = data['channel'] thread_ts = data['ts'] user = data['user'] # This is not username but user ID (the format is either U*** or W***) web_client.chat_postMessage( channel=channel_id, text=f"Hi <@{user}>!", thread_ts=thread_ts )slack_token = os.environ["SLACK_API_TOKEN"]rtm_client = RTMClient(token=slack_token)rtm_client.start() ``` ## The rtm.start vs. rtm.connect API methods {#rtm-methods} By default, the RTM client uses the [`rtm.connect`](/reference/methods/rtm.connect) API method to establish a WebSocket connection with Slack. The response contains basic information about the team and WebSocket URL. If you'd rather use the [`rtm.start`](/reference/methods/rtm.start) API method to establish the connection, which provides more information about the conversations and users on the team, you can set the `connect_method` option to `rtm.start` when instantiating the RTM Client. Note that on larger teams, use of `rtm.start` can be slow and unreliable. ``` import osfrom slack import RTMClient@RTMClient.run_on(event="message")def say_hello(**payload): data = payload['data'] web_client = payload['web_client'] if 'text' in data and 'Hello' in data['text']: channel_id = data['channel'] thread_ts = data['ts'] user = data['user'] # This is not username but user ID (the format is either U*** or W***) web_client.chat_postMessage( channel=channel_id, text=f"Hi <@{user}>!", thread_ts=thread_ts )slack_token = os.environ["SLACK_API_TOKEN"]rtm_client = RTMClient( token=slack_token, connect_method='rtm.start')rtm_client.start() ``` See the [`rtm.connect`](/reference/methods/rtm.connect) and [`rtm.start`](/reference/methods/rtm.start) API methods for more details. ## RTM events {#rtm-events} ``` { 'type': 'message', 'ts': '1358878749.000002', 'user': 'U023BECGF', 'text': 'Hello'} ``` Refer to the [Legacy RTM API](/legacy/legacy-rtm-api) page for more information. --- Source: https://docs.slack.dev/tools/python-slack-sdk/oauth # OAuth modules This page explains how to handle the Slack OAuth flow. If you're looking for a much easier way to do this, check out [Bolt for Python](https://github.com/slackapi/bolt-python), a full-stack Slack app framework. With Bolt, you won't need to implement most of the following code on your own. Refer to the [Python document for this module](https://docs.slack.dev/tools/python-slack-sdk/reference) for more details. ## App installation flow {#app-installation} OAuth allows a user in any Slack workspace to install your app. At the end of the OAuth flow, your app gains an access token. Refer to the [installing with OAuth](/authentication/installing-with-oauth) guide for details. The Python Slack SDK provides the necessary modules for building the OAuth flow. ### Starting an OAuth flow {#oauth-flow} The first step of the OAuth flow is to redirect a Slack user to [authorize](https://slack.com/oauth/v2/authorize) with a valid `state` parameter. To implement this process, you can use the following modules. Module What it's for Default Implementation `InstallationStore` Persist installation data and look it up by IDs. `FileInstallationStore` `OAuthStateStore` Issue and consume `state` parameter value on the server-side. `FileOAuthStateStore` `AuthorizeUrlGenerator` Build [https://slack.com/oauth/v2/authorize](https://slack.com/oauth/v2/authorize) with sufficient query parameters (same) The code snippet below demonstrates how to build it using [Flask](https://flask.palletsprojects.com/). ``` import osimport htmlfrom slack_sdk.oauth import AuthorizeUrlGeneratorfrom slack_sdk.oauth.installation_store import FileInstallationStore, Installationfrom slack_sdk.oauth.state_store import FileOAuthStateStore# Issue and consume state parameter value on the server-side.state_store = FileOAuthStateStore(expiration_seconds=300, base_dir="./data")# Persist installation data and look it up by IDs.installation_store = FileInstallationStore(base_dir="./data")# Build https://slack.com/oauth/v2/authorize with sufficient query parametersauthorize_url_generator = AuthorizeUrlGenerator( client_id=os.environ["SLACK_CLIENT_ID"], scopes=["app_mentions:read", "chat:write"], user_scopes=["search:read"],)from flask import Flask, request, make_responseapp = Flask(__name__)@app.route("/slack/install", methods=["GET"])def oauth_start(): # Generate a random value and store it on the server-side state = state_store.issue() # https://slack.com/oauth/v2/authorize?state=(generated value)&client_id={client_id}&scope=app_mentions:read,chat:write&user_scope=search:read url = authorize_url_generator.generate(state) return f'' \ f'' ``` When accessing `https://(your domain)/slack/install`, you will see an "Add to Slack" button on the page. You can start the app's installation flow by clicking the button. ### Handling a callback request from Slack {#handling-callback-requests} If all is well, a user goes through the Slack app installation UI and accepts all the scopes your app requests. After that happens, Slack redirects the user back to your specified Redirect URL. The redirection gives you a `code` parameter. You can exchange the value for an access token by calling the [oauth.v2.access](/reference/methods/oauth.v2.access) API method. ``` from slack_sdk.web import WebClientclient_secret = os.environ["SLACK_CLIENT_SECRET"]# Redirect URL@app.route("/slack/oauth/callback", methods=["GET"])def oauth_callback(): # Retrieve the auth code and state from the request params if "code" in request.args: # Verify the state parameter if state_store.consume(request.args["state"]): client = WebClient() # no prepared token needed for this # Complete the installation by calling oauth.v2.access API method oauth_response = client.oauth_v2_access( client_id=os.environ["SLACK_CLIENT_ID"], client_secret=client_secret, code=request.args["code"] ) installed_enterprise = oauth_response.get("enterprise") or {} is_enterprise_install = oauth_response.get("is_enterprise_install") installed_team = oauth_response.get("team") or {} installer = oauth_response.get("authed_user") or {} incoming_webhook = oauth_response.get("incoming_webhook") or {} bot_token = oauth_response.get("access_token") # NOTE: oauth.v2.access doesn't include bot_id in response bot_id = None enterprise_url = None if bot_token is not None: auth_test = client.auth_test(token=bot_token) bot_id = auth_test["bot_id"] if is_enterprise_install is True: enterprise_url = auth_test.get("url") installation = Installation( app_id=oauth_response.get("app_id"), enterprise_id=installed_enterprise.get("id"), enterprise_name=installed_enterprise.get("name"), enterprise_url=enterprise_url, team_id=installed_team.get("id"), team_name=installed_team.get("name"), bot_token=bot_token, bot_id=bot_id, bot_user_id=oauth_response.get("bot_user_id"), bot_scopes=oauth_response.get("scope"), # comma-separated string user_id=installer.get("id"), user_token=installer.get("access_token"), user_scopes=installer.get("scope"), # comma-separated string incoming_webhook_url=incoming_webhook.get("url"), incoming_webhook_channel=incoming_webhook.get("channel"), incoming_webhook_channel_id=incoming_webhook.get("channel_id"), incoming_webhook_configuration_url=incoming_webhook.get("configuration_url"), is_enterprise_install=is_enterprise_install, token_type=oauth_response.get("token_type"), ) # Store the installation installation_store.save(installation) return "Thanks for installing this app!" else: return make_response(f"Try the installation again (the state value is already expired)", 400) error = request.args["error"] if "error" in request.args else "" return make_response(f"Something is wrong with the installation (error: {html.escape(error)})", 400) ``` ## Token lookup {#token-lookup} Now that your Flask app can choose the right access token for incoming event requests, let's add the Slack event handler endpoint. You can use the same `InstallationStore` in the Slack event handler. ``` import jsonfrom slack_sdk.errors import SlackApiErrorfrom slack_sdk.signature import SignatureVerifiersigning_secret = os.environ["SLACK_SIGNING_SECRET"]signature_verifier = SignatureVerifier(signing_secret=signing_secret)@app.route("/slack/events", methods=["POST"])def slack_app(): # Verify incoming requests from Slack # https://docs.slack.dev/authentication/verifying-requests-from-slack if not signature_verifier.is_valid( body=request.get_data(), timestamp=request.headers.get("X-Slack-Request-Timestamp"), signature=request.headers.get("X-Slack-Signature")): return make_response("invalid request", 403) # Handle a slash command invocation if "command" in request.form \ and request.form["command"] == "/open-modal": try: # in the case where this app gets a request from an Enterprise Grid workspace enterprise_id = request.form.get("enterprise_id") # The workspace's ID team_id = request.form["team_id"] # Lookup the stored bot token for this workspace bot = installation_store.find_bot( enterprise_id=enterprise_id, team_id=team_id, ) bot_token = bot.bot_token if bot else None if not bot_token: # The app may be uninstalled or be used in a shared channel return make_response("Please install this app first!", 200) # Open a modal using the valid bot token client = WebClient(token=bot_token) trigger_id = request.form["trigger_id"] response = client.views_open( trigger_id=trigger_id, view={ "type": "modal", "callback_id": "modal-id", "title": { "type": "plain_text", "text": "Awesome Modal" }, "submit": { "type": "plain_text", "text": "Submit" }, "blocks": [ { "type": "input", "block_id": "b-id", "label": { "type": "plain_text", "text": "Input label", }, "element": { "action_id": "a-id", "type": "plain_text_input", } } ] } ) return make_response("", 200) except SlackApiError as e: code = e.response["error"] return make_response(f"Failed to open a modal due to {code}", 200) elif "payload" in request.form: # Data submission from the modal payload = json.loads(request.form["payload"]) if payload["type"] == "view_submission" \ and payload["view"]["callback_id"] == "modal-id": submitted_data = payload["view"]["state"]["values"] print(submitted_data) # {'b-id': {'a-id': {'type': 'plain_text_input', 'value': 'your input'}}} # You can use WebClient with a valid token here too return make_response("", 200) # Indicate unsupported request patterns return make_response("", 404) ``` ## Sign in with Slack {#siws} [Sign in with Slack](/authentication/sign-in-with-slack) helps users log into your service using their Slack profile. The platform feature was upgraded to be compatible with the standard [OpenID Connect](https://openid.net/connect/) specification. With slack-sdk v3.9+, implementing the OAuth flow is much easier. When you create a new Slack app, set the following user scopes: ``` oauth_config: redirect_urls: - https://{your-domain}/slack/oauth_redirect scopes: user: - openid # required - email # optional - profile # optional ``` Check [the Flask app example](https://github.com/slackapi/python-slack-sdk/blob/main/integration_tests/samples/openid_connect/flask_example.py) to learn how to implement an app that handles the OpenID Connect flow with your end-users as follows: **Build the OpenID Connect authorize URL** * `slack_sdk.oauth.OpenIDConnectAuthorizeUrlGenerator` helps you do this. * `slack_sdk.oauth.OAuthStateStore` is still available for generating the `state` parameter value. It's available for `nonce` management, too. **openid.connect.\* API calls** * `WebClient` can perform `openid.connect.token` API calls with given `code` parameter. If you want to know the way with asyncio, check [the Sanic app example](https://github.com/slackapi/python-slack-sdk/blob/main/integration_tests/samples/openid_connect/sanic_example.py) in the same directory. --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference # Package slack_sdk * The SDK website: [https://docs.slack.dev/tools/python-slack-sdk](https://docs.slack.dev/tools/python-slack-sdk) * PyPI package: [https://pypi.org/project/slack-sdk/](https://pypi.org/project/slack-sdk/) Here is the list of key modules in this SDK: #### Web API Client * Web API client: `[slack_sdk.web.client](web/client.html "slack_sdk.web.client")` * asyncio-based Web API client: `[slack_sdk.web.async_client](web/async_client.html "slack_sdk.web.async_client")` #### Webhook / response_url Client * Webhook client: `[slack_sdk.webhook.client](webhook/client.html "slack_sdk.webhook.client")` * asyncio-based Webhook client: `[slack_sdk.webhook.async_client](webhook/async_client.html "slack_sdk.webhook.async_client")` #### Socket Mode Client * The built-in Socket Mode client: `[slack_sdk.socket_mode.builtin.client](socket_mode/builtin/client.html "slack_sdk.socket_mode.builtin.client")` * [aiohttp](https://pypi.org/project/aiohttp/) based client: `[slack_sdk.socket_mode.aiohttp](socket_mode/aiohttp/index.html "slack_sdk.socket_mode.aiohttp")` * [websocket\_client](https://pypi.org/project/websocket-client/) based client: `[slack_sdk.socket_mode.websocket_client](socket_mode/websocket_client/index.html "slack_sdk.socket_mode.websocket_client")` * [websockets](https://pypi.org/project/websockets/) based client: `[slack_sdk.socket_mode.websockets](socket_mode/websockets/index.html "slack_sdk.socket_mode.websockets")` #### OAuth * `[slack_sdk.oauth.installation_store.installation_store](oauth/installation_store/installation_store.html "slack_sdk.oauth.installation_store.installation_store")` * `[slack_sdk.oauth.state_store](oauth/state_store/index.html "slack_sdk.oauth.state_store")` #### Audit Logs API Client * `[slack_sdk.audit_logs.v1.client](audit_logs/v1/client.html "slack_sdk.audit_logs.v1.client")` * `[slack_sdk.audit_logs.v1.async_client](audit_logs/v1/async_client.html "slack_sdk.audit_logs.v1.async_client")` #### SCIM API Client * `[slack_sdk.scim.v1.client](scim/v1/client.html "slack_sdk.scim.v1.client")` * `[slack_sdk.scim.v1.async_client](scim/v1/async_client.html "slack_sdk.scim.v1.async_client")` ## Sub-modules `[slack_sdk.aiohttp_version_checker](aiohttp_version_checker.html "slack_sdk.aiohttp_version_checker")` Internal module for checking aiohttp compatibility of async modules `[slack_sdk.audit_logs](audit_logs/index.html "slack_sdk.audit_logs")` Audit Logs API is a set of APIs for monitoring what’s happening in your Enterprise Grid organization … `[slack_sdk.errors](errors/index.html "slack_sdk.errors")` Errors that can be raised by this SDK `[slack_sdk.http_retry](http_retry/index.html "slack_sdk.http_retry")` `[slack_sdk.models](models/index.html "slack_sdk.models")` Classes for constructing Slack-specific data structure `[slack_sdk.oauth](oauth/index.html "slack_sdk.oauth")` Modules for implementing the Slack OAuth flow … `[slack_sdk.proxy_env_variable_loader](proxy_env_variable_loader.html "slack_sdk.proxy_env_variable_loader")` Internal module for loading proxy-related env variables `[slack_sdk.rtm](rtm/index.html "slack_sdk.rtm")` A Python module for interacting with Slack's RTM API. `[slack_sdk.rtm_v2](rtm_v2/index.html "slack_sdk.rtm_v2")` A Python module for interacting with Slack's RTM API. `[slack_sdk.scim](scim/index.html "slack_sdk.scim")` SCIM API is a set of APIs for provisioning and managing user accounts and groups. SCIM is used by Single Sign-On (SSO) services and identity providers … `[slack_sdk.signature](signature/index.html "slack_sdk.signature")` Slack request signature verifier `[slack_sdk.socket_mode](socket_mode/index.html "slack_sdk.socket_mode")` Socket Mode is a method of connecting your app to Slack’s APIs using WebSockets instead of HTTP. You can use slack\_sdk.socket\_mode.SocketModeClient … `[slack_sdk.version](version.html "slack_sdk.version")` Check the latest version at [https://pypi.org/project/slack-sdk/](https://pypi.org/project/slack-sdk/) `[slack_sdk.web](web/index.html "slack_sdk.web")` The Slack Web API allows you to build applications that interact with Slack in more complex ways than the integrations we provide out of the box. `[slack_sdk.webhook](webhook/index.html "slack_sdk.webhook")` You can use slack\_sdk.webhook.WebhookClient for Incoming Webhooks and message responses using response\_url in payloads. ## Classes `class WebClient (token: str | None = None, base_url: str = 'https://slack.com/api/', timeout: int = 30, ssl: ssl.SSLContext | None = None, proxy: str | None = None, headers: dict | None = None, user_agent_prefix: str | None = None, user_agent_suffix: str | None = None, team_id: str | None = None, logger: logging.Logger | None = None, retry_handlers: List[[RetryHandler](http_retry/handler.html#slack_sdk.http_retry.handler.RetryHandler "slack_sdk.http_retry.handler.RetryHandler")] | None = None)` Expand source code ``` class WebClient(BaseClient): """A WebClient allows apps to communicate with the Slack Platform's Web API. https://docs.slack.dev/reference/methods The Slack Web API is an interface for querying information from and enacting change in a Slack workspace. This client handles constructing and sending HTTP requests to Slack as well as parsing any responses received into a `SlackResponse`. Attributes: token (str): A string specifying an `xoxp-*` or `xoxb-*` token. base_url (str): A string representing the Slack API base URL. Default is `'https://slack.com/api/'` timeout (int): The maximum number of seconds the client will wait to connect and receive a response from Slack. Default is 30 seconds. ssl (SSLContext): An [`ssl.SSLContext`][1] instance, helpful for specifying your own custom certificate chain. proxy (str): String representing a fully-qualified URL to a proxy through which to route all requests to the Slack API. Even if this parameter is not specified, if any of the following environment variables are present, they will be loaded into this parameter: `HTTPS_PROXY`, `https_proxy`, `HTTP_PROXY` or `http_proxy`. headers (dict): Additional request headers to attach to all requests. Methods: `api_call`: Constructs a request and executes the API call to Slack. Example of recommended usage: ```python import os from slack_sdk import WebClient client = WebClient(token=os.environ['SLACK_API_TOKEN']) response = client.chat_postMessage( channel='#random', text="Hello world!") assert response["ok"] assert response["message"]["text"] == "Hello world!" ``` Example manually creating an API request: ```python import os from slack_sdk import WebClient client = WebClient(token=os.environ['SLACK_API_TOKEN']) response = client.api_call( api_method='chat.postMessage', json={'channel': '#random','text': "Hello world!"} ) assert response["ok"] assert response["message"]["text"] == "Hello world!" ``` Note: Any attributes or methods prefixed with _underscores are intended to be "private" internal use only. They may be changed or removed at anytime. [1]: https://docs.python.org/3/library/ssl.html#ssl.SSLContext """ def admin_analytics_getFile( self, *, type: str, date: Optional[str] = None, metadata_only: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Retrieve analytics data for a given date, presented as a compressed JSON file https://docs.slack.dev/reference/methods/admin.analytics.getFile """ kwargs.update({"type": type}) if date is not None: kwargs.update({"date": date}) if metadata_only is not None: kwargs.update({"metadata_only": metadata_only}) return self.api_call("admin.analytics.getFile", params=kwargs) def admin_apps_approve( self, *, app_id: Optional[str] = None, request_id: Optional[str] = None, enterprise_id: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Approve an app for installation on a workspace. Either app_id or request_id is required. These IDs can be obtained either directly via the app_requested event, or by the admin.apps.requests.list method. https://docs.slack.dev/reference/methods/admin.apps.approve """ if app_id: kwargs.update({"app_id": app_id}) elif request_id: kwargs.update({"request_id": request_id}) else: raise e.SlackRequestError("The app_id or request_id argument must be specified.") kwargs.update( { "enterprise_id": enterprise_id, "team_id": team_id, } ) return self.api_call("admin.apps.approve", params=kwargs) def admin_apps_approved_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, enterprise_id: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List approved apps for an org or workspace. https://docs.slack.dev/reference/methods/admin.apps.approved.list """ kwargs.update( { "cursor": cursor, "limit": limit, "enterprise_id": enterprise_id, "team_id": team_id, } ) return self.api_call("admin.apps.approved.list", http_verb="GET", params=kwargs) def admin_apps_clearResolution( self, *, app_id: str, enterprise_id: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Clear an app resolution https://docs.slack.dev/reference/methods/admin.apps.clearResolution """ kwargs.update( { "app_id": app_id, "enterprise_id": enterprise_id, "team_id": team_id, } ) return self.api_call("admin.apps.clearResolution", http_verb="POST", params=kwargs) def admin_apps_requests_cancel( self, *, request_id: str, enterprise_id: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List app requests for a team/workspace. https://docs.slack.dev/reference/methods/admin.apps.requests.cancel """ kwargs.update( { "request_id": request_id, "enterprise_id": enterprise_id, "team_id": team_id, } ) return self.api_call("admin.apps.requests.cancel", http_verb="POST", params=kwargs) def admin_apps_requests_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List app requests for a team/workspace. https://docs.slack.dev/reference/methods/admin.apps.requests.list """ kwargs.update( { "cursor": cursor, "limit": limit, "team_id": team_id, } ) return self.api_call("admin.apps.requests.list", http_verb="GET", params=kwargs) def admin_apps_restrict( self, *, app_id: Optional[str] = None, request_id: Optional[str] = None, enterprise_id: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Restrict an app for installation on a workspace. Exactly one of the team_id or enterprise_id arguments is required, not both. Either app_id or request_id is required. These IDs can be obtained either directly via the app_requested event, or by the admin.apps.requests.list method. https://docs.slack.dev/reference/methods/admin.apps.restrict """ if app_id: kwargs.update({"app_id": app_id}) elif request_id: kwargs.update({"request_id": request_id}) else: raise e.SlackRequestError("The app_id or request_id argument must be specified.") kwargs.update( { "enterprise_id": enterprise_id, "team_id": team_id, } ) return self.api_call("admin.apps.restrict", params=kwargs) def admin_apps_restricted_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, enterprise_id: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List restricted apps for an org or workspace. https://docs.slack.dev/reference/methods/admin.apps.restricted.list """ kwargs.update( { "cursor": cursor, "limit": limit, "enterprise_id": enterprise_id, "team_id": team_id, } ) return self.api_call("admin.apps.restricted.list", http_verb="GET", params=kwargs) def admin_apps_uninstall( self, *, app_id: str, enterprise_id: Optional[str] = None, team_ids: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """Uninstall an app from one or many workspaces, or an entire enterprise organization. With an org-level token, enterprise_id or team_ids is required. https://docs.slack.dev/reference/methods/admin.apps.uninstall """ kwargs.update({"app_id": app_id}) if enterprise_id is not None: kwargs.update({"enterprise_id": enterprise_id}) if team_ids is not None: if isinstance(team_ids, (list, tuple)): kwargs.update({"team_ids": ",".join(team_ids)}) else: kwargs.update({"team_ids": team_ids}) return self.api_call("admin.apps.uninstall", http_verb="POST", params=kwargs) def admin_apps_activities_list( self, *, app_id: Optional[str] = None, component_id: Optional[str] = None, component_type: Optional[str] = None, log_event_type: Optional[str] = None, max_date_created: Optional[int] = None, min_date_created: Optional[int] = None, min_log_level: Optional[str] = None, sort_direction: Optional[str] = None, source: Optional[str] = None, team_id: Optional[str] = None, trace_id: Optional[str] = None, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Get logs for a specified team/org https://docs.slack.dev/reference/methods/admin.apps.activities.list """ kwargs.update( { "app_id": app_id, "component_id": component_id, "component_type": component_type, "log_event_type": log_event_type, "max_date_created": max_date_created, "min_date_created": min_date_created, "min_log_level": min_log_level, "sort_direction": sort_direction, "source": source, "team_id": team_id, "trace_id": trace_id, "cursor": cursor, "limit": limit, } ) return self.api_call("admin.apps.activities.list", params=kwargs) def admin_apps_config_lookup( self, *, app_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Look up the app config for connectors by their IDs https://docs.slack.dev/reference/methods/admin.apps.config.lookup """ if isinstance(app_ids, (list, tuple)): kwargs.update({"app_ids": ",".join(app_ids)}) else: kwargs.update({"app_ids": app_ids}) return self.api_call("admin.apps.config.lookup", params=kwargs) def admin_apps_config_set( self, *, app_id: str, domain_restrictions: Optional[Dict[str, Any]] = None, workflow_auth_strategy: Optional[str] = None, **kwargs, ) -> SlackResponse: """Set the app config for a connector https://docs.slack.dev/reference/methods/admin.apps.config.set """ kwargs.update( { "app_id": app_id, "workflow_auth_strategy": workflow_auth_strategy, } ) if domain_restrictions is not None: kwargs.update({"domain_restrictions": json.dumps(domain_restrictions)}) return self.api_call("admin.apps.config.set", params=kwargs) def admin_auth_policy_getEntities( self, *, policy_name: str, cursor: Optional[str] = None, entity_type: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Fetch all the entities assigned to a particular authentication policy by name. https://docs.slack.dev/reference/methods/admin.auth.policy.getEntities """ kwargs.update({"policy_name": policy_name}) if cursor is not None: kwargs.update({"cursor": cursor}) if entity_type is not None: kwargs.update({"entity_type": entity_type}) if limit is not None: kwargs.update({"limit": limit}) return self.api_call("admin.auth.policy.getEntities", http_verb="POST", params=kwargs) def admin_auth_policy_assignEntities( self, *, entity_ids: Union[str, Sequence[str]], policy_name: str, entity_type: str, **kwargs, ) -> SlackResponse: """Assign entities to a particular authentication policy. https://docs.slack.dev/reference/methods/admin.auth.policy.assignEntities """ if isinstance(entity_ids, (list, tuple)): kwargs.update({"entity_ids": ",".join(entity_ids)}) else: kwargs.update({"entity_ids": entity_ids}) kwargs.update({"policy_name": policy_name}) kwargs.update({"entity_type": entity_type}) return self.api_call("admin.auth.policy.assignEntities", http_verb="POST", params=kwargs) def admin_auth_policy_removeEntities( self, *, entity_ids: Union[str, Sequence[str]], policy_name: str, entity_type: str, **kwargs, ) -> SlackResponse: """Remove specified entities from a specified authentication policy. https://docs.slack.dev/reference/methods/admin.auth.policy.removeEntities """ if isinstance(entity_ids, (list, tuple)): kwargs.update({"entity_ids": ",".join(entity_ids)}) else: kwargs.update({"entity_ids": entity_ids}) kwargs.update({"policy_name": policy_name}) kwargs.update({"entity_type": entity_type}) return self.api_call("admin.auth.policy.removeEntities", http_verb="POST", params=kwargs) def admin_conversations_createForObjects( self, *, object_id: str, salesforce_org_id: str, invite_object_team: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Create a Salesforce channel for the corresponding object provided. https://docs.slack.dev/reference/methods/admin.conversations.createForObjects """ kwargs.update( {"object_id": object_id, "salesforce_org_id": salesforce_org_id, "invite_object_team": invite_object_team} ) return self.api_call("admin.conversations.createForObjects", params=kwargs) def admin_conversations_linkObjects( self, *, channel: str, record_id: str, salesforce_org_id: str, **kwargs, ) -> SlackResponse: """Link a Salesforce record to a channel. https://docs.slack.dev/reference/methods/admin.conversations.linkObjects """ kwargs.update( { "channel": channel, "record_id": record_id, "salesforce_org_id": salesforce_org_id, } ) return self.api_call("admin.conversations.linkObjects", params=kwargs) def admin_conversations_unlinkObjects( self, *, channel: str, new_name: str, **kwargs, ) -> SlackResponse: """Unlink a Salesforce record from a channel. https://docs.slack.dev/reference/methods/admin.conversations.unlinkObjects """ kwargs.update( { "channel": channel, "new_name": new_name, } ) return self.api_call("admin.conversations.unlinkObjects", params=kwargs) def admin_barriers_create( self, *, barriered_from_usergroup_ids: Union[str, Sequence[str]], primary_usergroup_id: str, restricted_subjects: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Create an Information Barrier https://docs.slack.dev/reference/methods/admin.barriers.create """ kwargs.update({"primary_usergroup_id": primary_usergroup_id}) if isinstance(barriered_from_usergroup_ids, (list, tuple)): kwargs.update({"barriered_from_usergroup_ids": ",".join(barriered_from_usergroup_ids)}) else: kwargs.update({"barriered_from_usergroup_ids": barriered_from_usergroup_ids}) if isinstance(restricted_subjects, (list, tuple)): kwargs.update({"restricted_subjects": ",".join(restricted_subjects)}) else: kwargs.update({"restricted_subjects": restricted_subjects}) return self.api_call("admin.barriers.create", http_verb="POST", params=kwargs) def admin_barriers_delete( self, *, barrier_id: str, **kwargs, ) -> SlackResponse: """Delete an existing Information Barrier https://docs.slack.dev/reference/methods/admin.barriers.delete """ kwargs.update({"barrier_id": barrier_id}) return self.api_call("admin.barriers.delete", http_verb="POST", params=kwargs) def admin_barriers_update( self, *, barrier_id: str, barriered_from_usergroup_ids: Union[str, Sequence[str]], primary_usergroup_id: str, restricted_subjects: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Update an existing Information Barrier https://docs.slack.dev/reference/methods/admin.barriers.update """ kwargs.update({"barrier_id": barrier_id, "primary_usergroup_id": primary_usergroup_id}) if isinstance(barriered_from_usergroup_ids, (list, tuple)): kwargs.update({"barriered_from_usergroup_ids": ",".join(barriered_from_usergroup_ids)}) else: kwargs.update({"barriered_from_usergroup_ids": barriered_from_usergroup_ids}) if isinstance(restricted_subjects, (list, tuple)): kwargs.update({"restricted_subjects": ",".join(restricted_subjects)}) else: kwargs.update({"restricted_subjects": restricted_subjects}) return self.api_call("admin.barriers.update", http_verb="POST", params=kwargs) def admin_barriers_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Get all Information Barriers for your organization https://docs.slack.dev/reference/methods/admin.barriers.list""" kwargs.update( { "cursor": cursor, "limit": limit, } ) return self.api_call("admin.barriers.list", http_verb="GET", params=kwargs) def admin_conversations_create( self, *, is_private: bool, name: str, description: Optional[str] = None, org_wide: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Create a public or private channel-based conversation. https://docs.slack.dev/reference/methods/admin.conversations.create """ kwargs.update( { "is_private": is_private, "name": name, "description": description, "org_wide": org_wide, "team_id": team_id, } ) return self.api_call("admin.conversations.create", params=kwargs) def admin_conversations_delete( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """Delete a public or private channel. https://docs.slack.dev/reference/methods/admin.conversations.delete """ kwargs.update({"channel_id": channel_id}) return self.api_call("admin.conversations.delete", params=kwargs) def admin_conversations_invite( self, *, channel_id: str, user_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Invite a user to a public or private channel. https://docs.slack.dev/reference/methods/admin.conversations.invite """ kwargs.update({"channel_id": channel_id}) if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) # NOTE: the endpoint is unable to handle Content-Type: application/json as of Sep 3, 2020. return self.api_call("admin.conversations.invite", params=kwargs) def admin_conversations_archive( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """Archive a public or private channel. https://docs.slack.dev/reference/methods/admin.conversations.archive """ kwargs.update({"channel_id": channel_id}) return self.api_call("admin.conversations.archive", params=kwargs) def admin_conversations_unarchive( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """Unarchive a public or private channel. https://docs.slack.dev/reference/methods/admin.conversations.archive """ kwargs.update({"channel_id": channel_id}) return self.api_call("admin.conversations.unarchive", params=kwargs) def admin_conversations_rename( self, *, channel_id: str, name: str, **kwargs, ) -> SlackResponse: """Rename a public or private channel. https://docs.slack.dev/reference/methods/admin.conversations.rename """ kwargs.update({"channel_id": channel_id, "name": name}) return self.api_call("admin.conversations.rename", params=kwargs) def admin_conversations_search( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, query: Optional[str] = None, search_channel_types: Optional[Union[str, Sequence[str]]] = None, sort: Optional[str] = None, sort_dir: Optional[str] = None, team_ids: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """Search for public or private channels in an Enterprise organization. https://docs.slack.dev/reference/methods/admin.conversations.search """ kwargs.update( { "cursor": cursor, "limit": limit, "query": query, "sort": sort, "sort_dir": sort_dir, } ) if isinstance(search_channel_types, (list, tuple)): kwargs.update({"search_channel_types": ",".join(search_channel_types)}) else: kwargs.update({"search_channel_types": search_channel_types}) if isinstance(team_ids, (list, tuple)): kwargs.update({"team_ids": ",".join(team_ids)}) else: kwargs.update({"team_ids": team_ids}) return self.api_call("admin.conversations.search", params=kwargs) def admin_conversations_convertToPrivate( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """Convert a public channel to a private channel. https://docs.slack.dev/reference/methods/admin.conversations.convertToPrivate """ kwargs.update({"channel_id": channel_id}) return self.api_call("admin.conversations.convertToPrivate", params=kwargs) def admin_conversations_convertToPublic( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """Convert a privte channel to a public channel. https://docs.slack.dev/reference/methods/admin.conversations.convertToPublic """ kwargs.update({"channel_id": channel_id}) return self.api_call("admin.conversations.convertToPublic", params=kwargs) def admin_conversations_setConversationPrefs( self, *, channel_id: str, prefs: Union[str, Dict[str, str]], **kwargs, ) -> SlackResponse: """Set the posting permissions for a public or private channel. https://docs.slack.dev/reference/methods/admin.conversations.setConversationPrefs """ kwargs.update({"channel_id": channel_id}) if isinstance(prefs, dict): kwargs.update({"prefs": json.dumps(prefs)}) else: kwargs.update({"prefs": prefs}) return self.api_call("admin.conversations.setConversationPrefs", params=kwargs) def admin_conversations_getConversationPrefs( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """Get conversation preferences for a public or private channel. https://docs.slack.dev/reference/methods/admin.conversations.getConversationPrefs """ kwargs.update({"channel_id": channel_id}) return self.api_call("admin.conversations.getConversationPrefs", params=kwargs) def admin_conversations_disconnectShared( self, *, channel_id: str, leaving_team_ids: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """Disconnect a connected channel from one or more workspaces. https://docs.slack.dev/reference/methods/admin.conversations.disconnectShared """ kwargs.update({"channel_id": channel_id}) if isinstance(leaving_team_ids, (list, tuple)): kwargs.update({"leaving_team_ids": ",".join(leaving_team_ids)}) else: kwargs.update({"leaving_team_ids": leaving_team_ids}) return self.api_call("admin.conversations.disconnectShared", params=kwargs) def admin_conversations_lookup( self, *, last_message_activity_before: int, team_ids: Union[str, Sequence[str]], cursor: Optional[str] = None, limit: Optional[int] = None, max_member_count: Optional[int] = None, **kwargs, ) -> SlackResponse: """Returns channels on the given team using the filters. https://docs.slack.dev/reference/methods/admin.conversations.lookup """ kwargs.update( { "last_message_activity_before": last_message_activity_before, "cursor": cursor, "limit": limit, "max_member_count": max_member_count, } ) if isinstance(team_ids, (list, tuple)): kwargs.update({"team_ids": ",".join(team_ids)}) else: kwargs.update({"team_ids": team_ids}) return self.api_call("admin.conversations.lookup", params=kwargs) def admin_conversations_ekm_listOriginalConnectedChannelInfo( self, *, channel_ids: Optional[Union[str, Sequence[str]]] = None, cursor: Optional[str] = None, limit: Optional[int] = None, team_ids: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """List all disconnected channels—i.e., channels that were once connected to other workspaces and then disconnected—and the corresponding original channel IDs for key revocation with EKM. https://docs.slack.dev/reference/methods/admin.conversations.ekm.listOriginalConnectedChannelInfo """ kwargs.update( { "cursor": cursor, "limit": limit, } ) if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) if isinstance(team_ids, (list, tuple)): kwargs.update({"team_ids": ",".join(team_ids)}) else: kwargs.update({"team_ids": team_ids}) return self.api_call("admin.conversations.ekm.listOriginalConnectedChannelInfo", params=kwargs) def admin_conversations_restrictAccess_addGroup( self, *, channel_id: str, group_id: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Add an allowlist of IDP groups for accessing a channel. https://docs.slack.dev/reference/methods/admin.conversations.restrictAccess.addGroup """ kwargs.update( { "channel_id": channel_id, "group_id": group_id, "team_id": team_id, } ) return self.api_call( "admin.conversations.restrictAccess.addGroup", http_verb="GET", params=kwargs, ) def admin_conversations_restrictAccess_listGroups( self, *, channel_id: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List all IDP Groups linked to a channel. https://docs.slack.dev/reference/methods/admin.conversations.restrictAccess.listGroups """ kwargs.update( { "channel_id": channel_id, "team_id": team_id, } ) return self.api_call( "admin.conversations.restrictAccess.listGroups", http_verb="GET", params=kwargs, ) def admin_conversations_restrictAccess_removeGroup( self, *, channel_id: str, group_id: str, team_id: str, **kwargs, ) -> SlackResponse: """Remove a linked IDP group linked from a private channel. https://docs.slack.dev/reference/methods/admin.conversations.restrictAccess.removeGroup """ kwargs.update( { "channel_id": channel_id, "group_id": group_id, "team_id": team_id, } ) return self.api_call( "admin.conversations.restrictAccess.removeGroup", http_verb="GET", params=kwargs, ) def admin_conversations_setTeams( self, *, channel_id: str, org_channel: Optional[bool] = None, target_team_ids: Optional[Union[str, Sequence[str]]] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Set the workspaces in an Enterprise grid org that connect to a public or private channel. https://docs.slack.dev/reference/methods/admin.conversations.setTeams """ kwargs.update( { "channel_id": channel_id, "org_channel": org_channel, "team_id": team_id, } ) if isinstance(target_team_ids, (list, tuple)): kwargs.update({"target_team_ids": ",".join(target_team_ids)}) else: kwargs.update({"target_team_ids": target_team_ids}) return self.api_call("admin.conversations.setTeams", params=kwargs) def admin_conversations_getTeams( self, *, channel_id: str, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Set the workspaces in an Enterprise grid org that connect to a channel. https://docs.slack.dev/reference/methods/admin.conversations.getTeams """ kwargs.update( { "channel_id": channel_id, "cursor": cursor, "limit": limit, } ) return self.api_call("admin.conversations.getTeams", params=kwargs) def admin_conversations_getCustomRetention( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """Get a channel's retention policy https://docs.slack.dev/reference/methods/admin.conversations.getCustomRetention """ kwargs.update({"channel_id": channel_id}) return self.api_call("admin.conversations.getCustomRetention", params=kwargs) def admin_conversations_removeCustomRetention( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """Remove a channel's retention policy https://docs.slack.dev/reference/methods/admin.conversations.removeCustomRetention """ kwargs.update({"channel_id": channel_id}) return self.api_call("admin.conversations.removeCustomRetention", params=kwargs) def admin_conversations_setCustomRetention( self, *, channel_id: str, duration_days: int, **kwargs, ) -> SlackResponse: """Set a channel's retention policy https://docs.slack.dev/reference/methods/admin.conversations.setCustomRetention """ kwargs.update({"channel_id": channel_id, "duration_days": duration_days}) return self.api_call("admin.conversations.setCustomRetention", params=kwargs) def admin_conversations_bulkArchive( self, *, channel_ids: Union[Sequence[str], str], **kwargs, ) -> SlackResponse: """Archive public or private channels in bulk. https://docs.slack.dev/reference/methods/admin.conversations.bulkArchive """ kwargs.update({"channel_ids": ",".join(channel_ids) if isinstance(channel_ids, (list, tuple)) else channel_ids}) return self.api_call("admin.conversations.bulkArchive", params=kwargs) def admin_conversations_bulkDelete( self, *, channel_ids: Union[Sequence[str], str], **kwargs, ) -> SlackResponse: """Delete public or private channels in bulk. https://slack.com/api/admin.conversations.bulkDelete """ kwargs.update({"channel_ids": ",".join(channel_ids) if isinstance(channel_ids, (list, tuple)) else channel_ids}) return self.api_call("admin.conversations.bulkDelete", params=kwargs) def admin_conversations_bulkMove( self, *, channel_ids: Union[Sequence[str], str], target_team_id: str, **kwargs, ) -> SlackResponse: """Move public or private channels in bulk. https://docs.slack.dev/reference/methods/admin.conversations.bulkMove """ kwargs.update( { "target_team_id": target_team_id, "channel_ids": ",".join(channel_ids) if isinstance(channel_ids, (list, tuple)) else channel_ids, } ) return self.api_call("admin.conversations.bulkMove", params=kwargs) def admin_emoji_add( self, *, name: str, url: str, **kwargs, ) -> SlackResponse: """Add an emoji. https://docs.slack.dev/reference/methods/admin.emoji.add """ kwargs.update({"name": name, "url": url}) return self.api_call("admin.emoji.add", http_verb="GET", params=kwargs) def admin_emoji_addAlias( self, *, alias_for: str, name: str, **kwargs, ) -> SlackResponse: """Add an emoji alias. https://docs.slack.dev/reference/methods/admin.emoji.addAlias """ kwargs.update({"alias_for": alias_for, "name": name}) return self.api_call("admin.emoji.addAlias", http_verb="GET", params=kwargs) def admin_emoji_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """List emoji for an Enterprise Grid organization. https://docs.slack.dev/reference/methods/admin.emoji.list """ kwargs.update({"cursor": cursor, "limit": limit}) return self.api_call("admin.emoji.list", http_verb="GET", params=kwargs) def admin_emoji_remove( self, *, name: str, **kwargs, ) -> SlackResponse: """Remove an emoji across an Enterprise Grid organization. https://docs.slack.dev/reference/methods/admin.emoji.remove """ kwargs.update({"name": name}) return self.api_call("admin.emoji.remove", http_verb="GET", params=kwargs) def admin_emoji_rename( self, *, name: str, new_name: str, **kwargs, ) -> SlackResponse: """Rename an emoji. https://docs.slack.dev/reference/methods/admin.emoji.rename """ kwargs.update({"name": name, "new_name": new_name}) return self.api_call("admin.emoji.rename", http_verb="GET", params=kwargs) def admin_functions_list( self, *, app_ids: Union[str, Sequence[str]], team_id: Optional[str] = None, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Look up functions by a set of apps https://docs.slack.dev/reference/methods/admin.functions.list """ if isinstance(app_ids, (list, tuple)): kwargs.update({"app_ids": ",".join(app_ids)}) else: kwargs.update({"app_ids": app_ids}) kwargs.update( { "team_id": team_id, "cursor": cursor, "limit": limit, } ) return self.api_call("admin.functions.list", params=kwargs) def admin_functions_permissions_lookup( self, *, function_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Lookup the visibility of multiple Slack functions and include the users if it is limited to particular named entities. https://docs.slack.dev/reference/methods/admin.functions.permissions.lookup """ if isinstance(function_ids, (list, tuple)): kwargs.update({"function_ids": ",".join(function_ids)}) else: kwargs.update({"function_ids": function_ids}) return self.api_call("admin.functions.permissions.lookup", params=kwargs) def admin_functions_permissions_set( self, *, function_id: str, visibility: str, user_ids: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """Set the visibility of a Slack function and define the users or workspaces if it is set to named_entities https://docs.slack.dev/reference/methods/admin.functions.permissions.set """ kwargs.update( { "function_id": function_id, "visibility": visibility, } ) if user_ids is not None: if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) return self.api_call("admin.functions.permissions.set", params=kwargs) def admin_roles_addAssignments( self, *, role_id: str, entity_ids: Union[str, Sequence[str]], user_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Adds members to the specified role with the specified scopes https://docs.slack.dev/reference/methods/admin.roles.addAssignments """ kwargs.update({"role_id": role_id}) if isinstance(entity_ids, (list, tuple)): kwargs.update({"entity_ids": ",".join(entity_ids)}) else: kwargs.update({"entity_ids": entity_ids}) if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) return self.api_call("admin.roles.addAssignments", params=kwargs) def admin_roles_listAssignments( self, *, role_ids: Optional[Union[str, Sequence[str]]] = None, entity_ids: Optional[Union[str, Sequence[str]]] = None, cursor: Optional[str] = None, limit: Optional[Union[str, int]] = None, sort_dir: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists assignments for all roles across entities. Options to scope results by any combination of roles or entities https://docs.slack.dev/reference/methods/admin.roles.listAssignments """ kwargs.update({"cursor": cursor, "limit": limit, "sort_dir": sort_dir}) if isinstance(entity_ids, (list, tuple)): kwargs.update({"entity_ids": ",".join(entity_ids)}) else: kwargs.update({"entity_ids": entity_ids}) if isinstance(role_ids, (list, tuple)): kwargs.update({"role_ids": ",".join(role_ids)}) else: kwargs.update({"role_ids": role_ids}) return self.api_call("admin.roles.listAssignments", params=kwargs) def admin_roles_removeAssignments( self, *, role_id: str, entity_ids: Union[str, Sequence[str]], user_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Removes a set of users from a role for the given scopes and entities https://docs.slack.dev/reference/methods/admin.roles.removeAssignments """ kwargs.update({"role_id": role_id}) if isinstance(entity_ids, (list, tuple)): kwargs.update({"entity_ids": ",".join(entity_ids)}) else: kwargs.update({"entity_ids": entity_ids}) if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) return self.api_call("admin.roles.removeAssignments", params=kwargs) def admin_users_session_reset( self, *, user_id: str, mobile_only: Optional[bool] = None, web_only: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Wipes all valid sessions on all devices for a given user. https://docs.slack.dev/reference/methods/admin.users.session.reset """ kwargs.update( { "user_id": user_id, "mobile_only": mobile_only, "web_only": web_only, } ) return self.api_call("admin.users.session.reset", params=kwargs) def admin_users_session_resetBulk( self, *, user_ids: Union[str, Sequence[str]], mobile_only: Optional[bool] = None, web_only: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Enqueues an asynchronous job to wipe all valid sessions on all devices for a given list of users https://docs.slack.dev/reference/methods/admin.users.session.resetBulk """ if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) kwargs.update( { "mobile_only": mobile_only, "web_only": web_only, } ) return self.api_call("admin.users.session.resetBulk", params=kwargs) def admin_users_session_invalidate( self, *, session_id: str, team_id: str, **kwargs, ) -> SlackResponse: """Invalidate a single session for a user by session_id. https://docs.slack.dev/reference/methods/admin.users.session.invalidate """ kwargs.update({"session_id": session_id, "team_id": team_id}) return self.api_call("admin.users.session.invalidate", params=kwargs) def admin_users_session_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, team_id: Optional[str] = None, user_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists all active user sessions for an organization https://docs.slack.dev/reference/methods/admin.users.session.list """ kwargs.update( { "cursor": cursor, "limit": limit, "team_id": team_id, "user_id": user_id, } ) return self.api_call("admin.users.session.list", params=kwargs) def admin_teams_settings_setDefaultChannels( self, *, team_id: str, channel_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Set the default channels of a workspace. https://docs.slack.dev/reference/methods/admin.teams.settings.setDefaultChannels """ kwargs.update({"team_id": team_id}) if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) return self.api_call("admin.teams.settings.setDefaultChannels", http_verb="GET", params=kwargs) def admin_users_session_getSettings( self, *, user_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Get user-specific session settings—the session duration and what happens when the client closes—given a list of users. https://docs.slack.dev/reference/methods/admin.users.session.getSettings """ if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) return self.api_call("admin.users.session.getSettings", params=kwargs) def admin_users_session_setSettings( self, *, user_ids: Union[str, Sequence[str]], desktop_app_browser_quit: Optional[bool] = None, duration: Optional[int] = None, **kwargs, ) -> SlackResponse: """Configure the user-level session settings—the session duration and what happens when the client closes—for one or more users. https://docs.slack.dev/reference/methods/admin.users.session.setSettings """ if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) kwargs.update( { "desktop_app_browser_quit": desktop_app_browser_quit, "duration": duration, } ) return self.api_call("admin.users.session.setSettings", params=kwargs) def admin_users_session_clearSettings( self, *, user_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Clear user-specific session settings—the session duration and what happens when the client closes—for a list of users. https://docs.slack.dev/reference/methods/admin.users.session.clearSettings """ if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) return self.api_call("admin.users.session.clearSettings", params=kwargs) def admin_users_unsupportedVersions_export( self, *, date_end_of_support: Optional[Union[str, int]] = None, date_sessions_started: Optional[Union[str, int]] = None, **kwargs, ) -> SlackResponse: """Ask Slackbot to send you an export listing all workspace members using unsupported software, presented as a zipped CSV file. https://docs.slack.dev/reference/methods/admin.users.unsupportedVersions.export """ kwargs.update( { "date_end_of_support": date_end_of_support, "date_sessions_started": date_sessions_started, } ) return self.api_call("admin.users.unsupportedVersions.export", params=kwargs) def admin_inviteRequests_approve( self, *, invite_request_id: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Approve a workspace invite request. https://docs.slack.dev/reference/methods/admin.inviteRequests.approve """ kwargs.update({"invite_request_id": invite_request_id, "team_id": team_id}) return self.api_call("admin.inviteRequests.approve", params=kwargs) def admin_inviteRequests_approved_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List all approved workspace invite requests. https://docs.slack.dev/reference/methods/admin.inviteRequests.approved.list """ kwargs.update( { "cursor": cursor, "limit": limit, "team_id": team_id, } ) return self.api_call("admin.inviteRequests.approved.list", params=kwargs) def admin_inviteRequests_denied_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List all denied workspace invite requests. https://docs.slack.dev/reference/methods/admin.inviteRequests.denied.list """ kwargs.update( { "cursor": cursor, "limit": limit, "team_id": team_id, } ) return self.api_call("admin.inviteRequests.denied.list", params=kwargs) def admin_inviteRequests_deny( self, *, invite_request_id: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Deny a workspace invite request. https://docs.slack.dev/reference/methods/admin.inviteRequests.deny """ kwargs.update({"invite_request_id": invite_request_id, "team_id": team_id}) return self.api_call("admin.inviteRequests.deny", params=kwargs) def admin_inviteRequests_list( self, **kwargs, ) -> SlackResponse: """List all pending workspace invite requests.""" return self.api_call("admin.inviteRequests.list", params=kwargs) def admin_teams_admins_list( self, *, team_id: str, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """List all of the admins on a given workspace. https://docs.slack.dev/reference/methods/admin.inviteRequests.list """ kwargs.update( { "cursor": cursor, "limit": limit, "team_id": team_id, } ) return self.api_call("admin.teams.admins.list", http_verb="GET", params=kwargs) def admin_teams_create( self, *, team_domain: str, team_name: str, team_description: Optional[str] = None, team_discoverability: Optional[str] = None, **kwargs, ) -> SlackResponse: """Create an Enterprise team. https://docs.slack.dev/reference/methods/admin.teams.create """ kwargs.update( { "team_domain": team_domain, "team_name": team_name, "team_description": team_description, "team_discoverability": team_discoverability, } ) return self.api_call("admin.teams.create", params=kwargs) def admin_teams_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """List all teams on an Enterprise organization. https://docs.slack.dev/reference/methods/admin.teams.list """ kwargs.update({"cursor": cursor, "limit": limit}) return self.api_call("admin.teams.list", params=kwargs) def admin_teams_owners_list( self, *, team_id: str, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """List all of the admins on a given workspace. https://docs.slack.dev/reference/methods/admin.teams.owners.list """ kwargs.update({"team_id": team_id, "cursor": cursor, "limit": limit}) return self.api_call("admin.teams.owners.list", http_verb="GET", params=kwargs) def admin_teams_settings_info( self, *, team_id: str, **kwargs, ) -> SlackResponse: """Fetch information about settings in a workspace https://docs.slack.dev/reference/methods/admin.teams.settings.info """ kwargs.update({"team_id": team_id}) return self.api_call("admin.teams.settings.info", params=kwargs) def admin_teams_settings_setDescription( self, *, team_id: str, description: str, **kwargs, ) -> SlackResponse: """Set the description of a given workspace. https://docs.slack.dev/reference/methods/admin.teams.settings.setDescription """ kwargs.update({"team_id": team_id, "description": description}) return self.api_call("admin.teams.settings.setDescription", params=kwargs) def admin_teams_settings_setDiscoverability( self, *, team_id: str, discoverability: str, **kwargs, ) -> SlackResponse: """Sets the icon of a workspace. https://docs.slack.dev/reference/methods/admin.teams.settings.setDiscoverability """ kwargs.update({"team_id": team_id, "discoverability": discoverability}) return self.api_call("admin.teams.settings.setDiscoverability", params=kwargs) def admin_teams_settings_setIcon( self, *, team_id: str, image_url: str, **kwargs, ) -> SlackResponse: """Sets the icon of a workspace. https://docs.slack.dev/reference/methods/admin.teams.settings.setIcon """ kwargs.update({"team_id": team_id, "image_url": image_url}) return self.api_call("admin.teams.settings.setIcon", http_verb="GET", params=kwargs) def admin_teams_settings_setName( self, *, team_id: str, name: str, **kwargs, ) -> SlackResponse: """Sets the icon of a workspace. https://docs.slack.dev/reference/methods/admin.teams.settings.setName """ kwargs.update({"team_id": team_id, "name": name}) return self.api_call("admin.teams.settings.setName", params=kwargs) def admin_usergroups_addChannels( self, *, channel_ids: Union[str, Sequence[str]], usergroup_id: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Add one or more default channels to an IDP group. https://docs.slack.dev/reference/methods/admin.usergroups.addChannels """ kwargs.update({"team_id": team_id, "usergroup_id": usergroup_id}) if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) return self.api_call("admin.usergroups.addChannels", params=kwargs) def admin_usergroups_addTeams( self, *, usergroup_id: str, team_ids: Union[str, Sequence[str]], auto_provision: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Associate one or more default workspaces with an organization-wide IDP group. https://docs.slack.dev/reference/methods/admin.usergroups.addTeams """ kwargs.update({"usergroup_id": usergroup_id, "auto_provision": auto_provision}) if isinstance(team_ids, (list, tuple)): kwargs.update({"team_ids": ",".join(team_ids)}) else: kwargs.update({"team_ids": team_ids}) return self.api_call("admin.usergroups.addTeams", params=kwargs) def admin_usergroups_listChannels( self, *, usergroup_id: str, include_num_members: Optional[bool] = None, team_id: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Add one or more default channels to an IDP group. https://docs.slack.dev/reference/methods/admin.usergroups.listChannels """ kwargs.update( { "usergroup_id": usergroup_id, "include_num_members": include_num_members, "team_id": team_id, } ) return self.api_call("admin.usergroups.listChannels", params=kwargs) def admin_usergroups_removeChannels( self, *, usergroup_id: str, channel_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Add one or more default channels to an IDP group. https://docs.slack.dev/reference/methods/admin.usergroups.removeChannels """ kwargs.update({"usergroup_id": usergroup_id}) if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) return self.api_call("admin.usergroups.removeChannels", params=kwargs) def admin_users_assign( self, *, team_id: str, user_id: str, channel_ids: Optional[Union[str, Sequence[str]]] = None, is_restricted: Optional[bool] = None, is_ultra_restricted: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Add an Enterprise user to a workspace. https://docs.slack.dev/reference/methods/admin.users.assign """ kwargs.update( { "team_id": team_id, "user_id": user_id, "is_restricted": is_restricted, "is_ultra_restricted": is_ultra_restricted, } ) if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) return self.api_call("admin.users.assign", params=kwargs) def admin_users_invite( self, *, team_id: str, email: str, channel_ids: Union[str, Sequence[str]], custom_message: Optional[str] = None, email_password_policy_enabled: Optional[bool] = None, guest_expiration_ts: Optional[Union[str, float]] = None, is_restricted: Optional[bool] = None, is_ultra_restricted: Optional[bool] = None, real_name: Optional[str] = None, resend: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Invite a user to a workspace. https://docs.slack.dev/reference/methods/admin.users.invite """ kwargs.update( { "team_id": team_id, "email": email, "custom_message": custom_message, "email_password_policy_enabled": email_password_policy_enabled, "guest_expiration_ts": str(guest_expiration_ts) if guest_expiration_ts is not None else None, "is_restricted": is_restricted, "is_ultra_restricted": is_ultra_restricted, "real_name": real_name, "resend": resend, } ) if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) return self.api_call("admin.users.invite", params=kwargs) def admin_users_list( self, *, team_id: Optional[str] = None, include_deactivated_user_workspaces: Optional[bool] = None, is_active: Optional[bool] = None, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """List users on a workspace https://docs.slack.dev/reference/methods/admin.users.list """ kwargs.update( { "team_id": team_id, "include_deactivated_user_workspaces": include_deactivated_user_workspaces, "is_active": is_active, "cursor": cursor, "limit": limit, } ) return self.api_call("admin.users.list", params=kwargs) def admin_users_remove( self, *, team_id: str, user_id: str, **kwargs, ) -> SlackResponse: """Remove a user from a workspace. https://docs.slack.dev/reference/methods/admin.users.remove """ kwargs.update({"team_id": team_id, "user_id": user_id}) return self.api_call("admin.users.remove", params=kwargs) def admin_users_setAdmin( self, *, team_id: str, user_id: str, **kwargs, ) -> SlackResponse: """Set an existing guest, regular user, or owner to be an admin user. https://docs.slack.dev/reference/methods/admin.users.setAdmin """ kwargs.update({"team_id": team_id, "user_id": user_id}) return self.api_call("admin.users.setAdmin", params=kwargs) def admin_users_setExpiration( self, *, expiration_ts: int, user_id: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Set an expiration for a guest user. https://docs.slack.dev/reference/methods/admin.users.setExpiration """ kwargs.update({"expiration_ts": expiration_ts, "team_id": team_id, "user_id": user_id}) return self.api_call("admin.users.setExpiration", params=kwargs) def admin_users_setOwner( self, *, team_id: str, user_id: str, **kwargs, ) -> SlackResponse: """Set an existing guest, regular user, or admin user to be a workspace owner. https://docs.slack.dev/reference/methods/admin.users.setOwner """ kwargs.update({"team_id": team_id, "user_id": user_id}) return self.api_call("admin.users.setOwner", params=kwargs) def admin_users_setRegular( self, *, team_id: str, user_id: str, **kwargs, ) -> SlackResponse: """Set an existing guest user, admin user, or owner to be a regular user. https://docs.slack.dev/reference/methods/admin.users.setRegular """ kwargs.update({"team_id": team_id, "user_id": user_id}) return self.api_call("admin.users.setRegular", params=kwargs) def admin_workflows_search( self, *, app_id: Optional[str] = None, collaborator_ids: Optional[Union[str, Sequence[str]]] = None, cursor: Optional[str] = None, limit: Optional[int] = None, no_collaborators: Optional[bool] = None, num_trigger_ids: Optional[int] = None, query: Optional[str] = None, sort: Optional[str] = None, sort_dir: Optional[str] = None, source: Optional[str] = None, **kwargs, ) -> SlackResponse: """Search workflows within the team or enterprise https://docs.slack.dev/reference/methods/admin.workflows.search """ if collaborator_ids is not None: if isinstance(collaborator_ids, (list, tuple)): kwargs.update({"collaborator_ids": ",".join(collaborator_ids)}) else: kwargs.update({"collaborator_ids": collaborator_ids}) kwargs.update( { "app_id": app_id, "cursor": cursor, "limit": limit, "no_collaborators": no_collaborators, "num_trigger_ids": num_trigger_ids, "query": query, "sort": sort, "sort_dir": sort_dir, "source": source, } ) return self.api_call("admin.workflows.search", params=kwargs) def admin_workflows_permissions_lookup( self, *, workflow_ids: Union[str, Sequence[str]], max_workflow_triggers: Optional[int] = None, **kwargs, ) -> SlackResponse: """Look up the permissions for a set of workflows https://docs.slack.dev/reference/methods/admin.workflows.permissions.lookup """ if isinstance(workflow_ids, (list, tuple)): kwargs.update({"workflow_ids": ",".join(workflow_ids)}) else: kwargs.update({"workflow_ids": workflow_ids}) kwargs.update( { "max_workflow_triggers": max_workflow_triggers, } ) return self.api_call("admin.workflows.permissions.lookup", params=kwargs) def admin_workflows_collaborators_add( self, *, collaborator_ids: Union[str, Sequence[str]], workflow_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Add collaborators to workflows within the team or enterprise https://docs.slack.dev/reference/methods/admin.workflows.collaborators.add """ if isinstance(collaborator_ids, (list, tuple)): kwargs.update({"collaborator_ids": ",".join(collaborator_ids)}) else: kwargs.update({"collaborator_ids": collaborator_ids}) if isinstance(workflow_ids, (list, tuple)): kwargs.update({"workflow_ids": ",".join(workflow_ids)}) else: kwargs.update({"workflow_ids": workflow_ids}) return self.api_call("admin.workflows.collaborators.add", params=kwargs) def admin_workflows_collaborators_remove( self, *, collaborator_ids: Union[str, Sequence[str]], workflow_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Remove collaborators from workflows within the team or enterprise https://docs.slack.dev/reference/methods/admin.workflows.collaborators.remove """ if isinstance(collaborator_ids, (list, tuple)): kwargs.update({"collaborator_ids": ",".join(collaborator_ids)}) else: kwargs.update({"collaborator_ids": collaborator_ids}) if isinstance(workflow_ids, (list, tuple)): kwargs.update({"workflow_ids": ",".join(workflow_ids)}) else: kwargs.update({"workflow_ids": workflow_ids}) return self.api_call("admin.workflows.collaborators.remove", params=kwargs) def admin_workflows_unpublish( self, *, workflow_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Unpublish workflows within the team or enterprise https://docs.slack.dev/reference/methods/admin.workflows.unpublish """ if isinstance(workflow_ids, (list, tuple)): kwargs.update({"workflow_ids": ",".join(workflow_ids)}) else: kwargs.update({"workflow_ids": workflow_ids}) return self.api_call("admin.workflows.unpublish", params=kwargs) def api_test( self, *, error: Optional[str] = None, **kwargs, ) -> SlackResponse: """Checks API calling code. https://docs.slack.dev/reference/methods/api.test """ kwargs.update({"error": error}) return self.api_call("api.test", params=kwargs) def apps_connections_open( self, *, app_token: str, **kwargs, ) -> SlackResponse: """Generate a temporary Socket Mode WebSocket URL that your app can connect to in order to receive events and interactive payloads https://docs.slack.dev/reference/methods/apps.connections.open """ kwargs.update({"token": app_token}) return self.api_call("apps.connections.open", http_verb="POST", params=kwargs) def apps_event_authorizations_list( self, *, event_context: str, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Get a list of authorizations for the given event context. Each authorization represents an app installation that the event is visible to. https://docs.slack.dev/reference/methods/apps.event.authorizations.list """ kwargs.update({"event_context": event_context, "cursor": cursor, "limit": limit}) return self.api_call("apps.event.authorizations.list", params=kwargs) def apps_uninstall( self, *, client_id: str, client_secret: str, **kwargs, ) -> SlackResponse: """Uninstalls your app from a workspace. https://docs.slack.dev/reference/methods/apps.uninstall """ kwargs.update({"client_id": client_id, "client_secret": client_secret}) return self.api_call("apps.uninstall", params=kwargs) def apps_manifest_create( self, *, manifest: Union[str, Dict[str, Any]], **kwargs, ) -> SlackResponse: """Create an app from an app manifest https://docs.slack.dev/reference/methods/apps.manifest.create """ if isinstance(manifest, str): kwargs.update({"manifest": manifest}) else: kwargs.update({"manifest": json.dumps(manifest)}) return self.api_call("apps.manifest.create", params=kwargs) def apps_manifest_delete( self, *, app_id: str, **kwargs, ) -> SlackResponse: """Permanently deletes an app created through app manifests https://docs.slack.dev/reference/methods/apps.manifest.delete """ kwargs.update({"app_id": app_id}) return self.api_call("apps.manifest.delete", params=kwargs) def apps_manifest_export( self, *, app_id: str, **kwargs, ) -> SlackResponse: """Export an app manifest from an existing app https://docs.slack.dev/reference/methods/apps.manifest.export """ kwargs.update({"app_id": app_id}) return self.api_call("apps.manifest.export", params=kwargs) def apps_manifest_update( self, *, app_id: str, manifest: Union[str, Dict[str, Any]], **kwargs, ) -> SlackResponse: """Update an app from an app manifest https://docs.slack.dev/reference/methods/apps.manifest.update """ if isinstance(manifest, str): kwargs.update({"manifest": manifest}) else: kwargs.update({"manifest": json.dumps(manifest)}) kwargs.update({"app_id": app_id}) return self.api_call("apps.manifest.update", params=kwargs) def apps_manifest_validate( self, *, manifest: Union[str, Dict[str, Any]], app_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Validate an app manifest https://docs.slack.dev/reference/methods/apps.manifest.validate """ if isinstance(manifest, str): kwargs.update({"manifest": manifest}) else: kwargs.update({"manifest": json.dumps(manifest)}) kwargs.update({"app_id": app_id}) return self.api_call("apps.manifest.validate", params=kwargs) def apps_user_connection_update( self, *, user_id: str, status: str, **kwargs, ) -> SlackResponse: """Updates the connection status between a user and an app. https://docs.slack.dev/reference/methods/apps.user.connection.update """ kwargs.update({"user_id": user_id, "status": status}) return self.api_call("apps.user.connection.update", params=kwargs) def tooling_tokens_rotate( self, *, refresh_token: str, **kwargs, ) -> SlackResponse: """Exchanges a refresh token for a new app configuration token https://docs.slack.dev/reference/methods/tooling.tokens.rotate """ kwargs.update({"refresh_token": refresh_token}) return self.api_call("tooling.tokens.rotate", params=kwargs) def assistant_threads_setStatus( self, *, channel_id: str, thread_ts: str, status: str, loading_messages: Optional[List[str]] = None, icon_emoji: Optional[str] = None, icon_url: Optional[str] = None, username: Optional[str] = None, **kwargs, ) -> SlackResponse: """Set the status for an AI assistant thread. https://docs.slack.dev/reference/methods/assistant.threads.setStatus """ kwargs.update( { "channel_id": channel_id, "thread_ts": thread_ts, "status": status, "loading_messages": loading_messages, "icon_emoji": icon_emoji, "icon_url": icon_url, "username": username, } ) kwargs = _remove_none_values(kwargs) return self.api_call("assistant.threads.setStatus", json=kwargs) def assistant_threads_setTitle( self, *, channel_id: str, thread_ts: str, title: str, **kwargs, ) -> SlackResponse: """Set the title for the given assistant thread. https://docs.slack.dev/reference/methods/assistant.threads.setTitle """ kwargs.update({"channel_id": channel_id, "thread_ts": thread_ts, "title": title}) return self.api_call("assistant.threads.setTitle", params=kwargs) def assistant_threads_setSuggestedPrompts( self, *, channel_id: str, thread_ts: Optional[str] = None, title: Optional[str] = None, prompts: List[Dict[str, str]], **kwargs, ) -> SlackResponse: """Set suggested prompts for the given assistant thread. https://docs.slack.dev/reference/methods/assistant.threads.setSuggestedPrompts """ kwargs.update({"channel_id": channel_id, "prompts": prompts}) if thread_ts is not None: kwargs.update({"thread_ts": thread_ts}) if title is not None: kwargs.update({"title": title}) return self.api_call("assistant.threads.setSuggestedPrompts", json=kwargs) def auth_revoke( self, *, test: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Revokes a token. https://docs.slack.dev/reference/methods/auth.revoke """ kwargs.update({"test": test}) return self.api_call("auth.revoke", http_verb="GET", params=kwargs) def auth_test( self, **kwargs, ) -> SlackResponse: """Checks authentication & identity. https://docs.slack.dev/reference/methods/auth.test """ return self.api_call("auth.test", params=kwargs) def auth_teams_list( self, cursor: Optional[str] = None, limit: Optional[int] = None, include_icon: Optional[bool] = None, **kwargs, ) -> SlackResponse: """List the workspaces a token can access. https://docs.slack.dev/reference/methods/auth.teams.list """ kwargs.update({"cursor": cursor, "limit": limit, "include_icon": include_icon}) return self.api_call("auth.teams.list", params=kwargs) def bookmarks_add( self, *, channel_id: str, title: str, type: str, emoji: Optional[str] = None, entity_id: Optional[str] = None, link: Optional[str] = None, # include when type is 'link' parent_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Add bookmark to a channel. https://docs.slack.dev/reference/methods/bookmarks.add """ kwargs.update( { "channel_id": channel_id, "title": title, "type": type, "emoji": emoji, "entity_id": entity_id, "link": link, "parent_id": parent_id, } ) return self.api_call("bookmarks.add", http_verb="POST", params=kwargs) def bookmarks_edit( self, *, bookmark_id: str, channel_id: str, emoji: Optional[str] = None, link: Optional[str] = None, title: Optional[str] = None, **kwargs, ) -> SlackResponse: """Edit bookmark. https://docs.slack.dev/reference/methods/bookmarks.edit """ kwargs.update( { "bookmark_id": bookmark_id, "channel_id": channel_id, "emoji": emoji, "link": link, "title": title, } ) return self.api_call("bookmarks.edit", http_verb="POST", params=kwargs) def bookmarks_list( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """List bookmark for the channel. https://docs.slack.dev/reference/methods/bookmarks.list """ kwargs.update({"channel_id": channel_id}) return self.api_call("bookmarks.list", http_verb="POST", params=kwargs) def bookmarks_remove( self, *, bookmark_id: str, channel_id: str, **kwargs, ) -> SlackResponse: """Remove bookmark from the channel. https://docs.slack.dev/reference/methods/bookmarks.remove """ kwargs.update({"bookmark_id": bookmark_id, "channel_id": channel_id}) return self.api_call("bookmarks.remove", http_verb="POST", params=kwargs) def bots_info( self, *, bot: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Gets information about a bot user. https://docs.slack.dev/reference/methods/bots.info """ kwargs.update({"bot": bot, "team_id": team_id}) return self.api_call("bots.info", http_verb="GET", params=kwargs) def calls_add( self, *, external_unique_id: str, join_url: str, created_by: Optional[str] = None, date_start: Optional[int] = None, desktop_app_join_url: Optional[str] = None, external_display_id: Optional[str] = None, title: Optional[str] = None, users: Optional[Union[str, Sequence[Dict[str, str]]]] = None, **kwargs, ) -> SlackResponse: """Registers a new Call. https://docs.slack.dev/reference/methods/calls.add """ kwargs.update( { "external_unique_id": external_unique_id, "join_url": join_url, "created_by": created_by, "date_start": date_start, "desktop_app_join_url": desktop_app_join_url, "external_display_id": external_display_id, "title": title, } ) _update_call_participants( kwargs, users if users is not None else kwargs.get("users"), # type: ignore[arg-type] ) return self.api_call("calls.add", http_verb="POST", params=kwargs) def calls_end( self, *, id: str, duration: Optional[int] = None, **kwargs, ) -> SlackResponse: """Ends a Call. https://docs.slack.dev/reference/methods/calls.end """ kwargs.update({"id": id, "duration": duration}) return self.api_call("calls.end", http_verb="POST", params=kwargs) def calls_info( self, *, id: str, **kwargs, ) -> SlackResponse: """Returns information about a Call. https://docs.slack.dev/reference/methods/calls.info """ kwargs.update({"id": id}) return self.api_call("calls.info", http_verb="POST", params=kwargs) def calls_participants_add( self, *, id: str, users: Union[str, Sequence[Dict[str, str]]], **kwargs, ) -> SlackResponse: """Registers new participants added to a Call. https://docs.slack.dev/reference/methods/calls.participants.add """ kwargs.update({"id": id}) _update_call_participants(kwargs, users) return self.api_call("calls.participants.add", http_verb="POST", params=kwargs) def calls_participants_remove( self, *, id: str, users: Union[str, Sequence[Dict[str, str]]], **kwargs, ) -> SlackResponse: """Registers participants removed from a Call. https://docs.slack.dev/reference/methods/calls.participants.remove """ kwargs.update({"id": id}) _update_call_participants(kwargs, users) return self.api_call("calls.participants.remove", http_verb="POST", params=kwargs) def calls_update( self, *, id: str, desktop_app_join_url: Optional[str] = None, join_url: Optional[str] = None, title: Optional[str] = None, **kwargs, ) -> SlackResponse: """Updates information about a Call. https://docs.slack.dev/reference/methods/calls.update """ kwargs.update( { "id": id, "desktop_app_join_url": desktop_app_join_url, "join_url": join_url, "title": title, } ) return self.api_call("calls.update", http_verb="POST", params=kwargs) def canvases_create( self, *, title: Optional[str] = None, document_content: Dict[str, str], **kwargs, ) -> SlackResponse: """Create Canvas for a user https://docs.slack.dev/reference/methods/canvases.create """ kwargs.update({"title": title, "document_content": document_content}) return self.api_call("canvases.create", json=kwargs) def canvases_edit( self, *, canvas_id: str, changes: Sequence[Dict[str, Any]], **kwargs, ) -> SlackResponse: """Update an existing canvas https://docs.slack.dev/reference/methods/canvases.edit """ kwargs.update({"canvas_id": canvas_id, "changes": changes}) return self.api_call("canvases.edit", json=kwargs) def canvases_delete( self, *, canvas_id: str, **kwargs, ) -> SlackResponse: """Deletes a canvas https://docs.slack.dev/reference/methods/canvases.delete """ kwargs.update({"canvas_id": canvas_id}) return self.api_call("canvases.delete", params=kwargs) def canvases_access_set( self, *, canvas_id: str, access_level: str, channel_ids: Optional[Union[Sequence[str], str]] = None, user_ids: Optional[Union[Sequence[str], str]] = None, **kwargs, ) -> SlackResponse: """Sets the access level to a canvas for specified entities https://docs.slack.dev/reference/methods/canvases.access.set """ kwargs.update({"canvas_id": canvas_id, "access_level": access_level}) if channel_ids is not None: if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) if user_ids is not None: if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) return self.api_call("canvases.access.set", params=kwargs) def canvases_access_delete( self, *, canvas_id: str, channel_ids: Optional[Union[Sequence[str], str]] = None, user_ids: Optional[Union[Sequence[str], str]] = None, **kwargs, ) -> SlackResponse: """Create a Channel Canvas for a channel https://docs.slack.dev/reference/methods/canvases.access.delete """ kwargs.update({"canvas_id": canvas_id}) if channel_ids is not None: if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) if user_ids is not None: if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) return self.api_call("canvases.access.delete", params=kwargs) def canvases_sections_lookup( self, *, canvas_id: str, criteria: Dict[str, Any], **kwargs, ) -> SlackResponse: """Find sections matching the provided criteria https://docs.slack.dev/reference/methods/canvases.sections.lookup """ kwargs.update({"canvas_id": canvas_id, "criteria": json.dumps(criteria)}) return self.api_call("canvases.sections.lookup", params=kwargs) # -------------------------- # Deprecated: channels.* # You can use conversations.* APIs instead. # https://docs.slack.dev/changelog/2020-01-deprecating-antecedents-to-the-conversations-api/ # -------------------------- def channels_archive( self, *, channel: str, **kwargs, ) -> SlackResponse: """Archives a channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.archive", json=kwargs) def channels_create( self, *, name: str, **kwargs, ) -> SlackResponse: """Creates a channel.""" kwargs.update({"name": name}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.create", json=kwargs) def channels_history( self, *, channel: str, **kwargs, ) -> SlackResponse: """Fetches history of messages and events from a channel.""" kwargs.update({"channel": channel}) return self.api_call("channels.history", http_verb="GET", params=kwargs) def channels_info( self, *, channel: str, **kwargs, ) -> SlackResponse: """Gets information about a channel.""" kwargs.update({"channel": channel}) return self.api_call("channels.info", http_verb="GET", params=kwargs) def channels_invite( self, *, channel: str, user: str, **kwargs, ) -> SlackResponse: """Invites a user to a channel.""" kwargs.update({"channel": channel, "user": user}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.invite", json=kwargs) def channels_join( self, *, name: str, **kwargs, ) -> SlackResponse: """Joins a channel, creating it if needed.""" kwargs.update({"name": name}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.join", json=kwargs) def channels_kick( self, *, channel: str, user: str, **kwargs, ) -> SlackResponse: """Removes a user from a channel.""" kwargs.update({"channel": channel, "user": user}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.kick", json=kwargs) def channels_leave( self, *, channel: str, **kwargs, ) -> SlackResponse: """Leaves a channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.leave", json=kwargs) def channels_list( self, **kwargs, ) -> SlackResponse: """Lists all channels in a Slack team.""" return self.api_call("channels.list", http_verb="GET", params=kwargs) def channels_mark( self, *, channel: str, ts: str, **kwargs, ) -> SlackResponse: """Sets the read cursor in a channel.""" kwargs.update({"channel": channel, "ts": ts}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.mark", json=kwargs) def channels_rename( self, *, channel: str, name: str, **kwargs, ) -> SlackResponse: """Renames a channel.""" kwargs.update({"channel": channel, "name": name}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.rename", json=kwargs) def channels_replies( self, *, channel: str, thread_ts: str, **kwargs, ) -> SlackResponse: """Retrieve a thread of messages posted to a channel""" kwargs.update({"channel": channel, "thread_ts": thread_ts}) return self.api_call("channels.replies", http_verb="GET", params=kwargs) def channels_setPurpose( self, *, channel: str, purpose: str, **kwargs, ) -> SlackResponse: """Sets the purpose for a channel.""" kwargs.update({"channel": channel, "purpose": purpose}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.setPurpose", json=kwargs) def channels_setTopic( self, *, channel: str, topic: str, **kwargs, ) -> SlackResponse: """Sets the topic for a channel.""" kwargs.update({"channel": channel, "topic": topic}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.setTopic", json=kwargs) def channels_unarchive( self, *, channel: str, **kwargs, ) -> SlackResponse: """Unarchives a channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.unarchive", json=kwargs) # -------------------------- def chat_appendStream( self, *, channel: str, ts: str, markdown_text: Optional[str] = None, chunks: Optional[Sequence[Union[Dict, Chunk]]] = None, **kwargs, ) -> SlackResponse: """Appends text to an existing streaming conversation. https://docs.slack.dev/reference/methods/chat.appendStream """ kwargs.update( { "channel": channel, "ts": ts, "markdown_text": markdown_text, "chunks": chunks, } ) _parse_web_class_objects(kwargs) kwargs = _remove_none_values(kwargs) return self.api_call("chat.appendStream", json=kwargs) def chat_delete( self, *, channel: str, ts: str, as_user: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Deletes a message. https://docs.slack.dev/reference/methods/chat.delete """ kwargs.update({"channel": channel, "ts": ts, "as_user": as_user}) return self.api_call("chat.delete", params=kwargs) def chat_deleteScheduledMessage( self, *, channel: str, scheduled_message_id: str, as_user: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Deletes a scheduled message. https://docs.slack.dev/reference/methods/chat.deleteScheduledMessage """ kwargs.update( { "channel": channel, "scheduled_message_id": scheduled_message_id, "as_user": as_user, } ) return self.api_call("chat.deleteScheduledMessage", params=kwargs) def chat_getPermalink( self, *, channel: str, message_ts: str, **kwargs, ) -> SlackResponse: """Retrieve a permalink URL for a specific extant message https://docs.slack.dev/reference/methods/chat.getPermalink """ kwargs.update({"channel": channel, "message_ts": message_ts}) return self.api_call("chat.getPermalink", http_verb="GET", params=kwargs) def chat_meMessage( self, *, channel: str, text: str, **kwargs, ) -> SlackResponse: """Share a me message into a channel. https://docs.slack.dev/reference/methods/chat.meMessage """ kwargs.update({"channel": channel, "text": text}) return self.api_call("chat.meMessage", params=kwargs) def chat_postEphemeral( self, *, channel: str, user: str, text: Optional[str] = None, as_user: Optional[bool] = None, attachments: Optional[Union[str, Sequence[Union[Dict, Attachment]]]] = None, blocks: Optional[Union[str, Sequence[Union[Dict, Block]]]] = None, thread_ts: Optional[str] = None, icon_emoji: Optional[str] = None, icon_url: Optional[str] = None, link_names: Optional[bool] = None, username: Optional[str] = None, parse: Optional[str] = None, markdown_text: Optional[str] = None, **kwargs, ) -> SlackResponse: """Sends an ephemeral message to a user in a channel. https://docs.slack.dev/reference/methods/chat.postEphemeral """ kwargs.update( { "channel": channel, "user": user, "text": text, "as_user": as_user, "attachments": attachments, "blocks": blocks, "thread_ts": thread_ts, "icon_emoji": icon_emoji, "icon_url": icon_url, "link_names": link_names, "username": username, "parse": parse, "markdown_text": markdown_text, } ) _parse_web_class_objects(kwargs) kwargs = _remove_none_values(kwargs) _warn_if_message_text_content_is_missing("chat.postEphemeral", kwargs) # NOTE: intentionally using json over params for the API methods using blocks/attachments return self.api_call("chat.postEphemeral", json=kwargs) def chat_postMessage( self, *, channel: str, text: Optional[str] = None, as_user: Optional[bool] = None, attachments: Optional[Union[str, Sequence[Union[Dict, Attachment]]]] = None, blocks: Optional[Union[str, Sequence[Union[Dict, Block]]]] = None, thread_ts: Optional[str] = None, reply_broadcast: Optional[bool] = None, unfurl_links: Optional[bool] = None, unfurl_media: Optional[bool] = None, container_id: Optional[str] = None, icon_emoji: Optional[str] = None, icon_url: Optional[str] = None, mrkdwn: Optional[bool] = None, link_names: Optional[bool] = None, username: Optional[str] = None, parse: Optional[str] = None, # none, full metadata: Optional[Union[Dict, Metadata, EventAndEntityMetadata]] = None, markdown_text: Optional[str] = None, **kwargs, ) -> SlackResponse: """Sends a message to a channel. https://docs.slack.dev/reference/methods/chat.postMessage """ kwargs.update( { "channel": channel, "text": text, "as_user": as_user, "attachments": attachments, "blocks": blocks, "thread_ts": thread_ts, "reply_broadcast": reply_broadcast, "unfurl_links": unfurl_links, "unfurl_media": unfurl_media, "container_id": container_id, "icon_emoji": icon_emoji, "icon_url": icon_url, "mrkdwn": mrkdwn, "link_names": link_names, "username": username, "parse": parse, "metadata": metadata, "markdown_text": markdown_text, } ) _parse_web_class_objects(kwargs) kwargs = _remove_none_values(kwargs) _warn_if_message_text_content_is_missing("chat.postMessage", kwargs) # NOTE: intentionally using json over params for the API methods using blocks/attachments return self.api_call("chat.postMessage", json=kwargs) def chat_scheduleMessage( self, *, channel: str, post_at: Union[str, int], text: Optional[str] = None, as_user: Optional[bool] = None, attachments: Optional[Union[str, Sequence[Union[Dict, Attachment]]]] = None, blocks: Optional[Union[str, Sequence[Union[Dict, Block]]]] = None, thread_ts: Optional[str] = None, parse: Optional[str] = None, reply_broadcast: Optional[bool] = None, unfurl_links: Optional[bool] = None, unfurl_media: Optional[bool] = None, link_names: Optional[bool] = None, metadata: Optional[Union[Dict, Metadata]] = None, markdown_text: Optional[str] = None, **kwargs, ) -> SlackResponse: """Schedules a message. https://docs.slack.dev/reference/methods/chat.scheduleMessage """ kwargs.update( { "channel": channel, "post_at": post_at, "text": text, "as_user": as_user, "attachments": attachments, "blocks": blocks, "thread_ts": thread_ts, "reply_broadcast": reply_broadcast, "parse": parse, "unfurl_links": unfurl_links, "unfurl_media": unfurl_media, "link_names": link_names, "metadata": metadata, "markdown_text": markdown_text, } ) _parse_web_class_objects(kwargs) kwargs = _remove_none_values(kwargs) _warn_if_message_text_content_is_missing("chat.scheduleMessage", kwargs) # NOTE: intentionally using json over params for the API methods using blocks/attachments return self.api_call("chat.scheduleMessage", json=kwargs) def chat_scheduledMessages_list( self, *, channel: Optional[str] = None, cursor: Optional[str] = None, latest: Optional[str] = None, limit: Optional[int] = None, oldest: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists all scheduled messages. https://docs.slack.dev/reference/methods/chat.scheduledMessages.list """ kwargs.update( { "channel": channel, "cursor": cursor, "latest": latest, "limit": limit, "oldest": oldest, "team_id": team_id, } ) return self.api_call("chat.scheduledMessages.list", params=kwargs) def chat_startStream( self, *, channel: str, thread_ts: str, markdown_text: Optional[str] = None, recipient_team_id: Optional[str] = None, recipient_user_id: Optional[str] = None, chunks: Optional[Sequence[Union[Dict, Chunk]]] = None, task_display_mode: Optional[str] = None, # timeline, plan icon_emoji: Optional[str] = None, icon_url: Optional[str] = None, username: Optional[str] = None, **kwargs, ) -> SlackResponse: """Starts a new streaming conversation. https://docs.slack.dev/reference/methods/chat.startStream """ kwargs.update( { "channel": channel, "thread_ts": thread_ts, "markdown_text": markdown_text, "recipient_team_id": recipient_team_id, "recipient_user_id": recipient_user_id, "chunks": chunks, "task_display_mode": task_display_mode, "icon_emoji": icon_emoji, "icon_url": icon_url, "username": username, } ) _parse_web_class_objects(kwargs) kwargs = _remove_none_values(kwargs) return self.api_call("chat.startStream", json=kwargs) def chat_stopStream( self, *, channel: str, ts: str, markdown_text: Optional[str] = None, blocks: Optional[Union[str, Sequence[Union[Dict, Block]]]] = None, metadata: Optional[Union[Dict, Metadata]] = None, chunks: Optional[Sequence[Union[Dict, Chunk]]] = None, **kwargs, ) -> SlackResponse: """Stops a streaming conversation. https://docs.slack.dev/reference/methods/chat.stopStream """ kwargs.update( { "channel": channel, "ts": ts, "markdown_text": markdown_text, "blocks": blocks, "metadata": metadata, "chunks": chunks, } ) _parse_web_class_objects(kwargs) kwargs = _remove_none_values(kwargs) return self.api_call("chat.stopStream", json=kwargs) def chat_stream( self, *, buffer_size: int = 256, channel: str, thread_ts: str, recipient_team_id: Optional[str] = None, recipient_user_id: Optional[str] = None, task_display_mode: Optional[str] = None, icon_emoji: Optional[str] = None, icon_url: Optional[str] = None, username: Optional[str] = None, **kwargs, ) -> ChatStream: """Stream markdown text into a conversation. This method starts a new chat stream in a conversation that can be appended to. After appending an entire message, the stream can be stopped with concluding arguments such as "blocks" for gathering feedback. The following methods are used: - chat.startStream: Starts a new streaming conversation. [Reference](https://docs.slack.dev/reference/methods/chat.startStream). - chat.appendStream: Appends text to an existing streaming conversation. [Reference](https://docs.slack.dev/reference/methods/chat.appendStream). - chat.stopStream: Stops a streaming conversation. [Reference](https://docs.slack.dev/reference/methods/chat.stopStream). Args: buffer_size: The length of markdown_text to buffer in-memory before calling a stream method. Increasing this value decreases the number of method calls made for the same amount of text, which is useful to avoid rate limits. Default: 256. channel: An encoded ID that represents a channel, private group, or DM. thread_ts: Provide another message's ts value to reply to. Streamed messages should always be replies to a user request. recipient_team_id: The encoded ID of the team the user receiving the streaming text belongs to. Required when streaming to channels. recipient_user_id: The encoded ID of the user to receive the streaming text. Required when streaming to channels. task_display_mode: Specifies how tasks are displayed in the message. A "timeline" displays individual tasks with text and "plan" displays all tasks together. icon_emoji: Emoji to use as the icon for this message. Overrides icon_url. icon_url: Image URL to use as the icon for this message. username: The bot's username to display. **kwargs: Additional arguments passed to the underlying API calls. Returns: ChatStream instance for managing the stream Example: ```python streamer = client.chat_stream( channel="C0123456789", thread_ts="1700000001.123456", recipient_team_id="T0123456789", recipient_user_id="U0123456789", ) streamer.append(markdown_text="**hello wo") streamer.append(markdown_text="rld!**") streamer.stop() ``` """ return ChatStream( self, logger=self._logger, channel=channel, thread_ts=thread_ts, recipient_team_id=recipient_team_id, recipient_user_id=recipient_user_id, task_display_mode=task_display_mode, icon_emoji=icon_emoji, icon_url=icon_url, username=username, buffer_size=buffer_size, **kwargs, ) def chat_unfurl( self, *, channel: Optional[str] = None, ts: Optional[str] = None, source: Optional[str] = None, unfurl_id: Optional[str] = None, unfurls: Optional[Dict[str, Dict]] = None, # or user_auth_* metadata: Optional[Union[Dict, EventAndEntityMetadata]] = None, user_auth_blocks: Optional[Union[str, Sequence[Union[Dict, Block]]]] = None, user_auth_message: Optional[str] = None, user_auth_required: Optional[bool] = None, user_auth_url: Optional[str] = None, **kwargs, ) -> SlackResponse: """Provide custom unfurl behavior for user-posted URLs. https://docs.slack.dev/reference/methods/chat.unfurl """ kwargs.update( { "channel": channel, "ts": ts, "source": source, "unfurl_id": unfurl_id, "unfurls": unfurls, "metadata": metadata, "user_auth_blocks": user_auth_blocks, "user_auth_message": user_auth_message, "user_auth_required": user_auth_required, "user_auth_url": user_auth_url, } ) _parse_web_class_objects(kwargs) # for user_auth_blocks kwargs = _remove_none_values(kwargs) # NOTE: intentionally using json over params for API methods using blocks/attachments return self.api_call("chat.unfurl", json=kwargs) def chat_update( self, *, channel: str, ts: str, text: Optional[str] = None, attachments: Optional[Union[str, Sequence[Union[Dict, Attachment]]]] = None, blocks: Optional[Union[str, Sequence[Union[Dict, Block]]]] = None, as_user: Optional[bool] = None, file_ids: Optional[Union[str, Sequence[str]]] = None, link_names: Optional[bool] = None, parse: Optional[str] = None, # none, full reply_broadcast: Optional[bool] = None, metadata: Optional[Union[Dict, Metadata]] = None, markdown_text: Optional[str] = None, **kwargs, ) -> SlackResponse: """Updates a message in a channel. https://docs.slack.dev/reference/methods/chat.update """ kwargs.update( { "channel": channel, "ts": ts, "text": text, "attachments": attachments, "blocks": blocks, "as_user": as_user, "link_names": link_names, "parse": parse, "reply_broadcast": reply_broadcast, "metadata": metadata, "markdown_text": markdown_text, } ) if isinstance(file_ids, (list, tuple)): kwargs.update({"file_ids": ",".join(file_ids)}) else: kwargs.update({"file_ids": file_ids}) _parse_web_class_objects(kwargs) kwargs = _remove_none_values(kwargs) _warn_if_message_text_content_is_missing("chat.update", kwargs) # NOTE: intentionally using json over params for API methods using blocks/attachments return self.api_call("chat.update", json=kwargs) def conversations_acceptSharedInvite( self, *, channel_name: str, channel_id: Optional[str] = None, invite_id: Optional[str] = None, free_trial_accepted: Optional[bool] = None, is_private: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Accepts an invitation to a Slack Connect channel. https://docs.slack.dev/reference/methods/conversations.acceptSharedInvite """ if channel_id is None and invite_id is None: raise e.SlackRequestError("Either channel_id or invite_id must be provided.") kwargs.update( { "channel_name": channel_name, "channel_id": channel_id, "invite_id": invite_id, "free_trial_accepted": free_trial_accepted, "is_private": is_private, "team_id": team_id, } ) return self.api_call("conversations.acceptSharedInvite", http_verb="POST", params=kwargs) def conversations_approveSharedInvite( self, *, invite_id: str, target_team: Optional[str] = None, **kwargs, ) -> SlackResponse: """Approves an invitation to a Slack Connect channel. https://docs.slack.dev/reference/methods/conversations.approveSharedInvite """ kwargs.update({"invite_id": invite_id, "target_team": target_team}) return self.api_call("conversations.approveSharedInvite", http_verb="POST", params=kwargs) def conversations_archive( self, *, channel: str, **kwargs, ) -> SlackResponse: """Archives a conversation. https://docs.slack.dev/reference/methods/conversations.archive """ kwargs.update({"channel": channel}) return self.api_call("conversations.archive", params=kwargs) def conversations_close( self, *, channel: str, **kwargs, ) -> SlackResponse: """Closes a direct message or multi-person direct message. https://docs.slack.dev/reference/methods/conversations.close """ kwargs.update({"channel": channel}) return self.api_call("conversations.close", params=kwargs) def conversations_create( self, *, name: str, is_private: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Initiates a public or private channel-based conversation https://docs.slack.dev/reference/methods/conversations.create """ kwargs.update({"name": name, "is_private": is_private, "team_id": team_id}) return self.api_call("conversations.create", params=kwargs) def conversations_declineSharedInvite( self, *, invite_id: str, target_team: Optional[str] = None, **kwargs, ) -> SlackResponse: """Declines a Slack Connect channel invite. https://docs.slack.dev/reference/methods/conversations.declineSharedInvite """ kwargs.update({"invite_id": invite_id, "target_team": target_team}) return self.api_call("conversations.declineSharedInvite", http_verb="GET", params=kwargs) def conversations_externalInvitePermissions_set( self, *, action: str, channel: str, target_team: str, **kwargs ) -> SlackResponse: """Sets a team in a shared External Limited channel to a shared Slack Connect channel or vice versa. https://docs.slack.dev/reference/methods/conversations.externalInvitePermissions.set """ kwargs.update( { "action": action, "channel": channel, "target_team": target_team, } ) return self.api_call("conversations.externalInvitePermissions.set", params=kwargs) def conversations_history( self, *, channel: str, cursor: Optional[str] = None, inclusive: Optional[bool] = None, include_all_metadata: Optional[bool] = None, latest: Optional[str] = None, limit: Optional[int] = None, oldest: Optional[str] = None, **kwargs, ) -> SlackResponse: """Fetches a conversation's history of messages and events. https://docs.slack.dev/reference/methods/conversations.history """ kwargs.update( { "channel": channel, "cursor": cursor, "inclusive": inclusive, "include_all_metadata": include_all_metadata, "limit": limit, "latest": latest, "oldest": oldest, } ) return self.api_call("conversations.history", http_verb="GET", params=kwargs) def conversations_info( self, *, channel: str, include_locale: Optional[bool] = None, include_num_members: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Retrieve information about a conversation. https://docs.slack.dev/reference/methods/conversations.info """ kwargs.update( { "channel": channel, "include_locale": include_locale, "include_num_members": include_num_members, } ) return self.api_call("conversations.info", http_verb="GET", params=kwargs) def conversations_invite( self, *, channel: str, users: Union[str, Sequence[str]], force: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Invites users to a channel. https://docs.slack.dev/reference/methods/conversations.invite """ kwargs.update( { "channel": channel, "force": force, } ) if isinstance(users, (list, tuple)): kwargs.update({"users": ",".join(users)}) else: kwargs.update({"users": users}) return self.api_call("conversations.invite", params=kwargs) def conversations_inviteShared( self, *, channel: str, emails: Optional[Union[str, Sequence[str]]] = None, user_ids: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """Sends an invitation to a Slack Connect channel. https://docs.slack.dev/reference/methods/conversations.inviteShared """ if emails is None and user_ids is None: raise e.SlackRequestError("Either emails or user ids must be provided.") kwargs.update({"channel": channel}) if isinstance(emails, (list, tuple)): kwargs.update({"emails": ",".join(emails)}) else: kwargs.update({"emails": emails}) if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) return self.api_call("conversations.inviteShared", http_verb="GET", params=kwargs) def conversations_join( self, *, channel: str, **kwargs, ) -> SlackResponse: """Joins an existing conversation. https://docs.slack.dev/reference/methods/conversations.join """ kwargs.update({"channel": channel}) return self.api_call("conversations.join", params=kwargs) def conversations_kick( self, *, channel: str, user: str, **kwargs, ) -> SlackResponse: """Removes a user from a conversation. https://docs.slack.dev/reference/methods/conversations.kick """ kwargs.update({"channel": channel, "user": user}) return self.api_call("conversations.kick", params=kwargs) def conversations_leave( self, *, channel: str, **kwargs, ) -> SlackResponse: """Leaves a conversation. https://docs.slack.dev/reference/methods/conversations.leave """ kwargs.update({"channel": channel}) return self.api_call("conversations.leave", params=kwargs) def conversations_list( self, *, cursor: Optional[str] = None, exclude_archived: Optional[bool] = None, limit: Optional[int] = None, team_id: Optional[str] = None, types: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """Lists all channels in a Slack team. https://docs.slack.dev/reference/methods/conversations.list """ kwargs.update( { "cursor": cursor, "exclude_archived": exclude_archived, "limit": limit, "team_id": team_id, } ) if isinstance(types, (list, tuple)): kwargs.update({"types": ",".join(types)}) else: kwargs.update({"types": types}) return self.api_call("conversations.list", http_verb="GET", params=kwargs) def conversations_listConnectInvites( self, *, count: Optional[int] = None, cursor: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List shared channel invites that have been generated or received but have not yet been approved by all parties. https://docs.slack.dev/reference/methods/conversations.listConnectInvites """ kwargs.update({"count": count, "cursor": cursor, "team_id": team_id}) return self.api_call("conversations.listConnectInvites", params=kwargs) def conversations_mark( self, *, channel: str, ts: str, **kwargs, ) -> SlackResponse: """Sets the read cursor in a channel. https://docs.slack.dev/reference/methods/conversations.mark """ kwargs.update({"channel": channel, "ts": ts}) return self.api_call("conversations.mark", params=kwargs) def conversations_members( self, *, channel: str, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Retrieve members of a conversation. https://docs.slack.dev/reference/methods/conversations.members """ kwargs.update({"channel": channel, "cursor": cursor, "limit": limit}) return self.api_call("conversations.members", http_verb="GET", params=kwargs) def conversations_open( self, *, channel: Optional[str] = None, return_im: Optional[bool] = None, users: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """Opens or resumes a direct message or multi-person direct message. https://docs.slack.dev/reference/methods/conversations.open """ if channel is None and users is None: raise e.SlackRequestError("Either channel or users must be provided.") kwargs.update({"channel": channel, "return_im": return_im}) if isinstance(users, (list, tuple)): kwargs.update({"users": ",".join(users)}) else: kwargs.update({"users": users}) return self.api_call("conversations.open", params=kwargs) def conversations_rename( self, *, channel: str, name: str, **kwargs, ) -> SlackResponse: """Renames a conversation. https://docs.slack.dev/reference/methods/conversations.rename """ kwargs.update({"channel": channel, "name": name}) return self.api_call("conversations.rename", params=kwargs) def conversations_replies( self, *, channel: str, ts: str, cursor: Optional[str] = None, inclusive: Optional[bool] = None, include_all_metadata: Optional[bool] = None, latest: Optional[str] = None, limit: Optional[int] = None, oldest: Optional[str] = None, **kwargs, ) -> SlackResponse: """Retrieve a thread of messages posted to a conversation https://docs.slack.dev/reference/methods/conversations.replies """ kwargs.update( { "channel": channel, "ts": ts, "cursor": cursor, "inclusive": inclusive, "include_all_metadata": include_all_metadata, "limit": limit, "latest": latest, "oldest": oldest, } ) return self.api_call("conversations.replies", http_verb="GET", params=kwargs) def conversations_requestSharedInvite_approve( self, *, invite_id: str, channel_id: Optional[str] = None, is_external_limited: Optional[str] = None, message: Optional[Dict[str, Any]] = None, **kwargs, ) -> SlackResponse: """Approve a request to add an external user to a channel. This also sends them a Slack Connect invite. https://docs.slack.dev/reference/methods/conversations.requestSharedInvite.approve """ kwargs.update( { "invite_id": invite_id, "channel_id": channel_id, "is_external_limited": is_external_limited, } ) if message is not None: kwargs.update({"message": json.dumps(message)}) return self.api_call("conversations.requestSharedInvite.approve", params=kwargs) def conversations_requestSharedInvite_deny( self, *, invite_id: str, message: Optional[str] = None, **kwargs, ) -> SlackResponse: """Deny a request to invite an external user to a channel. https://docs.slack.dev/reference/methods/conversations.requestSharedInvite.deny """ kwargs.update({"invite_id": invite_id, "message": message}) return self.api_call("conversations.requestSharedInvite.deny", params=kwargs) def conversations_requestSharedInvite_list( self, *, cursor: Optional[str] = None, include_approved: Optional[bool] = None, include_denied: Optional[bool] = None, include_expired: Optional[bool] = None, invite_ids: Optional[Union[str, Sequence[str]]] = None, limit: Optional[int] = None, user_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists requests to add external users to channels with ability to filter. https://docs.slack.dev/reference/methods/conversations.requestSharedInvite.list """ kwargs.update( { "cursor": cursor, "include_approved": include_approved, "include_denied": include_denied, "include_expired": include_expired, "limit": limit, "user_id": user_id, } ) if invite_ids is not None: if isinstance(invite_ids, (list, tuple)): kwargs.update({"invite_ids": ",".join(invite_ids)}) else: kwargs.update({"invite_ids": invite_ids}) return self.api_call("conversations.requestSharedInvite.list", params=kwargs) def conversations_setPurpose( self, *, channel: str, purpose: str, **kwargs, ) -> SlackResponse: """Sets the purpose for a conversation. https://docs.slack.dev/reference/methods/conversations.setPurpose """ kwargs.update({"channel": channel, "purpose": purpose}) return self.api_call("conversations.setPurpose", params=kwargs) def conversations_setTopic( self, *, channel: str, topic: str, **kwargs, ) -> SlackResponse: """Sets the topic for a conversation. https://docs.slack.dev/reference/methods/conversations.setTopic """ kwargs.update({"channel": channel, "topic": topic}) return self.api_call("conversations.setTopic", params=kwargs) def conversations_unarchive( self, *, channel: str, **kwargs, ) -> SlackResponse: """Reverses conversation archival. https://docs.slack.dev/reference/methods/conversations.unarchive """ kwargs.update({"channel": channel}) return self.api_call("conversations.unarchive", params=kwargs) def conversations_canvases_create( self, *, channel_id: str, document_content: Dict[str, str], **kwargs, ) -> SlackResponse: """Create a Channel Canvas for a channel https://docs.slack.dev/reference/methods/conversations.canvases.create """ kwargs.update({"channel_id": channel_id, "document_content": document_content}) return self.api_call("conversations.canvases.create", json=kwargs) def dialog_open( self, *, dialog: Dict[str, Any], trigger_id: str, **kwargs, ) -> SlackResponse: """Open a dialog with a user. https://docs.slack.dev/reference/methods/dialog.open """ kwargs.update({"dialog": dialog, "trigger_id": trigger_id}) kwargs = _remove_none_values(kwargs) # NOTE: As the dialog can be a dict, this API call works only with json format. return self.api_call("dialog.open", json=kwargs) def dnd_endDnd( self, **kwargs, ) -> SlackResponse: """Ends the current user's Do Not Disturb session immediately. https://docs.slack.dev/reference/methods/dnd.endDnd """ return self.api_call("dnd.endDnd", params=kwargs) def dnd_endSnooze( self, **kwargs, ) -> SlackResponse: """Ends the current user's snooze mode immediately. https://docs.slack.dev/reference/methods/dnd.endSnooze """ return self.api_call("dnd.endSnooze", params=kwargs) def dnd_info( self, *, team_id: Optional[str] = None, user: Optional[str] = None, **kwargs, ) -> SlackResponse: """Retrieves a user's current Do Not Disturb status. https://docs.slack.dev/reference/methods/dnd.info """ kwargs.update({"team_id": team_id, "user": user}) return self.api_call("dnd.info", http_verb="GET", params=kwargs) def dnd_setSnooze( self, *, num_minutes: Union[int, str], **kwargs, ) -> SlackResponse: """Turns on Do Not Disturb mode for the current user, or changes its duration. https://docs.slack.dev/reference/methods/dnd.setSnooze """ kwargs.update({"num_minutes": num_minutes}) return self.api_call("dnd.setSnooze", http_verb="GET", params=kwargs) def dnd_teamInfo( self, users: Union[str, Sequence[str]], team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Retrieves the Do Not Disturb status for users on a team. https://docs.slack.dev/reference/methods/dnd.teamInfo """ if isinstance(users, (list, tuple)): kwargs.update({"users": ",".join(users)}) else: kwargs.update({"users": users}) kwargs.update({"team_id": team_id}) return self.api_call("dnd.teamInfo", http_verb="GET", params=kwargs) def emoji_list( self, include_categories: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Lists custom emoji for a team. https://docs.slack.dev/reference/methods/emoji.list """ kwargs.update({"include_categories": include_categories}) return self.api_call("emoji.list", http_verb="GET", params=kwargs) def entity_presentDetails( self, trigger_id: str, metadata: Optional[Union[Dict, EntityMetadata]] = None, user_auth_required: Optional[bool] = None, user_auth_url: Optional[str] = None, error: Optional[Dict[str, Any]] = None, **kwargs, ) -> SlackResponse: """Provides entity details for the flexpane. https://docs.slack.dev/reference/methods/entity.presentDetails/ """ kwargs.update({"trigger_id": trigger_id}) if metadata is not None: kwargs.update({"metadata": metadata}) if user_auth_required is not None: kwargs.update({"user_auth_required": user_auth_required}) if user_auth_url is not None: kwargs.update({"user_auth_url": user_auth_url}) if error is not None: kwargs.update({"error": error}) _parse_web_class_objects(kwargs) return self.api_call("entity.presentDetails", json=kwargs) def files_comments_delete( self, *, file: str, id: str, **kwargs, ) -> SlackResponse: """Deletes an existing comment on a file. https://docs.slack.dev/reference/methods/files.comments.delete """ kwargs.update({"file": file, "id": id}) return self.api_call("files.comments.delete", params=kwargs) def files_delete( self, *, file: str, **kwargs, ) -> SlackResponse: """Deletes a file. https://docs.slack.dev/reference/methods/files.delete """ kwargs.update({"file": file}) return self.api_call("files.delete", params=kwargs) def files_info( self, *, file: str, count: Optional[int] = None, cursor: Optional[str] = None, limit: Optional[int] = None, page: Optional[int] = None, **kwargs, ) -> SlackResponse: """Gets information about a team file. https://docs.slack.dev/reference/methods/files.info """ kwargs.update( { "file": file, "count": count, "cursor": cursor, "limit": limit, "page": page, } ) return self.api_call("files.info", http_verb="GET", params=kwargs) def files_list( self, *, channel: Optional[str] = None, count: Optional[int] = None, page: Optional[int] = None, show_files_hidden_by_limit: Optional[bool] = None, team_id: Optional[str] = None, ts_from: Optional[str] = None, ts_to: Optional[str] = None, types: Optional[Union[str, Sequence[str]]] = None, user: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists & filters team files. https://docs.slack.dev/reference/methods/files.list """ kwargs.update( { "channel": channel, "count": count, "page": page, "show_files_hidden_by_limit": show_files_hidden_by_limit, "team_id": team_id, "ts_from": ts_from, "ts_to": ts_to, "user": user, } ) if isinstance(types, (list, tuple)): kwargs.update({"types": ",".join(types)}) else: kwargs.update({"types": types}) return self.api_call("files.list", http_verb="GET", params=kwargs) def files_remote_info( self, *, external_id: Optional[str] = None, file: Optional[str] = None, **kwargs, ) -> SlackResponse: """Retrieve information about a remote file added to Slack. https://docs.slack.dev/reference/methods/files.remote.info """ kwargs.update({"external_id": external_id, "file": file}) return self.api_call("files.remote.info", http_verb="GET", params=kwargs) def files_remote_list( self, *, channel: Optional[str] = None, cursor: Optional[str] = None, limit: Optional[int] = None, ts_from: Optional[str] = None, ts_to: Optional[str] = None, **kwargs, ) -> SlackResponse: """Retrieve information about a remote file added to Slack. https://docs.slack.dev/reference/methods/files.remote.list """ kwargs.update( { "channel": channel, "cursor": cursor, "limit": limit, "ts_from": ts_from, "ts_to": ts_to, } ) return self.api_call("files.remote.list", http_verb="GET", params=kwargs) def files_remote_add( self, *, external_id: str, external_url: str, title: str, filetype: Optional[str] = None, indexable_file_contents: Optional[Union[str, bytes, IOBase]] = None, preview_image: Optional[Union[str, bytes, IOBase]] = None, **kwargs, ) -> SlackResponse: """Adds a file from a remote service. https://docs.slack.dev/reference/methods/files.remote.add """ kwargs.update( { "external_id": external_id, "external_url": external_url, "title": title, "filetype": filetype, } ) files = None # preview_image (file): Preview of the document via multipart/form-data. if preview_image is not None or indexable_file_contents is not None: files = { "preview_image": preview_image, "indexable_file_contents": indexable_file_contents, } return self.api_call( # Intentionally using "POST" method over "GET" here "files.remote.add", http_verb="POST", data=kwargs, files=files, ) def files_remote_update( self, *, external_id: Optional[str] = None, external_url: Optional[str] = None, file: Optional[str] = None, title: Optional[str] = None, filetype: Optional[str] = None, indexable_file_contents: Optional[str] = None, preview_image: Optional[str] = None, **kwargs, ) -> SlackResponse: """Updates an existing remote file. https://docs.slack.dev/reference/methods/files.remote.update """ kwargs.update( { "external_id": external_id, "external_url": external_url, "file": file, "title": title, "filetype": filetype, } ) files = None # preview_image (file): Preview of the document via multipart/form-data. if preview_image is not None or indexable_file_contents is not None: files = { "preview_image": preview_image, "indexable_file_contents": indexable_file_contents, } return self.api_call( # Intentionally using "POST" method over "GET" here "files.remote.update", http_verb="POST", data=kwargs, files=files, ) def files_remote_remove( self, *, external_id: Optional[str] = None, file: Optional[str] = None, **kwargs, ) -> SlackResponse: """Remove a remote file. https://docs.slack.dev/reference/methods/files.remote.remove """ kwargs.update({"external_id": external_id, "file": file}) return self.api_call("files.remote.remove", http_verb="POST", params=kwargs) def files_remote_share( self, *, channels: Union[str, Sequence[str]], external_id: Optional[str] = None, file: Optional[str] = None, **kwargs, ) -> SlackResponse: """Share a remote file into a channel. https://docs.slack.dev/reference/methods/files.remote.share """ if external_id is None and file is None: raise e.SlackRequestError("Either external_id or file must be provided.") if isinstance(channels, (list, tuple)): kwargs.update({"channels": ",".join(channels)}) else: kwargs.update({"channels": channels}) kwargs.update({"external_id": external_id, "file": file}) return self.api_call("files.remote.share", http_verb="GET", params=kwargs) def files_revokePublicURL( self, *, file: str, **kwargs, ) -> SlackResponse: """Revokes public/external sharing access for a file https://docs.slack.dev/reference/methods/files.revokePublicURL """ kwargs.update({"file": file}) return self.api_call("files.revokePublicURL", params=kwargs) def files_sharedPublicURL( self, *, file: str, **kwargs, ) -> SlackResponse: """Enables a file for public/external sharing. https://docs.slack.dev/reference/methods/files.sharedPublicURL """ kwargs.update({"file": file}) return self.api_call("files.sharedPublicURL", params=kwargs) def files_upload( self, *, file: Optional[Union[str, bytes, IOBase]] = None, content: Optional[Union[str, bytes]] = None, filename: Optional[str] = None, filetype: Optional[str] = None, initial_comment: Optional[str] = None, thread_ts: Optional[str] = None, title: Optional[str] = None, channels: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """Uploads or creates a file. https://docs.slack.dev/reference/methods/files.upload """ _print_files_upload_v2_suggestion() if file is None and content is None: raise e.SlackRequestError("The file or content argument must be specified.") if file is not None and content is not None: raise e.SlackRequestError("You cannot specify both the file and the content argument.") if isinstance(channels, (list, tuple)): kwargs.update({"channels": ",".join(channels)}) else: kwargs.update({"channels": channels}) kwargs.update( { "filename": filename, "filetype": filetype, "initial_comment": initial_comment, "thread_ts": thread_ts, "title": title, } ) if file: if kwargs.get("filename") is None and isinstance(file, str): # use the local filename if filename is missing if kwargs.get("filename") is None: kwargs["filename"] = file.split(os.path.sep)[-1] return self.api_call("files.upload", files={"file": file}, data=kwargs) else: kwargs["content"] = content return self.api_call("files.upload", data=kwargs) def files_upload_v2( self, *, # for sending a single file filename: Optional[str] = None, # you can skip this only when sending along with content parameter file: Optional[Union[str, bytes, IOBase, os.PathLike]] = None, content: Optional[Union[str, bytes]] = None, title: Optional[str] = None, alt_txt: Optional[str] = None, highlight_type: Optional[str] = None, snippet_type: Optional[str] = None, # To upload multiple files at a time file_uploads: Optional[List[Dict[str, Any]]] = None, channel: Optional[str] = None, channels: Optional[List[str]] = None, initial_comment: Optional[str] = None, thread_ts: Optional[str] = None, request_file_info: bool = True, # since v3.23, this flag is no longer necessary **kwargs, ) -> SlackResponse: """This wrapper method provides an easy way to upload files using the following endpoints: - step1: https://docs.slack.dev/reference/methods/files.getUploadURLExternal - step2: "https://files.slack.com/upload/v1/..." URLs returned from files.getUploadURLExternal API - step3: https://docs.slack.dev/reference/methods/files.completeUploadExternal and https://docs.slack.dev/reference/methods/files.info """ if file is None and content is None and file_uploads is None: raise e.SlackRequestError("Any of file, content, and file_uploads must be specified.") if file is not None and content is not None: raise e.SlackRequestError("You cannot specify both the file and the content argument.") # deprecated arguments: filetype = kwargs.get("filetype") if filetype is not None: warnings.warn("The filetype parameter is no longer supported. Please remove it from the arguments.") # step1: files.getUploadURLExternal per file files: List[Dict[str, Any]] = [] if file_uploads is not None: for f in file_uploads: files.append(_to_v2_file_upload_item(f)) else: f = _to_v2_file_upload_item( { "filename": filename, "file": file, "content": content, "title": title, "alt_txt": alt_txt, "highlight_type": highlight_type, "snippet_type": snippet_type, } ) files.append(f) for f in files: url_response = self.files_getUploadURLExternal( filename=f.get("filename"), # type: ignore[arg-type] length=f.get("length"), # type: ignore[arg-type] alt_txt=f.get("alt_txt"), snippet_type=f.get("snippet_type"), token=kwargs.get("token"), ) _validate_for_legacy_client(url_response) f["file_id"] = url_response.get("file_id") # type: ignore[union-attr, unused-ignore] f["upload_url"] = url_response.get("upload_url") # type: ignore[union-attr, unused-ignore] # step2: "https://files.slack.com/upload/v1/..." per file for f in files: upload_result = self._upload_file( url=f["upload_url"], data=f["data"], logger=self._logger, timeout=self.timeout, proxy=self.proxy, ssl=self.ssl, ) if upload_result.status != 200: status = upload_result.status body = upload_result.body message = ( "Failed to upload a file " f"(status: {status}, body: {body}, filename: {f.get('filename')}, title: {f.get('title')})" ) raise e.SlackRequestError(message) # step3: files.completeUploadExternal with all the sets of (file_id + title) completion = self.files_completeUploadExternal( files=[{"id": f["file_id"], "title": f["title"], "highlight_type": f.get("highlight_type")} for f in files], channel_id=channel, channels=channels, initial_comment=initial_comment, thread_ts=thread_ts, **kwargs, ) if len(completion.get("files")) == 1: # type: ignore[arg-type, union-attr, unused-ignore] completion.data["file"] = completion.get("files")[0] # type: ignore[index, union-attr, unused-ignore] return completion def files_getUploadURLExternal( self, *, filename: str, length: int, alt_txt: Optional[str] = None, snippet_type: Optional[str] = None, **kwargs, ) -> SlackResponse: """Gets a URL for an edge external upload. https://docs.slack.dev/reference/methods/files.getUploadURLExternal """ kwargs.update( { "filename": filename, "length": length, "alt_txt": alt_txt, "snippet_type": snippet_type, } ) return self.api_call("files.getUploadURLExternal", params=kwargs) def files_completeUploadExternal( self, *, files: List[Dict[str, Optional[str]]], channel_id: Optional[str] = None, channels: Optional[List[str]] = None, initial_comment: Optional[str] = None, thread_ts: Optional[str] = None, **kwargs, ) -> SlackResponse: """Finishes an upload started with files.getUploadURLExternal. https://docs.slack.dev/reference/methods/files.completeUploadExternal """ _files = [{k: v for k, v in f.items() if v is not None} for f in files] kwargs.update( { "files": json.dumps(_files), "channel_id": channel_id, "initial_comment": initial_comment, "thread_ts": thread_ts, } ) if channels: kwargs["channels"] = ",".join(channels) return self.api_call("files.completeUploadExternal", params=kwargs) def functions_completeSuccess( self, *, function_execution_id: str, outputs: Dict[str, Any], **kwargs, ) -> SlackResponse: """Signal the successful completion of a function https://docs.slack.dev/reference/methods/functions.completeSuccess """ kwargs.update({"function_execution_id": function_execution_id, "outputs": json.dumps(outputs)}) return self.api_call("functions.completeSuccess", params=kwargs) def functions_completeError( self, *, function_execution_id: str, error: str, **kwargs, ) -> SlackResponse: """Signal the failure to execute a function https://docs.slack.dev/reference/methods/functions.completeError """ kwargs.update({"function_execution_id": function_execution_id, "error": error}) return self.api_call("functions.completeError", params=kwargs) # -------------------------- # Deprecated: groups.* # You can use conversations.* APIs instead. # https://docs.slack.dev/changelog/2020-01-deprecating-antecedents-to-the-conversations-api/ # -------------------------- def groups_archive( self, *, channel: str, **kwargs, ) -> SlackResponse: """Archives a private channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.archive", json=kwargs) def groups_create( self, *, name: str, **kwargs, ) -> SlackResponse: """Creates a private channel.""" kwargs.update({"name": name}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.create", json=kwargs) def groups_createChild( self, *, channel: str, **kwargs, ) -> SlackResponse: """Clones and archives a private channel.""" kwargs.update({"channel": channel}) return self.api_call("groups.createChild", http_verb="GET", params=kwargs) def groups_history( self, *, channel: str, **kwargs, ) -> SlackResponse: """Fetches history of messages and events from a private channel.""" kwargs.update({"channel": channel}) return self.api_call("groups.history", http_verb="GET", params=kwargs) def groups_info( self, *, channel: str, **kwargs, ) -> SlackResponse: """Gets information about a private channel.""" kwargs.update({"channel": channel}) return self.api_call("groups.info", http_verb="GET", params=kwargs) def groups_invite( self, *, channel: str, user: str, **kwargs, ) -> SlackResponse: """Invites a user to a private channel.""" kwargs.update({"channel": channel, "user": user}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.invite", json=kwargs) def groups_kick( self, *, channel: str, user: str, **kwargs, ) -> SlackResponse: """Removes a user from a private channel.""" kwargs.update({"channel": channel, "user": user}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.kick", json=kwargs) def groups_leave( self, *, channel: str, **kwargs, ) -> SlackResponse: """Leaves a private channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.leave", json=kwargs) def groups_list( self, **kwargs, ) -> SlackResponse: """Lists private channels that the calling user has access to.""" return self.api_call("groups.list", http_verb="GET", params=kwargs) def groups_mark( self, *, channel: str, ts: str, **kwargs, ) -> SlackResponse: """Sets the read cursor in a private channel.""" kwargs.update({"channel": channel, "ts": ts}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.mark", json=kwargs) def groups_open( self, *, channel: str, **kwargs, ) -> SlackResponse: """Opens a private channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.open", json=kwargs) def groups_rename( self, *, channel: str, name: str, **kwargs, ) -> SlackResponse: """Renames a private channel.""" kwargs.update({"channel": channel, "name": name}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.rename", json=kwargs) def groups_replies( self, *, channel: str, thread_ts: str, **kwargs, ) -> SlackResponse: """Retrieve a thread of messages posted to a private channel""" kwargs.update({"channel": channel, "thread_ts": thread_ts}) return self.api_call("groups.replies", http_verb="GET", params=kwargs) def groups_setPurpose( self, *, channel: str, purpose: str, **kwargs, ) -> SlackResponse: """Sets the purpose for a private channel.""" kwargs.update({"channel": channel, "purpose": purpose}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.setPurpose", json=kwargs) def groups_setTopic( self, *, channel: str, topic: str, **kwargs, ) -> SlackResponse: """Sets the topic for a private channel.""" kwargs.update({"channel": channel, "topic": topic}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.setTopic", json=kwargs) def groups_unarchive( self, *, channel: str, **kwargs, ) -> SlackResponse: """Unarchives a private channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.unarchive", json=kwargs) # -------------------------- # Deprecated: im.* # You can use conversations.* APIs instead. # https://docs.slack.dev/changelog/2020-01-deprecating-antecedents-to-the-conversations-api/ # -------------------------- def im_close( self, *, channel: str, **kwargs, ) -> SlackResponse: """Close a direct message channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("im.close", json=kwargs) def im_history( self, *, channel: str, **kwargs, ) -> SlackResponse: """Fetches history of messages and events from direct message channel.""" kwargs.update({"channel": channel}) return self.api_call("im.history", http_verb="GET", params=kwargs) def im_list( self, **kwargs, ) -> SlackResponse: """Lists direct message channels for the calling user.""" return self.api_call("im.list", http_verb="GET", params=kwargs) def im_mark( self, *, channel: str, ts: str, **kwargs, ) -> SlackResponse: """Sets the read cursor in a direct message channel.""" kwargs.update({"channel": channel, "ts": ts}) kwargs = _remove_none_values(kwargs) return self.api_call("im.mark", json=kwargs) def im_open( self, *, user: str, **kwargs, ) -> SlackResponse: """Opens a direct message channel.""" kwargs.update({"user": user}) kwargs = _remove_none_values(kwargs) return self.api_call("im.open", json=kwargs) def im_replies( self, *, channel: str, thread_ts: str, **kwargs, ) -> SlackResponse: """Retrieve a thread of messages posted to a direct message conversation""" kwargs.update({"channel": channel, "thread_ts": thread_ts}) return self.api_call("im.replies", http_verb="GET", params=kwargs) # -------------------------- def migration_exchange( self, *, users: Union[str, Sequence[str]], team_id: Optional[str] = None, to_old: Optional[bool] = None, **kwargs, ) -> SlackResponse: """For Enterprise Grid workspaces, map local user IDs to global user IDs https://docs.slack.dev/reference/methods/migration.exchange """ if isinstance(users, (list, tuple)): kwargs.update({"users": ",".join(users)}) else: kwargs.update({"users": users}) kwargs.update({"team_id": team_id, "to_old": to_old}) return self.api_call("migration.exchange", http_verb="GET", params=kwargs) # -------------------------- # Deprecated: mpim.* # You can use conversations.* APIs instead. # https://docs.slack.dev/changelog/2020-01-deprecating-antecedents-to-the-conversations-api/ # -------------------------- def mpim_close( self, *, channel: str, **kwargs, ) -> SlackResponse: """Closes a multiparty direct message channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("mpim.close", json=kwargs) def mpim_history( self, *, channel: str, **kwargs, ) -> SlackResponse: """Fetches history of messages and events from a multiparty direct message.""" kwargs.update({"channel": channel}) return self.api_call("mpim.history", http_verb="GET", params=kwargs) def mpim_list( self, **kwargs, ) -> SlackResponse: """Lists multiparty direct message channels for the calling user.""" return self.api_call("mpim.list", http_verb="GET", params=kwargs) def mpim_mark( self, *, channel: str, ts: str, **kwargs, ) -> SlackResponse: """Sets the read cursor in a multiparty direct message channel.""" kwargs.update({"channel": channel, "ts": ts}) kwargs = _remove_none_values(kwargs) return self.api_call("mpim.mark", json=kwargs) def mpim_open( self, *, users: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """This method opens a multiparty direct message.""" if isinstance(users, (list, tuple)): kwargs.update({"users": ",".join(users)}) else: kwargs.update({"users": users}) return self.api_call("mpim.open", params=kwargs) def mpim_replies( self, *, channel: str, thread_ts: str, **kwargs, ) -> SlackResponse: """Retrieve a thread of messages posted to a direct message conversation from a multiparty direct message. """ kwargs.update({"channel": channel, "thread_ts": thread_ts}) return self.api_call("mpim.replies", http_verb="GET", params=kwargs) # -------------------------- def oauth_v2_access( self, *, client_id: str, client_secret: str, # This field is required when processing the OAuth redirect URL requests # while it's absent for token rotation code: Optional[str] = None, redirect_uri: Optional[str] = None, # This field is required for token rotation grant_type: Optional[str] = None, # This field is required for token rotation refresh_token: Optional[str] = None, **kwargs, ) -> SlackResponse: """Exchanges a temporary OAuth verifier code for an access token. https://docs.slack.dev/reference/methods/oauth.v2.access """ if redirect_uri is not None: kwargs.update({"redirect_uri": redirect_uri}) if code is not None: kwargs.update({"code": code}) if grant_type is not None: kwargs.update({"grant_type": grant_type}) if refresh_token is not None: kwargs.update({"refresh_token": refresh_token}) return self.api_call( "oauth.v2.access", data=kwargs, auth={"client_id": client_id, "client_secret": client_secret}, ) def oauth_access( self, *, client_id: str, client_secret: str, code: str, redirect_uri: Optional[str] = None, **kwargs, ) -> SlackResponse: """Exchanges a temporary OAuth verifier code for an access token. https://docs.slack.dev/reference/methods/oauth.access """ if redirect_uri is not None: kwargs.update({"redirect_uri": redirect_uri}) kwargs.update({"code": code}) return self.api_call( "oauth.access", data=kwargs, auth={"client_id": client_id, "client_secret": client_secret}, ) def oauth_v2_exchange( self, *, token: str, client_id: str, client_secret: str, **kwargs, ) -> SlackResponse: """Exchanges a legacy access token for a new expiring access token and refresh token https://docs.slack.dev/reference/methods/oauth.v2.exchange """ kwargs.update({"client_id": client_id, "client_secret": client_secret, "token": token}) return self.api_call("oauth.v2.exchange", params=kwargs) def openid_connect_token( self, client_id: str, client_secret: str, code: Optional[str] = None, redirect_uri: Optional[str] = None, grant_type: Optional[str] = None, refresh_token: Optional[str] = None, **kwargs, ) -> SlackResponse: """Exchanges a temporary OAuth verifier code for an access token for Sign in with Slack. https://docs.slack.dev/reference/methods/openid.connect.token """ if redirect_uri is not None: kwargs.update({"redirect_uri": redirect_uri}) if code is not None: kwargs.update({"code": code}) if grant_type is not None: kwargs.update({"grant_type": grant_type}) if refresh_token is not None: kwargs.update({"refresh_token": refresh_token}) return self.api_call( "openid.connect.token", data=kwargs, auth={"client_id": client_id, "client_secret": client_secret}, ) def openid_connect_userInfo( self, **kwargs, ) -> SlackResponse: """Get the identity of a user who has authorized Sign in with Slack. https://docs.slack.dev/reference/methods/openid.connect.userInfo """ return self.api_call("openid.connect.userInfo", params=kwargs) def pins_add( self, *, channel: str, timestamp: Optional[str] = None, **kwargs, ) -> SlackResponse: """Pins an item to a channel. https://docs.slack.dev/reference/methods/pins.add """ kwargs.update({"channel": channel, "timestamp": timestamp}) return self.api_call("pins.add", params=kwargs) def pins_list( self, *, channel: str, **kwargs, ) -> SlackResponse: """Lists items pinned to a channel. https://docs.slack.dev/reference/methods/pins.list """ kwargs.update({"channel": channel}) return self.api_call("pins.list", http_verb="GET", params=kwargs) def pins_remove( self, *, channel: str, timestamp: Optional[str] = None, **kwargs, ) -> SlackResponse: """Un-pins an item from a channel. https://docs.slack.dev/reference/methods/pins.remove """ kwargs.update({"channel": channel, "timestamp": timestamp}) return self.api_call("pins.remove", params=kwargs) def reactions_add( self, *, channel: str, name: str, timestamp: str, **kwargs, ) -> SlackResponse: """Adds a reaction to an item. https://docs.slack.dev/reference/methods/reactions.add """ kwargs.update({"channel": channel, "name": name, "timestamp": timestamp}) return self.api_call("reactions.add", params=kwargs) def reactions_get( self, *, channel: Optional[str] = None, file: Optional[str] = None, file_comment: Optional[str] = None, full: Optional[bool] = None, timestamp: Optional[str] = None, **kwargs, ) -> SlackResponse: """Gets reactions for an item. https://docs.slack.dev/reference/methods/reactions.get """ kwargs.update( { "channel": channel, "file": file, "file_comment": file_comment, "full": full, "timestamp": timestamp, } ) return self.api_call("reactions.get", http_verb="GET", params=kwargs) def reactions_list( self, *, count: Optional[int] = None, cursor: Optional[str] = None, full: Optional[bool] = None, limit: Optional[int] = None, page: Optional[int] = None, team_id: Optional[str] = None, user: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists reactions made by a user. https://docs.slack.dev/reference/methods/reactions.list """ kwargs.update( { "count": count, "cursor": cursor, "full": full, "limit": limit, "page": page, "team_id": team_id, "user": user, } ) return self.api_call("reactions.list", http_verb="GET", params=kwargs) def reactions_remove( self, *, name: str, channel: Optional[str] = None, file: Optional[str] = None, file_comment: Optional[str] = None, timestamp: Optional[str] = None, **kwargs, ) -> SlackResponse: """Removes a reaction from an item. https://docs.slack.dev/reference/methods/reactions.remove """ kwargs.update( { "name": name, "channel": channel, "file": file, "file_comment": file_comment, "timestamp": timestamp, } ) return self.api_call("reactions.remove", params=kwargs) def reminders_add( self, *, text: str, time: str, team_id: Optional[str] = None, user: Optional[str] = None, recurrence: Optional[str] = None, **kwargs, ) -> SlackResponse: """Creates a reminder. https://docs.slack.dev/reference/methods/reminders.add """ kwargs.update( { "text": text, "time": time, "team_id": team_id, "user": user, "recurrence": recurrence, } ) return self.api_call("reminders.add", params=kwargs) def reminders_complete( self, *, reminder: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Marks a reminder as complete. https://docs.slack.dev/reference/methods/reminders.complete """ kwargs.update({"reminder": reminder, "team_id": team_id}) return self.api_call("reminders.complete", params=kwargs) def reminders_delete( self, *, reminder: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Deletes a reminder. https://docs.slack.dev/reference/methods/reminders.delete """ kwargs.update({"reminder": reminder, "team_id": team_id}) return self.api_call("reminders.delete", params=kwargs) def reminders_info( self, *, reminder: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Gets information about a reminder. https://docs.slack.dev/reference/methods/reminders.info """ kwargs.update({"reminder": reminder, "team_id": team_id}) return self.api_call("reminders.info", http_verb="GET", params=kwargs) def reminders_list( self, *, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists all reminders created by or for a given user. https://docs.slack.dev/reference/methods/reminders.list """ kwargs.update({"team_id": team_id}) return self.api_call("reminders.list", http_verb="GET", params=kwargs) def rtm_connect( self, *, batch_presence_aware: Optional[bool] = None, presence_sub: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Starts a Real Time Messaging session. https://docs.slack.dev/reference/methods/rtm.connect """ kwargs.update({"batch_presence_aware": batch_presence_aware, "presence_sub": presence_sub}) return self.api_call("rtm.connect", http_verb="GET", params=kwargs) def rtm_start( self, *, batch_presence_aware: Optional[bool] = None, include_locale: Optional[bool] = None, mpim_aware: Optional[bool] = None, no_latest: Optional[bool] = None, no_unreads: Optional[bool] = None, presence_sub: Optional[bool] = None, simple_latest: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Starts a Real Time Messaging session. https://docs.slack.dev/reference/methods/rtm.start """ kwargs.update( { "batch_presence_aware": batch_presence_aware, "include_locale": include_locale, "mpim_aware": mpim_aware, "no_latest": no_latest, "no_unreads": no_unreads, "presence_sub": presence_sub, "simple_latest": simple_latest, } ) return self.api_call("rtm.start", http_verb="GET", params=kwargs) def search_all( self, *, query: str, count: Optional[int] = None, highlight: Optional[bool] = None, page: Optional[int] = None, sort: Optional[str] = None, sort_dir: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Searches for messages and files matching a query. https://docs.slack.dev/reference/methods/search.all """ kwargs.update( { "query": query, "count": count, "highlight": highlight, "page": page, "sort": sort, "sort_dir": sort_dir, "team_id": team_id, } ) return self.api_call("search.all", http_verb="GET", params=kwargs) def search_files( self, *, query: str, count: Optional[int] = None, highlight: Optional[bool] = None, page: Optional[int] = None, sort: Optional[str] = None, sort_dir: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Searches for files matching a query. https://docs.slack.dev/reference/methods/search.files """ kwargs.update( { "query": query, "count": count, "highlight": highlight, "page": page, "sort": sort, "sort_dir": sort_dir, "team_id": team_id, } ) return self.api_call("search.files", http_verb="GET", params=kwargs) def search_messages( self, *, query: str, count: Optional[int] = None, cursor: Optional[str] = None, highlight: Optional[bool] = None, page: Optional[int] = None, sort: Optional[str] = None, sort_dir: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Searches for messages matching a query. https://docs.slack.dev/reference/methods/search.messages """ kwargs.update( { "query": query, "count": count, "cursor": cursor, "highlight": highlight, "page": page, "sort": sort, "sort_dir": sort_dir, "team_id": team_id, } ) return self.api_call("search.messages", http_verb="GET", params=kwargs) def slackLists_access_delete( self, *, list_id: str, channel_ids: Optional[List[str]] = None, user_ids: Optional[List[str]] = None, **kwargs, ) -> SlackResponse: """Revoke access to a List for specified entities. https://docs.slack.dev/reference/methods/slackLists.access.delete """ kwargs.update({"list_id": list_id, "channel_ids": channel_ids, "user_ids": user_ids}) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.access.delete", json=kwargs) def slackLists_access_set( self, *, list_id: str, access_level: str, channel_ids: Optional[List[str]] = None, user_ids: Optional[List[str]] = None, **kwargs, ) -> SlackResponse: """Set the access level to a List for specified entities. https://docs.slack.dev/reference/methods/slackLists.access.set """ kwargs.update({"list_id": list_id, "access_level": access_level, "channel_ids": channel_ids, "user_ids": user_ids}) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.access.set", json=kwargs) def slackLists_create( self, *, name: str, description_blocks: Optional[Union[str, Sequence[Union[Dict, RichTextBlock]]]] = None, schema: Optional[List[Dict[str, Any]]] = None, copy_from_list_id: Optional[str] = None, include_copied_list_records: Optional[bool] = None, todo_mode: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Creates a List. https://docs.slack.dev/reference/methods/slackLists.create """ kwargs.update( { "name": name, "description_blocks": description_blocks, "schema": schema, "copy_from_list_id": copy_from_list_id, "include_copied_list_records": include_copied_list_records, "todo_mode": todo_mode, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.create", json=kwargs) def slackLists_download_get( self, *, list_id: str, job_id: str, **kwargs, ) -> SlackResponse: """Retrieve List download URL from an export job to download List contents. https://docs.slack.dev/reference/methods/slackLists.download.get """ kwargs.update( { "list_id": list_id, "job_id": job_id, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.download.get", json=kwargs) def slackLists_download_start( self, *, list_id: str, include_archived: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Initiate a job to export List contents. https://docs.slack.dev/reference/methods/slackLists.download.start """ kwargs.update( { "list_id": list_id, "include_archived": include_archived, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.download.start", json=kwargs) def slackLists_items_create( self, *, list_id: str, duplicated_item_id: Optional[str] = None, parent_item_id: Optional[str] = None, initial_fields: Optional[List[Dict[str, Any]]] = None, **kwargs, ) -> SlackResponse: """Add a new item to an existing List. https://docs.slack.dev/reference/methods/slackLists.items.create """ kwargs.update( { "list_id": list_id, "duplicated_item_id": duplicated_item_id, "parent_item_id": parent_item_id, "initial_fields": initial_fields, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.items.create", json=kwargs) def slackLists_items_delete( self, *, list_id: str, id: str, **kwargs, ) -> SlackResponse: """Deletes an item from an existing List. https://docs.slack.dev/reference/methods/slackLists.items.delete """ kwargs.update( { "list_id": list_id, "id": id, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.items.delete", json=kwargs) def slackLists_items_deleteMultiple( self, *, list_id: str, ids: List[str], **kwargs, ) -> SlackResponse: """Deletes multiple items from an existing List. https://docs.slack.dev/reference/methods/slackLists.items.deleteMultiple """ kwargs.update( { "list_id": list_id, "ids": ids, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.items.deleteMultiple", json=kwargs) def slackLists_items_info( self, *, list_id: str, id: str, include_is_subscribed: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Get a row from a List. https://docs.slack.dev/reference/methods/slackLists.items.info """ kwargs.update( { "list_id": list_id, "id": id, "include_is_subscribed": include_is_subscribed, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.items.info", json=kwargs) def slackLists_items_list( self, *, list_id: str, limit: Optional[int] = None, cursor: Optional[str] = None, archived: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Get records from a List. https://docs.slack.dev/reference/methods/slackLists.items.list """ kwargs.update( { "list_id": list_id, "limit": limit, "cursor": cursor, "archived": archived, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.items.list", json=kwargs) def slackLists_items_update( self, *, list_id: str, cells: List[Dict[str, Any]], **kwargs, ) -> SlackResponse: """Updates cells in a List. https://docs.slack.dev/reference/methods/slackLists.items.update """ kwargs.update( { "list_id": list_id, "cells": cells, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.items.update", json=kwargs) def slackLists_update( self, *, id: str, name: Optional[str] = None, description_blocks: Optional[Union[str, Sequence[Union[Dict, RichTextBlock]]]] = None, todo_mode: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Update a List. https://docs.slack.dev/reference/methods/slackLists.update """ kwargs.update( { "id": id, "name": name, "description_blocks": description_blocks, "todo_mode": todo_mode, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.update", json=kwargs) def stars_add( self, *, channel: Optional[str] = None, file: Optional[str] = None, file_comment: Optional[str] = None, timestamp: Optional[str] = None, **kwargs, ) -> SlackResponse: """Adds a star to an item. https://docs.slack.dev/reference/methods/stars.add """ kwargs.update( { "channel": channel, "file": file, "file_comment": file_comment, "timestamp": timestamp, } ) return self.api_call("stars.add", params=kwargs) def stars_list( self, *, count: Optional[int] = None, cursor: Optional[str] = None, limit: Optional[int] = None, page: Optional[int] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists stars for a user. https://docs.slack.dev/reference/methods/stars.list """ kwargs.update( { "count": count, "cursor": cursor, "limit": limit, "page": page, "team_id": team_id, } ) return self.api_call("stars.list", http_verb="GET", params=kwargs) def stars_remove( self, *, channel: Optional[str] = None, file: Optional[str] = None, file_comment: Optional[str] = None, timestamp: Optional[str] = None, **kwargs, ) -> SlackResponse: """Removes a star from an item. https://docs.slack.dev/reference/methods/stars.remove """ kwargs.update( { "channel": channel, "file": file, "file_comment": file_comment, "timestamp": timestamp, } ) return self.api_call("stars.remove", params=kwargs) def team_accessLogs( self, *, before: Optional[Union[int, str]] = None, count: Optional[Union[int, str]] = None, page: Optional[Union[int, str]] = None, team_id: Optional[str] = None, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Gets the access logs for the current team. https://docs.slack.dev/reference/methods/team.accessLogs """ kwargs.update( { "before": before, "count": count, "page": page, "team_id": team_id, "cursor": cursor, "limit": limit, } ) return self.api_call("team.accessLogs", http_verb="GET", params=kwargs) def team_billableInfo( self, *, team_id: Optional[str] = None, user: Optional[str] = None, **kwargs, ) -> SlackResponse: """Gets billable users information for the current team. https://docs.slack.dev/reference/methods/team.billableInfo """ kwargs.update({"team_id": team_id, "user": user}) return self.api_call("team.billableInfo", http_verb="GET", params=kwargs) def team_billing_info( self, **kwargs, ) -> SlackResponse: """Reads a workspace's billing plan information. https://docs.slack.dev/reference/methods/team.billing.info """ return self.api_call("team.billing.info", params=kwargs) def team_externalTeams_disconnect( self, *, target_team: str, **kwargs, ) -> SlackResponse: """Disconnects an external organization. https://docs.slack.dev/reference/methods/team.externalTeams.disconnect """ kwargs.update( { "target_team": target_team, } ) return self.api_call("team.externalTeams.disconnect", params=kwargs) def team_externalTeams_list( self, *, connection_status_filter: Optional[str] = None, slack_connect_pref_filter: Optional[Sequence[str]] = None, sort_direction: Optional[str] = None, sort_field: Optional[str] = None, workspace_filter: Optional[Sequence[str]] = None, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Returns a list of all the external teams connected and details about the connection. https://docs.slack.dev/reference/methods/team.externalTeams.list """ kwargs.update( { "connection_status_filter": connection_status_filter, "sort_direction": sort_direction, "sort_field": sort_field, "cursor": cursor, "limit": limit, } ) if slack_connect_pref_filter is not None: if isinstance(slack_connect_pref_filter, (list, tuple)): kwargs.update({"slack_connect_pref_filter": ",".join(slack_connect_pref_filter)}) else: kwargs.update({"slack_connect_pref_filter": slack_connect_pref_filter}) if workspace_filter is not None: if isinstance(workspace_filter, (list, tuple)): kwargs.update({"workspace_filter": ",".join(workspace_filter)}) else: kwargs.update({"workspace_filter": workspace_filter}) return self.api_call("team.externalTeams.list", http_verb="GET", params=kwargs) def team_info( self, *, team: Optional[str] = None, domain: Optional[str] = None, **kwargs, ) -> SlackResponse: """Gets information about the current team. https://docs.slack.dev/reference/methods/team.info """ kwargs.update({"team": team, "domain": domain}) return self.api_call("team.info", http_verb="GET", params=kwargs) def team_integrationLogs( self, *, app_id: Optional[str] = None, change_type: Optional[str] = None, count: Optional[Union[int, str]] = None, page: Optional[Union[int, str]] = None, service_id: Optional[str] = None, team_id: Optional[str] = None, user: Optional[str] = None, **kwargs, ) -> SlackResponse: """Gets the integration logs for the current team. https://docs.slack.dev/reference/methods/team.integrationLogs """ kwargs.update( { "app_id": app_id, "change_type": change_type, "count": count, "page": page, "service_id": service_id, "team_id": team_id, "user": user, } ) return self.api_call("team.integrationLogs", http_verb="GET", params=kwargs) def team_profile_get( self, *, visibility: Optional[str] = None, **kwargs, ) -> SlackResponse: """Retrieve a team's profile. https://docs.slack.dev/reference/methods/team.profile.get """ kwargs.update({"visibility": visibility}) return self.api_call("team.profile.get", http_verb="GET", params=kwargs) def team_preferences_list( self, **kwargs, ) -> SlackResponse: """Retrieve a list of a workspace's team preferences. https://docs.slack.dev/reference/methods/team.preferences.list """ return self.api_call("team.preferences.list", params=kwargs) def usergroups_create( self, *, name: str, channels: Optional[Union[str, Sequence[str]]] = None, description: Optional[str] = None, handle: Optional[str] = None, include_count: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Create a User Group https://docs.slack.dev/reference/methods/usergroups.create """ kwargs.update( { "name": name, "description": description, "handle": handle, "include_count": include_count, "team_id": team_id, } ) if isinstance(channels, (list, tuple)): kwargs.update({"channels": ",".join(channels)}) else: kwargs.update({"channels": channels}) return self.api_call("usergroups.create", params=kwargs) def usergroups_disable( self, *, usergroup: str, include_count: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Disable an existing User Group https://docs.slack.dev/reference/methods/usergroups.disable """ kwargs.update({"usergroup": usergroup, "include_count": include_count, "team_id": team_id}) return self.api_call("usergroups.disable", params=kwargs) def usergroups_enable( self, *, usergroup: str, include_count: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Enable a User Group https://docs.slack.dev/reference/methods/usergroups.enable """ kwargs.update({"usergroup": usergroup, "include_count": include_count, "team_id": team_id}) return self.api_call("usergroups.enable", params=kwargs) def usergroups_list( self, *, include_count: Optional[bool] = None, include_disabled: Optional[bool] = None, include_users: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List all User Groups for a team https://docs.slack.dev/reference/methods/usergroups.list """ kwargs.update( { "include_count": include_count, "include_disabled": include_disabled, "include_users": include_users, "team_id": team_id, } ) return self.api_call("usergroups.list", http_verb="GET", params=kwargs) def usergroups_update( self, *, usergroup: str, channels: Optional[Union[str, Sequence[str]]] = None, description: Optional[str] = None, handle: Optional[str] = None, include_count: Optional[bool] = None, name: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Update an existing User Group https://docs.slack.dev/reference/methods/usergroups.update """ kwargs.update( { "usergroup": usergroup, "description": description, "handle": handle, "include_count": include_count, "name": name, "team_id": team_id, } ) if isinstance(channels, (list, tuple)): kwargs.update({"channels": ",".join(channels)}) else: kwargs.update({"channels": channels}) return self.api_call("usergroups.update", params=kwargs) def usergroups_users_list( self, *, usergroup: str, include_disabled: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List all users in a User Group https://docs.slack.dev/reference/methods/usergroups.users.list """ kwargs.update( { "usergroup": usergroup, "include_disabled": include_disabled, "team_id": team_id, } ) return self.api_call("usergroups.users.list", http_verb="GET", params=kwargs) def usergroups_users_update( self, *, usergroup: str, users: Union[str, Sequence[str]], include_count: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Update the list of users for a User Group https://docs.slack.dev/reference/methods/usergroups.users.update """ kwargs.update( { "usergroup": usergroup, "include_count": include_count, "team_id": team_id, } ) if isinstance(users, (list, tuple)): kwargs.update({"users": ",".join(users)}) else: kwargs.update({"users": users}) return self.api_call("usergroups.users.update", params=kwargs) def users_conversations( self, *, cursor: Optional[str] = None, exclude_archived: Optional[bool] = None, limit: Optional[int] = None, team_id: Optional[str] = None, types: Optional[Union[str, Sequence[str]]] = None, user: Optional[str] = None, **kwargs, ) -> SlackResponse: """List conversations the calling user may access. https://docs.slack.dev/reference/methods/users.conversations """ kwargs.update( { "cursor": cursor, "exclude_archived": exclude_archived, "limit": limit, "team_id": team_id, "user": user, } ) if isinstance(types, (list, tuple)): kwargs.update({"types": ",".join(types)}) else: kwargs.update({"types": types}) return self.api_call("users.conversations", http_verb="GET", params=kwargs) def users_deletePhoto( self, **kwargs, ) -> SlackResponse: """Delete the user profile photo https://docs.slack.dev/reference/methods/users.deletePhoto """ return self.api_call("users.deletePhoto", http_verb="GET", params=kwargs) def users_getPresence( self, *, user: str, **kwargs, ) -> SlackResponse: """Gets user presence information. https://docs.slack.dev/reference/methods/users.getPresence """ kwargs.update({"user": user}) return self.api_call("users.getPresence", http_verb="GET", params=kwargs) def users_identity( self, **kwargs, ) -> SlackResponse: """Get a user's identity. https://docs.slack.dev/reference/methods/users.identity """ return self.api_call("users.identity", http_verb="GET", params=kwargs) def users_info( self, *, user: str, include_locale: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Gets information about a user. https://docs.slack.dev/reference/methods/users.info """ kwargs.update({"user": user, "include_locale": include_locale}) return self.api_call("users.info", http_verb="GET", params=kwargs) def users_list( self, *, cursor: Optional[str] = None, include_locale: Optional[bool] = None, limit: Optional[int] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists all users in a Slack team. https://docs.slack.dev/reference/methods/users.list """ kwargs.update( { "cursor": cursor, "include_locale": include_locale, "limit": limit, "team_id": team_id, } ) return self.api_call("users.list", http_verb="GET", params=kwargs) def users_lookupByEmail( self, *, email: str, **kwargs, ) -> SlackResponse: """Find a user with an email address. https://docs.slack.dev/reference/methods/users.lookupByEmail """ kwargs.update({"email": email}) return self.api_call("users.lookupByEmail", http_verb="GET", params=kwargs) def users_setPhoto( self, *, image: Union[str, IOBase], crop_w: Optional[Union[int, str]] = None, crop_x: Optional[Union[int, str]] = None, crop_y: Optional[Union[int, str]] = None, **kwargs, ) -> SlackResponse: """Set the user profile photo https://docs.slack.dev/reference/methods/users.setPhoto """ kwargs.update({"crop_w": crop_w, "crop_x": crop_x, "crop_y": crop_y}) return self.api_call("users.setPhoto", files={"image": image}, data=kwargs) def users_setPresence( self, *, presence: str, **kwargs, ) -> SlackResponse: """Manually sets user presence. https://docs.slack.dev/reference/methods/users.setPresence """ kwargs.update({"presence": presence}) return self.api_call("users.setPresence", params=kwargs) def users_discoverableContacts_lookup( self, email: str, **kwargs, ) -> SlackResponse: """Lookup an email address to see if someone is on Slack https://docs.slack.dev/reference/methods/users.discoverableContacts.lookup """ kwargs.update({"email": email}) return self.api_call("users.discoverableContacts.lookup", params=kwargs) def users_profile_get( self, *, user: Optional[str] = None, include_labels: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Retrieves a user's profile information. https://docs.slack.dev/reference/methods/users.profile.get """ kwargs.update({"user": user, "include_labels": include_labels}) return self.api_call("users.profile.get", http_verb="GET", params=kwargs) def users_profile_set( self, *, name: Optional[str] = None, value: Optional[str] = None, user: Optional[str] = None, profile: Optional[Dict] = None, **kwargs, ) -> SlackResponse: """Set the profile information for a user. https://docs.slack.dev/reference/methods/users.profile.set """ kwargs.update( { "name": name, "profile": profile, "user": user, "value": value, } ) kwargs = _remove_none_values(kwargs) # NOTE: Intentionally using json for the "profile" parameter return self.api_call("users.profile.set", json=kwargs) def views_open( self, *, trigger_id: Optional[str] = None, interactivity_pointer: Optional[str] = None, view: Union[dict, View], **kwargs, ) -> SlackResponse: """Open a view for a user. https://docs.slack.dev/reference/methods/views.open See https://docs.slack.dev/surfaces/modals/ for details. """ kwargs.update({"trigger_id": trigger_id, "interactivity_pointer": interactivity_pointer}) if isinstance(view, View): kwargs.update({"view": view.to_dict()}) else: kwargs.update({"view": view}) kwargs = _remove_none_values(kwargs) # NOTE: Intentionally using json for the "view" parameter return self.api_call("views.open", json=kwargs) def views_push( self, *, trigger_id: Optional[str] = None, interactivity_pointer: Optional[str] = None, view: Union[dict, View], **kwargs, ) -> SlackResponse: """Push a view onto the stack of a root view. Push a new view onto the existing view stack by passing a view payload and a valid trigger_id generated from an interaction within the existing modal. Read the modals documentation (https://docs.slack.dev/surfaces/modals/) to learn more about the lifecycle and intricacies of views. https://docs.slack.dev/reference/methods/views.push """ kwargs.update({"trigger_id": trigger_id, "interactivity_pointer": interactivity_pointer}) if isinstance(view, View): kwargs.update({"view": view.to_dict()}) else: kwargs.update({"view": view}) kwargs = _remove_none_values(kwargs) # NOTE: Intentionally using json for the "view" parameter return self.api_call("views.push", json=kwargs) def views_update( self, *, view: Union[dict, View], external_id: Optional[str] = None, view_id: Optional[str] = None, hash: Optional[str] = None, **kwargs, ) -> SlackResponse: """Update an existing view. Update a view by passing a new view definition along with the view_id returned in views.open or the external_id. See the modals documentation (https://docs.slack.dev/surfaces/modals/#updating_views) to learn more about updating views and avoiding race conditions with the hash argument. https://docs.slack.dev/reference/methods/views.update """ if isinstance(view, View): kwargs.update({"view": view.to_dict()}) else: kwargs.update({"view": view}) if external_id: kwargs.update({"external_id": external_id}) elif view_id: kwargs.update({"view_id": view_id}) else: raise e.SlackRequestError("Either view_id or external_id is required.") kwargs.update({"hash": hash}) kwargs = _remove_none_values(kwargs) # NOTE: Intentionally using json for the "view" parameter return self.api_call("views.update", json=kwargs) def views_publish( self, *, user_id: str, view: Union[dict, View], hash: Optional[str] = None, **kwargs, ) -> SlackResponse: """Publish a static view for a User. Create or update the view that comprises an app's Home tab (https://docs.slack.dev/surfaces/app-home/) https://docs.slack.dev/reference/methods/views.publish """ kwargs.update({"user_id": user_id, "hash": hash}) if isinstance(view, View): kwargs.update({"view": view.to_dict()}) else: kwargs.update({"view": view}) kwargs = _remove_none_values(kwargs) # NOTE: Intentionally using json for the "view" parameter return self.api_call("views.publish", json=kwargs) def workflows_featured_add( self, *, channel_id: str, trigger_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Add featured workflows to a channel. https://docs.slack.dev/reference/methods/workflows.featured.add """ kwargs.update({"channel_id": channel_id}) if isinstance(trigger_ids, (list, tuple)): kwargs.update({"trigger_ids": ",".join(trigger_ids)}) else: kwargs.update({"trigger_ids": trigger_ids}) return self.api_call("workflows.featured.add", params=kwargs) def workflows_featured_list( self, *, channel_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """List the featured workflows for specified channels. https://docs.slack.dev/reference/methods/workflows.featured.list """ if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) return self.api_call("workflows.featured.list", params=kwargs) def workflows_featured_remove( self, *, channel_id: str, trigger_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Remove featured workflows from a channel. https://docs.slack.dev/reference/methods/workflows.featured.remove """ kwargs.update({"channel_id": channel_id}) if isinstance(trigger_ids, (list, tuple)): kwargs.update({"trigger_ids": ",".join(trigger_ids)}) else: kwargs.update({"trigger_ids": trigger_ids}) return self.api_call("workflows.featured.remove", params=kwargs) def workflows_featured_set( self, *, channel_id: str, trigger_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Set featured workflows for a channel. https://docs.slack.dev/reference/methods/workflows.featured.set """ kwargs.update({"channel_id": channel_id}) if isinstance(trigger_ids, (list, tuple)): kwargs.update({"trigger_ids": ",".join(trigger_ids)}) else: kwargs.update({"trigger_ids": trigger_ids}) return self.api_call("workflows.featured.set", params=kwargs) def workflows_stepCompleted( self, *, workflow_step_execute_id: str, outputs: Optional[dict] = None, **kwargs, ) -> SlackResponse: """Indicate a successful outcome of a workflow step's execution. https://docs.slack.dev/reference/methods/workflows.stepCompleted """ kwargs.update({"workflow_step_execute_id": workflow_step_execute_id}) if outputs is not None: kwargs.update({"outputs": outputs}) kwargs = _remove_none_values(kwargs) # NOTE: Intentionally using json for the "outputs" parameter return self.api_call("workflows.stepCompleted", json=kwargs) def workflows_stepFailed( self, *, workflow_step_execute_id: str, error: Dict[str, str], **kwargs, ) -> SlackResponse: """Indicate an unsuccessful outcome of a workflow step's execution. https://docs.slack.dev/reference/methods/workflows.stepFailed """ kwargs.update( { "workflow_step_execute_id": workflow_step_execute_id, "error": error, } ) kwargs = _remove_none_values(kwargs) # NOTE: Intentionally using json for the "error" parameter return self.api_call("workflows.stepFailed", json=kwargs) def workflows_updateStep( self, *, workflow_step_edit_id: str, inputs: Optional[Dict[str, Any]] = None, outputs: Optional[List[Dict[str, str]]] = None, **kwargs, ) -> SlackResponse: """Update the configuration for a workflow extension step. https://docs.slack.dev/reference/methods/workflows.updateStep """ kwargs.update({"workflow_step_edit_id": workflow_step_edit_id}) if inputs is not None: kwargs.update({"inputs": inputs}) if outputs is not None: kwargs.update({"outputs": outputs}) kwargs = _remove_none_values(kwargs) # NOTE: Intentionally using json for the "inputs" / "outputs" parameters return self.api_call("workflows.updateStep", json=kwargs) ``` A WebClient allows apps to communicate with the Slack Platform's Web API. [https://docs.slack.dev/reference/methods](https://docs.slack.dev/reference/methods) The Slack Web API is an interface for querying information from and enacting change in a Slack workspace. This client handles constructing and sending HTTP requests to Slack as well as parsing any responses received into a `SlackResponse`. ## Attributes **`token`** : `str` A string specifying an `xoxp-*` or `xoxb-*` token. **`base_url`** : `str` A string representing the Slack API base URL. Default is `'https://slack.com/api/'` **`timeout`** : `int` The maximum number of seconds the client will wait to connect and receive a response from Slack. Default is 30 seconds. **`ssl`** : `SSLContext` An [`ssl.SSLContext`](https://docs.python.org/3/library/ssl.html#ssl.SSLContext) instance, helpful for specifying your own custom certificate chain. **`proxy`** : `str` String representing a fully-qualified URL to a proxy through which to route all requests to the Slack API. Even if this parameter is not specified, if any of the following environment variables are present, they will be loaded into this parameter: `HTTPS_PROXY`, `https_proxy`, `HTTP_PROXY` or `http_proxy`. **`headers`** : `dict` Additional request headers to attach to all requests. ## Methods `api_call`: Constructs a request and executes the API call to Slack. Example of recommended usage: ```python import os from slack_sdk import WebClient client = WebClient(token=os.environ['SLACK_API_TOKEN']) response = client.chat_postMessage( channel='#random', text="Hello world!") assert response["ok"] assert response["message"]["text"] == "Hello world!" ``` Example manually creating an API request: ```python import os from slack_sdk import WebClient client = WebClient(token=os.environ['SLACK_API_TOKEN']) response = client.api_call( api_method='chat.postMessage', json={'channel': '#random','text': "Hello world!"} ) assert response["ok"] assert response["message"]["text"] == "Hello world!" ``` ## Note Any attributes or methods prefixed with \_underscores are intended to be "private" internal use only. They may be changed or removed at anytime. ### Ancestors * [BaseClient](web/base_client.html#slack_sdk.web.base_client.BaseClient "slack_sdk.web.base_client.BaseClient") ### Methods `def admin_analytics_getFile(self, *, type: str, date: str | None = None, metadata_only: bool | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_analytics_getFile( self, *, type: str, date: Optional[str] = None, metadata_only: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Retrieve analytics data for a given date, presented as a compressed JSON file https://docs.slack.dev/reference/methods/admin.analytics.getFile """ kwargs.update({"type": type}) if date is not None: kwargs.update({"date": date}) if metadata_only is not None: kwargs.update({"metadata_only": metadata_only}) return self.api_call("admin.analytics.getFile", params=kwargs) ``` Retrieve analytics data for a given date, presented as a compressed JSON file [https://docs.slack.dev/reference/methods/admin.analytics.getFile](https://docs.slack.dev/reference/methods/admin.analytics.getFile) `def admin_apps_activities_list(self, *, app_id: str | None = None, component_id: str | None = None, component_type: str | None = None, log_event_type: str | None = None, max_date_created: int | None = None, min_date_created: int | None = None, min_log_level: str | None = None, sort_direction: str | None = None, source: str | None = None, team_id: str | None = None, trace_id: str | None = None, cursor: str | None = None, limit: int | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_apps_activities_list( self, *, app_id: Optional[str] = None, component_id: Optional[str] = None, component_type: Optional[str] = None, log_event_type: Optional[str] = None, max_date_created: Optional[int] = None, min_date_created: Optional[int] = None, min_log_level: Optional[str] = None, sort_direction: Optional[str] = None, source: Optional[str] = None, team_id: Optional[str] = None, trace_id: Optional[str] = None, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Get logs for a specified team/org https://docs.slack.dev/reference/methods/admin.apps.activities.list """ kwargs.update( { "app_id": app_id, "component_id": component_id, "component_type": component_type, "log_event_type": log_event_type, "max_date_created": max_date_created, "min_date_created": min_date_created, "min_log_level": min_log_level, "sort_direction": sort_direction, "source": source, "team_id": team_id, "trace_id": trace_id, "cursor": cursor, "limit": limit, } ) return self.api_call("admin.apps.activities.list", params=kwargs) ``` Get logs for a specified team/org [https://docs.slack.dev/reference/methods/admin.apps.activities.list](https://docs.slack.dev/reference/methods/admin.apps.activities.list) `def admin_apps_approve(self, *, app_id: str | None = None, request_id: str | None = None, enterprise_id: str | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_apps_approve( self, *, app_id: Optional[str] = None, request_id: Optional[str] = None, enterprise_id: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Approve an app for installation on a workspace. Either app_id or request_id is required. These IDs can be obtained either directly via the app_requested event, or by the admin.apps.requests.list method. https://docs.slack.dev/reference/methods/admin.apps.approve """ if app_id: kwargs.update({"app_id": app_id}) elif request_id: kwargs.update({"request_id": request_id}) else: raise e.SlackRequestError("The app_id or request_id argument must be specified.") kwargs.update( { "enterprise_id": enterprise_id, "team_id": team_id, } ) return self.api_call("admin.apps.approve", params=kwargs) ``` Approve an app for installation on a workspace. Either app\_id or request\_id is required. These IDs can be obtained either directly via the app\_requested event, or by the admin.apps.requests.list method. [https://docs.slack.dev/reference/methods/admin.apps.approve](https://docs.slack.dev/reference/methods/admin.apps.approve) `def admin_apps_approved_list(self, *, cursor: str | None = None, limit: int | None = None, enterprise_id: str | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_apps_approved_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, enterprise_id: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List approved apps for an org or workspace. https://docs.slack.dev/reference/methods/admin.apps.approved.list """ kwargs.update( { "cursor": cursor, "limit": limit, "enterprise_id": enterprise_id, "team_id": team_id, } ) return self.api_call("admin.apps.approved.list", http_verb="GET", params=kwargs) ``` List approved apps for an org or workspace. [https://docs.slack.dev/reference/methods/admin.apps.approved.list](https://docs.slack.dev/reference/methods/admin.apps.approved.list) `def admin_apps_clearResolution(self, *, app_id: str, enterprise_id: str | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_apps_clearResolution( self, *, app_id: str, enterprise_id: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Clear an app resolution https://docs.slack.dev/reference/methods/admin.apps.clearResolution """ kwargs.update( { "app_id": app_id, "enterprise_id": enterprise_id, "team_id": team_id, } ) return self.api_call("admin.apps.clearResolution", http_verb="POST", params=kwargs) ``` Clear an app resolution [https://docs.slack.dev/reference/methods/admin.apps.clearResolution](https://docs.slack.dev/reference/methods/admin.apps.clearResolution) `def admin_apps_config_lookup(self, *, app_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_apps_config_lookup( self, *, app_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Look up the app config for connectors by their IDs https://docs.slack.dev/reference/methods/admin.apps.config.lookup """ if isinstance(app_ids, (list, tuple)): kwargs.update({"app_ids": ",".join(app_ids)}) else: kwargs.update({"app_ids": app_ids}) return self.api_call("admin.apps.config.lookup", params=kwargs) ``` Look up the app config for connectors by their IDs [https://docs.slack.dev/reference/methods/admin.apps.config.lookup](https://docs.slack.dev/reference/methods/admin.apps.config.lookup) `def admin_apps_config_set(self, *, app_id: str, domain_restrictions: Dict[str, Any] | None = None, workflow_auth_strategy: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_apps_config_set( self, *, app_id: str, domain_restrictions: Optional[Dict[str, Any]] = None, workflow_auth_strategy: Optional[str] = None, **kwargs, ) -> SlackResponse: """Set the app config for a connector https://docs.slack.dev/reference/methods/admin.apps.config.set """ kwargs.update( { "app_id": app_id, "workflow_auth_strategy": workflow_auth_strategy, } ) if domain_restrictions is not None: kwargs.update({"domain_restrictions": json.dumps(domain_restrictions)}) return self.api_call("admin.apps.config.set", params=kwargs) ``` Set the app config for a connector [https://docs.slack.dev/reference/methods/admin.apps.config.set](https://docs.slack.dev/reference/methods/admin.apps.config.set) `def admin_apps_requests_cancel(self, *, request_id: str, enterprise_id: str | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_apps_requests_cancel( self, *, request_id: str, enterprise_id: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List app requests for a team/workspace. https://docs.slack.dev/reference/methods/admin.apps.requests.cancel """ kwargs.update( { "request_id": request_id, "enterprise_id": enterprise_id, "team_id": team_id, } ) return self.api_call("admin.apps.requests.cancel", http_verb="POST", params=kwargs) ``` List app requests for a team/workspace. [https://docs.slack.dev/reference/methods/admin.apps.requests.cancel](https://docs.slack.dev/reference/methods/admin.apps.requests.cancel) `def admin_apps_requests_list(self, *, cursor: str | None = None, limit: int | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_apps_requests_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List app requests for a team/workspace. https://docs.slack.dev/reference/methods/admin.apps.requests.list """ kwargs.update( { "cursor": cursor, "limit": limit, "team_id": team_id, } ) return self.api_call("admin.apps.requests.list", http_verb="GET", params=kwargs) ``` List app requests for a team/workspace. [https://docs.slack.dev/reference/methods/admin.apps.requests.list](https://docs.slack.dev/reference/methods/admin.apps.requests.list) `def admin_apps_restrict(self, *, app_id: str | None = None, request_id: str | None = None, enterprise_id: str | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_apps_restrict( self, *, app_id: Optional[str] = None, request_id: Optional[str] = None, enterprise_id: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Restrict an app for installation on a workspace. Exactly one of the team_id or enterprise_id arguments is required, not both. Either app_id or request_id is required. These IDs can be obtained either directly via the app_requested event, or by the admin.apps.requests.list method. https://docs.slack.dev/reference/methods/admin.apps.restrict """ if app_id: kwargs.update({"app_id": app_id}) elif request_id: kwargs.update({"request_id": request_id}) else: raise e.SlackRequestError("The app_id or request_id argument must be specified.") kwargs.update( { "enterprise_id": enterprise_id, "team_id": team_id, } ) return self.api_call("admin.apps.restrict", params=kwargs) ``` Restrict an app for installation on a workspace. Exactly one of the team\_id or enterprise\_id arguments is required, not both. Either app\_id or request\_id is required. These IDs can be obtained either directly via the app\_requested event, or by the admin.apps.requests.list method. [https://docs.slack.dev/reference/methods/admin.apps.restrict](https://docs.slack.dev/reference/methods/admin.apps.restrict) `def admin_apps_restricted_list(self, *, cursor: str | None = None, limit: int | None = None, enterprise_id: str | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_apps_restricted_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, enterprise_id: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List restricted apps for an org or workspace. https://docs.slack.dev/reference/methods/admin.apps.restricted.list """ kwargs.update( { "cursor": cursor, "limit": limit, "enterprise_id": enterprise_id, "team_id": team_id, } ) return self.api_call("admin.apps.restricted.list", http_verb="GET", params=kwargs) ``` List restricted apps for an org or workspace. [https://docs.slack.dev/reference/methods/admin.apps.restricted.list](https://docs.slack.dev/reference/methods/admin.apps.restricted.list) `def admin_apps_uninstall(self, *, app_id: str, enterprise_id: str | None = None, team_ids: str | Sequence[str] | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_apps_uninstall( self, *, app_id: str, enterprise_id: Optional[str] = None, team_ids: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """Uninstall an app from one or many workspaces, or an entire enterprise organization. With an org-level token, enterprise_id or team_ids is required. https://docs.slack.dev/reference/methods/admin.apps.uninstall """ kwargs.update({"app_id": app_id}) if enterprise_id is not None: kwargs.update({"enterprise_id": enterprise_id}) if team_ids is not None: if isinstance(team_ids, (list, tuple)): kwargs.update({"team_ids": ",".join(team_ids)}) else: kwargs.update({"team_ids": team_ids}) return self.api_call("admin.apps.uninstall", http_verb="POST", params=kwargs) ``` Uninstall an app from one or many workspaces, or an entire enterprise organization. With an org-level token, enterprise\_id or team\_ids is required. [https://docs.slack.dev/reference/methods/admin.apps.uninstall](https://docs.slack.dev/reference/methods/admin.apps.uninstall) `def admin_auth_policy_assignEntities(self, *, entity_ids: str | Sequence[str], policy_name: str, entity_type: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_auth_policy_assignEntities( self, *, entity_ids: Union[str, Sequence[str]], policy_name: str, entity_type: str, **kwargs, ) -> SlackResponse: """Assign entities to a particular authentication policy. https://docs.slack.dev/reference/methods/admin.auth.policy.assignEntities """ if isinstance(entity_ids, (list, tuple)): kwargs.update({"entity_ids": ",".join(entity_ids)}) else: kwargs.update({"entity_ids": entity_ids}) kwargs.update({"policy_name": policy_name}) kwargs.update({"entity_type": entity_type}) return self.api_call("admin.auth.policy.assignEntities", http_verb="POST", params=kwargs) ``` Assign entities to a particular authentication policy. [https://docs.slack.dev/reference/methods/admin.auth.policy.assignEntities](https://docs.slack.dev/reference/methods/admin.auth.policy.assignEntities) `def admin_auth_policy_getEntities(self, *, policy_name: str, cursor: str | None = None, entity_type: str | None = None, limit: int | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_auth_policy_getEntities( self, *, policy_name: str, cursor: Optional[str] = None, entity_type: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Fetch all the entities assigned to a particular authentication policy by name. https://docs.slack.dev/reference/methods/admin.auth.policy.getEntities """ kwargs.update({"policy_name": policy_name}) if cursor is not None: kwargs.update({"cursor": cursor}) if entity_type is not None: kwargs.update({"entity_type": entity_type}) if limit is not None: kwargs.update({"limit": limit}) return self.api_call("admin.auth.policy.getEntities", http_verb="POST", params=kwargs) ``` Fetch all the entities assigned to a particular authentication policy by name. [https://docs.slack.dev/reference/methods/admin.auth.policy.getEntities](https://docs.slack.dev/reference/methods/admin.auth.policy.getEntities) `def admin_auth_policy_removeEntities(self, *, entity_ids: str | Sequence[str], policy_name: str, entity_type: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_auth_policy_removeEntities( self, *, entity_ids: Union[str, Sequence[str]], policy_name: str, entity_type: str, **kwargs, ) -> SlackResponse: """Remove specified entities from a specified authentication policy. https://docs.slack.dev/reference/methods/admin.auth.policy.removeEntities """ if isinstance(entity_ids, (list, tuple)): kwargs.update({"entity_ids": ",".join(entity_ids)}) else: kwargs.update({"entity_ids": entity_ids}) kwargs.update({"policy_name": policy_name}) kwargs.update({"entity_type": entity_type}) return self.api_call("admin.auth.policy.removeEntities", http_verb="POST", params=kwargs) ``` Remove specified entities from a specified authentication policy. [https://docs.slack.dev/reference/methods/admin.auth.policy.removeEntities](https://docs.slack.dev/reference/methods/admin.auth.policy.removeEntities) `def admin_barriers_create(self, *, barriered_from_usergroup_ids: str | Sequence[str], primary_usergroup_id: str, restricted_subjects: str | Sequence[str], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_barriers_create( self, *, barriered_from_usergroup_ids: Union[str, Sequence[str]], primary_usergroup_id: str, restricted_subjects: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Create an Information Barrier https://docs.slack.dev/reference/methods/admin.barriers.create """ kwargs.update({"primary_usergroup_id": primary_usergroup_id}) if isinstance(barriered_from_usergroup_ids, (list, tuple)): kwargs.update({"barriered_from_usergroup_ids": ",".join(barriered_from_usergroup_ids)}) else: kwargs.update({"barriered_from_usergroup_ids": barriered_from_usergroup_ids}) if isinstance(restricted_subjects, (list, tuple)): kwargs.update({"restricted_subjects": ",".join(restricted_subjects)}) else: kwargs.update({"restricted_subjects": restricted_subjects}) return self.api_call("admin.barriers.create", http_verb="POST", params=kwargs) ``` Create an Information Barrier [https://docs.slack.dev/reference/methods/admin.barriers.create](https://docs.slack.dev/reference/methods/admin.barriers.create) `def admin_barriers_delete(self, *, barrier_id: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_barriers_delete( self, *, barrier_id: str, **kwargs, ) -> SlackResponse: """Delete an existing Information Barrier https://docs.slack.dev/reference/methods/admin.barriers.delete """ kwargs.update({"barrier_id": barrier_id}) return self.api_call("admin.barriers.delete", http_verb="POST", params=kwargs) ``` Delete an existing Information Barrier [https://docs.slack.dev/reference/methods/admin.barriers.delete](https://docs.slack.dev/reference/methods/admin.barriers.delete) `def admin_barriers_list(self, *, cursor: str | None = None, limit: int | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_barriers_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Get all Information Barriers for your organization https://docs.slack.dev/reference/methods/admin.barriers.list""" kwargs.update( { "cursor": cursor, "limit": limit, } ) return self.api_call("admin.barriers.list", http_verb="GET", params=kwargs) ``` Get all Information Barriers for your organization [https://docs.slack.dev/reference/methods/admin.barriers.list](https://docs.slack.dev/reference/methods/admin.barriers.list) `def admin_barriers_update(self, *, barrier_id: str, barriered_from_usergroup_ids: str | Sequence[str], primary_usergroup_id: str, restricted_subjects: str | Sequence[str], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_barriers_update( self, *, barrier_id: str, barriered_from_usergroup_ids: Union[str, Sequence[str]], primary_usergroup_id: str, restricted_subjects: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Update an existing Information Barrier https://docs.slack.dev/reference/methods/admin.barriers.update """ kwargs.update({"barrier_id": barrier_id, "primary_usergroup_id": primary_usergroup_id}) if isinstance(barriered_from_usergroup_ids, (list, tuple)): kwargs.update({"barriered_from_usergroup_ids": ",".join(barriered_from_usergroup_ids)}) else: kwargs.update({"barriered_from_usergroup_ids": barriered_from_usergroup_ids}) if isinstance(restricted_subjects, (list, tuple)): kwargs.update({"restricted_subjects": ",".join(restricted_subjects)}) else: kwargs.update({"restricted_subjects": restricted_subjects}) return self.api_call("admin.barriers.update", http_verb="POST", params=kwargs) ``` Update an existing Information Barrier [https://docs.slack.dev/reference/methods/admin.barriers.update](https://docs.slack.dev/reference/methods/admin.barriers.update) `def admin_conversations_archive(self, *, channel_id: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_archive( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """Archive a public or private channel. https://docs.slack.dev/reference/methods/admin.conversations.archive """ kwargs.update({"channel_id": channel_id}) return self.api_call("admin.conversations.archive", params=kwargs) ``` Archive a public or private channel. [https://docs.slack.dev/reference/methods/admin.conversations.archive](https://docs.slack.dev/reference/methods/admin.conversations.archive) `def admin_conversations_bulkArchive(self, *, channel_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_bulkArchive( self, *, channel_ids: Union[Sequence[str], str], **kwargs, ) -> SlackResponse: """Archive public or private channels in bulk. https://docs.slack.dev/reference/methods/admin.conversations.bulkArchive """ kwargs.update({"channel_ids": ",".join(channel_ids) if isinstance(channel_ids, (list, tuple)) else channel_ids}) return self.api_call("admin.conversations.bulkArchive", params=kwargs) ``` Archive public or private channels in bulk. [https://docs.slack.dev/reference/methods/admin.conversations.bulkArchive](https://docs.slack.dev/reference/methods/admin.conversations.bulkArchive) `def admin_conversations_bulkDelete(self, *, channel_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_bulkDelete( self, *, channel_ids: Union[Sequence[str], str], **kwargs, ) -> SlackResponse: """Delete public or private channels in bulk. https://slack.com/api/admin.conversations.bulkDelete """ kwargs.update({"channel_ids": ",".join(channel_ids) if isinstance(channel_ids, (list, tuple)) else channel_ids}) return self.api_call("admin.conversations.bulkDelete", params=kwargs) ``` Delete public or private channels in bulk. [https://slack.com/api/admin.conversations.bulkDelete](https://slack.com/api/admin.conversations.bulkDelete) `def admin_conversations_bulkMove(self, *, channel_ids: str | Sequence[str], target_team_id: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_bulkMove( self, *, channel_ids: Union[Sequence[str], str], target_team_id: str, **kwargs, ) -> SlackResponse: """Move public or private channels in bulk. https://docs.slack.dev/reference/methods/admin.conversations.bulkMove """ kwargs.update( { "target_team_id": target_team_id, "channel_ids": ",".join(channel_ids) if isinstance(channel_ids, (list, tuple)) else channel_ids, } ) return self.api_call("admin.conversations.bulkMove", params=kwargs) ``` Move public or private channels in bulk. [https://docs.slack.dev/reference/methods/admin.conversations.bulkMove](https://docs.slack.dev/reference/methods/admin.conversations.bulkMove) `def admin_conversations_convertToPrivate(self, *, channel_id: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_convertToPrivate( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """Convert a public channel to a private channel. https://docs.slack.dev/reference/methods/admin.conversations.convertToPrivate """ kwargs.update({"channel_id": channel_id}) return self.api_call("admin.conversations.convertToPrivate", params=kwargs) ``` Convert a public channel to a private channel. [https://docs.slack.dev/reference/methods/admin.conversations.convertToPrivate](https://docs.slack.dev/reference/methods/admin.conversations.convertToPrivate) `def admin_conversations_convertToPublic(self, *, channel_id: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_convertToPublic( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """Convert a privte channel to a public channel. https://docs.slack.dev/reference/methods/admin.conversations.convertToPublic """ kwargs.update({"channel_id": channel_id}) return self.api_call("admin.conversations.convertToPublic", params=kwargs) ``` Convert a privte channel to a public channel. [https://docs.slack.dev/reference/methods/admin.conversations.convertToPublic](https://docs.slack.dev/reference/methods/admin.conversations.convertToPublic) `def admin_conversations_create(self, *, is_private: bool, name: str, description: str | None = None, org_wide: bool | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_create( self, *, is_private: bool, name: str, description: Optional[str] = None, org_wide: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Create a public or private channel-based conversation. https://docs.slack.dev/reference/methods/admin.conversations.create """ kwargs.update( { "is_private": is_private, "name": name, "description": description, "org_wide": org_wide, "team_id": team_id, } ) return self.api_call("admin.conversations.create", params=kwargs) ``` Create a public or private channel-based conversation. [https://docs.slack.dev/reference/methods/admin.conversations.create](https://docs.slack.dev/reference/methods/admin.conversations.create) `def admin_conversations_createForObjects(self, *, object_id: str, salesforce_org_id: str, invite_object_team: bool | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_createForObjects( self, *, object_id: str, salesforce_org_id: str, invite_object_team: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Create a Salesforce channel for the corresponding object provided. https://docs.slack.dev/reference/methods/admin.conversations.createForObjects """ kwargs.update( {"object_id": object_id, "salesforce_org_id": salesforce_org_id, "invite_object_team": invite_object_team} ) return self.api_call("admin.conversations.createForObjects", params=kwargs) ``` Create a Salesforce channel for the corresponding object provided. [https://docs.slack.dev/reference/methods/admin.conversations.createForObjects](https://docs.slack.dev/reference/methods/admin.conversations.createForObjects) `def admin_conversations_delete(self, *, channel_id: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_delete( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """Delete a public or private channel. https://docs.slack.dev/reference/methods/admin.conversations.delete """ kwargs.update({"channel_id": channel_id}) return self.api_call("admin.conversations.delete", params=kwargs) ``` Delete a public or private channel. [https://docs.slack.dev/reference/methods/admin.conversations.delete](https://docs.slack.dev/reference/methods/admin.conversations.delete) `def admin_conversations_disconnectShared(self, *, channel_id: str, leaving_team_ids: str | Sequence[str] | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_disconnectShared( self, *, channel_id: str, leaving_team_ids: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """Disconnect a connected channel from one or more workspaces. https://docs.slack.dev/reference/methods/admin.conversations.disconnectShared """ kwargs.update({"channel_id": channel_id}) if isinstance(leaving_team_ids, (list, tuple)): kwargs.update({"leaving_team_ids": ",".join(leaving_team_ids)}) else: kwargs.update({"leaving_team_ids": leaving_team_ids}) return self.api_call("admin.conversations.disconnectShared", params=kwargs) ``` Disconnect a connected channel from one or more workspaces. [https://docs.slack.dev/reference/methods/admin.conversations.disconnectShared](https://docs.slack.dev/reference/methods/admin.conversations.disconnectShared) `def admin_conversations_ekm_listOriginalConnectedChannelInfo(self, *, channel_ids: str | Sequence[str] | None = None, cursor: str | None = None, limit: int | None = None, team_ids: str | Sequence[str] | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_ekm_listOriginalConnectedChannelInfo( self, *, channel_ids: Optional[Union[str, Sequence[str]]] = None, cursor: Optional[str] = None, limit: Optional[int] = None, team_ids: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """List all disconnected channels—i.e., channels that were once connected to other workspaces and then disconnected—and the corresponding original channel IDs for key revocation with EKM. https://docs.slack.dev/reference/methods/admin.conversations.ekm.listOriginalConnectedChannelInfo """ kwargs.update( { "cursor": cursor, "limit": limit, } ) if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) if isinstance(team_ids, (list, tuple)): kwargs.update({"team_ids": ",".join(team_ids)}) else: kwargs.update({"team_ids": team_ids}) return self.api_call("admin.conversations.ekm.listOriginalConnectedChannelInfo", params=kwargs) ``` List all disconnected channels—i.e., channels that were once connected to other workspaces and then disconnected—and the corresponding original channel IDs for key revocation with EKM. [https://docs.slack.dev/reference/methods/admin.conversations.ekm.listOriginalConnectedChannelInfo](https://docs.slack.dev/reference/methods/admin.conversations.ekm.listOriginalConnectedChannelInfo) `def admin_conversations_getConversationPrefs(self, *, channel_id: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_getConversationPrefs( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """Get conversation preferences for a public or private channel. https://docs.slack.dev/reference/methods/admin.conversations.getConversationPrefs """ kwargs.update({"channel_id": channel_id}) return self.api_call("admin.conversations.getConversationPrefs", params=kwargs) ``` Get conversation preferences for a public or private channel. [https://docs.slack.dev/reference/methods/admin.conversations.getConversationPrefs](https://docs.slack.dev/reference/methods/admin.conversations.getConversationPrefs) `def admin_conversations_getCustomRetention(self, *, channel_id: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_getCustomRetention( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """Get a channel's retention policy https://docs.slack.dev/reference/methods/admin.conversations.getCustomRetention """ kwargs.update({"channel_id": channel_id}) return self.api_call("admin.conversations.getCustomRetention", params=kwargs) ``` Get a channel's retention policy [https://docs.slack.dev/reference/methods/admin.conversations.getCustomRetention](https://docs.slack.dev/reference/methods/admin.conversations.getCustomRetention) `def admin_conversations_getTeams(self, *, channel_id: str, cursor: str | None = None, limit: int | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_getTeams( self, *, channel_id: str, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Set the workspaces in an Enterprise grid org that connect to a channel. https://docs.slack.dev/reference/methods/admin.conversations.getTeams """ kwargs.update( { "channel_id": channel_id, "cursor": cursor, "limit": limit, } ) return self.api_call("admin.conversations.getTeams", params=kwargs) ``` Set the workspaces in an Enterprise grid org that connect to a channel. [https://docs.slack.dev/reference/methods/admin.conversations.getTeams](https://docs.slack.dev/reference/methods/admin.conversations.getTeams) `def admin_conversations_invite(self, *, channel_id: str, user_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_invite( self, *, channel_id: str, user_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Invite a user to a public or private channel. https://docs.slack.dev/reference/methods/admin.conversations.invite """ kwargs.update({"channel_id": channel_id}) if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) # NOTE: the endpoint is unable to handle Content-Type: application/json as of Sep 3, 2020. return self.api_call("admin.conversations.invite", params=kwargs) ``` Invite a user to a public or private channel. [https://docs.slack.dev/reference/methods/admin.conversations.invite](https://docs.slack.dev/reference/methods/admin.conversations.invite) `def admin_conversations_linkObjects(self, *, channel: str, record_id: str, salesforce_org_id: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_linkObjects( self, *, channel: str, record_id: str, salesforce_org_id: str, **kwargs, ) -> SlackResponse: """Link a Salesforce record to a channel. https://docs.slack.dev/reference/methods/admin.conversations.linkObjects """ kwargs.update( { "channel": channel, "record_id": record_id, "salesforce_org_id": salesforce_org_id, } ) return self.api_call("admin.conversations.linkObjects", params=kwargs) ``` Link a Salesforce record to a channel. [https://docs.slack.dev/reference/methods/admin.conversations.linkObjects](https://docs.slack.dev/reference/methods/admin.conversations.linkObjects) `def admin_conversations_lookup(self, *, last_message_activity_before: int, team_ids: str | Sequence[str], cursor: str | None = None, limit: int | None = None, max_member_count: int | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_lookup( self, *, last_message_activity_before: int, team_ids: Union[str, Sequence[str]], cursor: Optional[str] = None, limit: Optional[int] = None, max_member_count: Optional[int] = None, **kwargs, ) -> SlackResponse: """Returns channels on the given team using the filters. https://docs.slack.dev/reference/methods/admin.conversations.lookup """ kwargs.update( { "last_message_activity_before": last_message_activity_before, "cursor": cursor, "limit": limit, "max_member_count": max_member_count, } ) if isinstance(team_ids, (list, tuple)): kwargs.update({"team_ids": ",".join(team_ids)}) else: kwargs.update({"team_ids": team_ids}) return self.api_call("admin.conversations.lookup", params=kwargs) ``` Returns channels on the given team using the filters. [https://docs.slack.dev/reference/methods/admin.conversations.lookup](https://docs.slack.dev/reference/methods/admin.conversations.lookup) `def admin_conversations_removeCustomRetention(self, *, channel_id: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_removeCustomRetention( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """Remove a channel's retention policy https://docs.slack.dev/reference/methods/admin.conversations.removeCustomRetention """ kwargs.update({"channel_id": channel_id}) return self.api_call("admin.conversations.removeCustomRetention", params=kwargs) ``` Remove a channel's retention policy [https://docs.slack.dev/reference/methods/admin.conversations.removeCustomRetention](https://docs.slack.dev/reference/methods/admin.conversations.removeCustomRetention) `def admin_conversations_rename(self, *, channel_id: str, name: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_rename( self, *, channel_id: str, name: str, **kwargs, ) -> SlackResponse: """Rename a public or private channel. https://docs.slack.dev/reference/methods/admin.conversations.rename """ kwargs.update({"channel_id": channel_id, "name": name}) return self.api_call("admin.conversations.rename", params=kwargs) ``` Rename a public or private channel. [https://docs.slack.dev/reference/methods/admin.conversations.rename](https://docs.slack.dev/reference/methods/admin.conversations.rename) `def admin_conversations_restrictAccess_addGroup(self, *, channel_id: str, group_id: str, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_restrictAccess_addGroup( self, *, channel_id: str, group_id: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Add an allowlist of IDP groups for accessing a channel. https://docs.slack.dev/reference/methods/admin.conversations.restrictAccess.addGroup """ kwargs.update( { "channel_id": channel_id, "group_id": group_id, "team_id": team_id, } ) return self.api_call( "admin.conversations.restrictAccess.addGroup", http_verb="GET", params=kwargs, ) ``` Add an allowlist of IDP groups for accessing a channel. [https://docs.slack.dev/reference/methods/admin.conversations.restrictAccess.addGroup](https://docs.slack.dev/reference/methods/admin.conversations.restrictAccess.addGroup) `def admin_conversations_restrictAccess_listGroups(self, *, channel_id: str, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_restrictAccess_listGroups( self, *, channel_id: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List all IDP Groups linked to a channel. https://docs.slack.dev/reference/methods/admin.conversations.restrictAccess.listGroups """ kwargs.update( { "channel_id": channel_id, "team_id": team_id, } ) return self.api_call( "admin.conversations.restrictAccess.listGroups", http_verb="GET", params=kwargs, ) ``` List all IDP Groups linked to a channel. [https://docs.slack.dev/reference/methods/admin.conversations.restrictAccess.listGroups](https://docs.slack.dev/reference/methods/admin.conversations.restrictAccess.listGroups) `def admin_conversations_restrictAccess_removeGroup(self, *, channel_id: str, group_id: str, team_id: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_restrictAccess_removeGroup( self, *, channel_id: str, group_id: str, team_id: str, **kwargs, ) -> SlackResponse: """Remove a linked IDP group linked from a private channel. https://docs.slack.dev/reference/methods/admin.conversations.restrictAccess.removeGroup """ kwargs.update( { "channel_id": channel_id, "group_id": group_id, "team_id": team_id, } ) return self.api_call( "admin.conversations.restrictAccess.removeGroup", http_verb="GET", params=kwargs, ) ``` Remove a linked IDP group linked from a private channel. [https://docs.slack.dev/reference/methods/admin.conversations.restrictAccess.removeGroup](https://docs.slack.dev/reference/methods/admin.conversations.restrictAccess.removeGroup) `def admin_conversations_search(self, *, cursor: str | None = None, limit: int | None = None, query: str | None = None, search_channel_types: str | Sequence[str] | None = None, sort: str | None = None, sort_dir: str | None = None, team_ids: str | Sequence[str] | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_search( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, query: Optional[str] = None, search_channel_types: Optional[Union[str, Sequence[str]]] = None, sort: Optional[str] = None, sort_dir: Optional[str] = None, team_ids: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """Search for public or private channels in an Enterprise organization. https://docs.slack.dev/reference/methods/admin.conversations.search """ kwargs.update( { "cursor": cursor, "limit": limit, "query": query, "sort": sort, "sort_dir": sort_dir, } ) if isinstance(search_channel_types, (list, tuple)): kwargs.update({"search_channel_types": ",".join(search_channel_types)}) else: kwargs.update({"search_channel_types": search_channel_types}) if isinstance(team_ids, (list, tuple)): kwargs.update({"team_ids": ",".join(team_ids)}) else: kwargs.update({"team_ids": team_ids}) return self.api_call("admin.conversations.search", params=kwargs) ``` Search for public or private channels in an Enterprise organization. [https://docs.slack.dev/reference/methods/admin.conversations.search](https://docs.slack.dev/reference/methods/admin.conversations.search) `def admin_conversations_setConversationPrefs(self, *, channel_id: str, prefs: str | Dict[str, str], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_setConversationPrefs( self, *, channel_id: str, prefs: Union[str, Dict[str, str]], **kwargs, ) -> SlackResponse: """Set the posting permissions for a public or private channel. https://docs.slack.dev/reference/methods/admin.conversations.setConversationPrefs """ kwargs.update({"channel_id": channel_id}) if isinstance(prefs, dict): kwargs.update({"prefs": json.dumps(prefs)}) else: kwargs.update({"prefs": prefs}) return self.api_call("admin.conversations.setConversationPrefs", params=kwargs) ``` Set the posting permissions for a public or private channel. [https://docs.slack.dev/reference/methods/admin.conversations.setConversationPrefs](https://docs.slack.dev/reference/methods/admin.conversations.setConversationPrefs) `def admin_conversations_setCustomRetention(self, *, channel_id: str, duration_days: int, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_setCustomRetention( self, *, channel_id: str, duration_days: int, **kwargs, ) -> SlackResponse: """Set a channel's retention policy https://docs.slack.dev/reference/methods/admin.conversations.setCustomRetention """ kwargs.update({"channel_id": channel_id, "duration_days": duration_days}) return self.api_call("admin.conversations.setCustomRetention", params=kwargs) ``` Set a channel's retention policy [https://docs.slack.dev/reference/methods/admin.conversations.setCustomRetention](https://docs.slack.dev/reference/methods/admin.conversations.setCustomRetention) `def admin_conversations_setTeams(self, *, channel_id: str, org_channel: bool | None = None, target_team_ids: str | Sequence[str] | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_setTeams( self, *, channel_id: str, org_channel: Optional[bool] = None, target_team_ids: Optional[Union[str, Sequence[str]]] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Set the workspaces in an Enterprise grid org that connect to a public or private channel. https://docs.slack.dev/reference/methods/admin.conversations.setTeams """ kwargs.update( { "channel_id": channel_id, "org_channel": org_channel, "team_id": team_id, } ) if isinstance(target_team_ids, (list, tuple)): kwargs.update({"target_team_ids": ",".join(target_team_ids)}) else: kwargs.update({"target_team_ids": target_team_ids}) return self.api_call("admin.conversations.setTeams", params=kwargs) ``` Set the workspaces in an Enterprise grid org that connect to a public or private channel. [https://docs.slack.dev/reference/methods/admin.conversations.setTeams](https://docs.slack.dev/reference/methods/admin.conversations.setTeams) `def admin_conversations_unarchive(self, *, channel_id: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_unarchive( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """Unarchive a public or private channel. https://docs.slack.dev/reference/methods/admin.conversations.archive """ kwargs.update({"channel_id": channel_id}) return self.api_call("admin.conversations.unarchive", params=kwargs) ``` Unarchive a public or private channel. [https://docs.slack.dev/reference/methods/admin.conversations.archive](https://docs.slack.dev/reference/methods/admin.conversations.archive) `def admin_conversations_unlinkObjects(self, *, channel: str, new_name: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_unlinkObjects( self, *, channel: str, new_name: str, **kwargs, ) -> SlackResponse: """Unlink a Salesforce record from a channel. https://docs.slack.dev/reference/methods/admin.conversations.unlinkObjects """ kwargs.update( { "channel": channel, "new_name": new_name, } ) return self.api_call("admin.conversations.unlinkObjects", params=kwargs) ``` Unlink a Salesforce record from a channel. [https://docs.slack.dev/reference/methods/admin.conversations.unlinkObjects](https://docs.slack.dev/reference/methods/admin.conversations.unlinkObjects) `def admin_emoji_add(self, *, name: str, url: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_emoji_add( self, *, name: str, url: str, **kwargs, ) -> SlackResponse: """Add an emoji. https://docs.slack.dev/reference/methods/admin.emoji.add """ kwargs.update({"name": name, "url": url}) return self.api_call("admin.emoji.add", http_verb="GET", params=kwargs) ``` Add an emoji. [https://docs.slack.dev/reference/methods/admin.emoji.add](https://docs.slack.dev/reference/methods/admin.emoji.add) `def admin_emoji_addAlias(self, *, alias_for: str, name: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_emoji_addAlias( self, *, alias_for: str, name: str, **kwargs, ) -> SlackResponse: """Add an emoji alias. https://docs.slack.dev/reference/methods/admin.emoji.addAlias """ kwargs.update({"alias_for": alias_for, "name": name}) return self.api_call("admin.emoji.addAlias", http_verb="GET", params=kwargs) ``` Add an emoji alias. [https://docs.slack.dev/reference/methods/admin.emoji.addAlias](https://docs.slack.dev/reference/methods/admin.emoji.addAlias) `def admin_emoji_list(self, *, cursor: str | None = None, limit: int | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_emoji_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """List emoji for an Enterprise Grid organization. https://docs.slack.dev/reference/methods/admin.emoji.list """ kwargs.update({"cursor": cursor, "limit": limit}) return self.api_call("admin.emoji.list", http_verb="GET", params=kwargs) ``` List emoji for an Enterprise Grid organization. [https://docs.slack.dev/reference/methods/admin.emoji.list](https://docs.slack.dev/reference/methods/admin.emoji.list) `def admin_emoji_remove(self, *, name: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_emoji_remove( self, *, name: str, **kwargs, ) -> SlackResponse: """Remove an emoji across an Enterprise Grid organization. https://docs.slack.dev/reference/methods/admin.emoji.remove """ kwargs.update({"name": name}) return self.api_call("admin.emoji.remove", http_verb="GET", params=kwargs) ``` Remove an emoji across an Enterprise Grid organization. [https://docs.slack.dev/reference/methods/admin.emoji.remove](https://docs.slack.dev/reference/methods/admin.emoji.remove) `def admin_emoji_rename(self, *, name: str, new_name: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_emoji_rename( self, *, name: str, new_name: str, **kwargs, ) -> SlackResponse: """Rename an emoji. https://docs.slack.dev/reference/methods/admin.emoji.rename """ kwargs.update({"name": name, "new_name": new_name}) return self.api_call("admin.emoji.rename", http_verb="GET", params=kwargs) ``` Rename an emoji. [https://docs.slack.dev/reference/methods/admin.emoji.rename](https://docs.slack.dev/reference/methods/admin.emoji.rename) `def admin_functions_list(self, *, app_ids: str | Sequence[str], team_id: str | None = None, cursor: str | None = None, limit: int | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_functions_list( self, *, app_ids: Union[str, Sequence[str]], team_id: Optional[str] = None, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Look up functions by a set of apps https://docs.slack.dev/reference/methods/admin.functions.list """ if isinstance(app_ids, (list, tuple)): kwargs.update({"app_ids": ",".join(app_ids)}) else: kwargs.update({"app_ids": app_ids}) kwargs.update( { "team_id": team_id, "cursor": cursor, "limit": limit, } ) return self.api_call("admin.functions.list", params=kwargs) ``` Look up functions by a set of apps [https://docs.slack.dev/reference/methods/admin.functions.list](https://docs.slack.dev/reference/methods/admin.functions.list) `def admin_functions_permissions_lookup(self, *, function_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_functions_permissions_lookup( self, *, function_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Lookup the visibility of multiple Slack functions and include the users if it is limited to particular named entities. https://docs.slack.dev/reference/methods/admin.functions.permissions.lookup """ if isinstance(function_ids, (list, tuple)): kwargs.update({"function_ids": ",".join(function_ids)}) else: kwargs.update({"function_ids": function_ids}) return self.api_call("admin.functions.permissions.lookup", params=kwargs) ``` Lookup the visibility of multiple Slack functions and include the users if it is limited to particular named entities. [https://docs.slack.dev/reference/methods/admin.functions.permissions.lookup](https://docs.slack.dev/reference/methods/admin.functions.permissions.lookup) `def admin_functions_permissions_set(self, *, function_id: str, visibility: str, user_ids: str | Sequence[str] | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_functions_permissions_set( self, *, function_id: str, visibility: str, user_ids: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """Set the visibility of a Slack function and define the users or workspaces if it is set to named_entities https://docs.slack.dev/reference/methods/admin.functions.permissions.set """ kwargs.update( { "function_id": function_id, "visibility": visibility, } ) if user_ids is not None: if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) return self.api_call("admin.functions.permissions.set", params=kwargs) ``` Set the visibility of a Slack function and define the users or workspaces if it is set to named\_entities [https://docs.slack.dev/reference/methods/admin.functions.permissions.set](https://docs.slack.dev/reference/methods/admin.functions.permissions.set) `def admin_inviteRequests_approve(self, *, invite_request_id: str, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_inviteRequests_approve( self, *, invite_request_id: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Approve a workspace invite request. https://docs.slack.dev/reference/methods/admin.inviteRequests.approve """ kwargs.update({"invite_request_id": invite_request_id, "team_id": team_id}) return self.api_call("admin.inviteRequests.approve", params=kwargs) ``` Approve a workspace invite request. [https://docs.slack.dev/reference/methods/admin.inviteRequests.approve](https://docs.slack.dev/reference/methods/admin.inviteRequests.approve) `def admin_inviteRequests_approved_list(self, *, cursor: str | None = None, limit: int | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_inviteRequests_approved_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List all approved workspace invite requests. https://docs.slack.dev/reference/methods/admin.inviteRequests.approved.list """ kwargs.update( { "cursor": cursor, "limit": limit, "team_id": team_id, } ) return self.api_call("admin.inviteRequests.approved.list", params=kwargs) ``` List all approved workspace invite requests. [https://docs.slack.dev/reference/methods/admin.inviteRequests.approved.list](https://docs.slack.dev/reference/methods/admin.inviteRequests.approved.list) `def admin_inviteRequests_denied_list(self, *, cursor: str | None = None, limit: int | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_inviteRequests_denied_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List all denied workspace invite requests. https://docs.slack.dev/reference/methods/admin.inviteRequests.denied.list """ kwargs.update( { "cursor": cursor, "limit": limit, "team_id": team_id, } ) return self.api_call("admin.inviteRequests.denied.list", params=kwargs) ``` List all denied workspace invite requests. [https://docs.slack.dev/reference/methods/admin.inviteRequests.denied.list](https://docs.slack.dev/reference/methods/admin.inviteRequests.denied.list) `def admin_inviteRequests_deny(self, *, invite_request_id: str, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_inviteRequests_deny( self, *, invite_request_id: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Deny a workspace invite request. https://docs.slack.dev/reference/methods/admin.inviteRequests.deny """ kwargs.update({"invite_request_id": invite_request_id, "team_id": team_id}) return self.api_call("admin.inviteRequests.deny", params=kwargs) ``` Deny a workspace invite request. [https://docs.slack.dev/reference/methods/admin.inviteRequests.deny](https://docs.slack.dev/reference/methods/admin.inviteRequests.deny) `def admin_inviteRequests_list(self, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_inviteRequests_list( self, **kwargs, ) -> SlackResponse: """List all pending workspace invite requests.""" return self.api_call("admin.inviteRequests.list", params=kwargs) ``` List all pending workspace invite requests. `def admin_roles_addAssignments(self, *, role_id: str, entity_ids: str | Sequence[str], user_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_roles_addAssignments( self, *, role_id: str, entity_ids: Union[str, Sequence[str]], user_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Adds members to the specified role with the specified scopes https://docs.slack.dev/reference/methods/admin.roles.addAssignments """ kwargs.update({"role_id": role_id}) if isinstance(entity_ids, (list, tuple)): kwargs.update({"entity_ids": ",".join(entity_ids)}) else: kwargs.update({"entity_ids": entity_ids}) if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) return self.api_call("admin.roles.addAssignments", params=kwargs) ``` Adds members to the specified role with the specified scopes [https://docs.slack.dev/reference/methods/admin.roles.addAssignments](https://docs.slack.dev/reference/methods/admin.roles.addAssignments) `def admin_roles_listAssignments(self, *, role_ids: str | Sequence[str] | None = None, entity_ids: str | Sequence[str] | None = None, cursor: str | None = None, limit: str | int | None = None, sort_dir: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_roles_listAssignments( self, *, role_ids: Optional[Union[str, Sequence[str]]] = None, entity_ids: Optional[Union[str, Sequence[str]]] = None, cursor: Optional[str] = None, limit: Optional[Union[str, int]] = None, sort_dir: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists assignments for all roles across entities. Options to scope results by any combination of roles or entities https://docs.slack.dev/reference/methods/admin.roles.listAssignments """ kwargs.update({"cursor": cursor, "limit": limit, "sort_dir": sort_dir}) if isinstance(entity_ids, (list, tuple)): kwargs.update({"entity_ids": ",".join(entity_ids)}) else: kwargs.update({"entity_ids": entity_ids}) if isinstance(role_ids, (list, tuple)): kwargs.update({"role_ids": ",".join(role_ids)}) else: kwargs.update({"role_ids": role_ids}) return self.api_call("admin.roles.listAssignments", params=kwargs) ``` Lists assignments for all roles across entities. Options to scope results by any combination of roles or entities [https://docs.slack.dev/reference/methods/admin.roles.listAssignments](https://docs.slack.dev/reference/methods/admin.roles.listAssignments) `def admin_roles_removeAssignments(self, *, role_id: str, entity_ids: str | Sequence[str], user_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_roles_removeAssignments( self, *, role_id: str, entity_ids: Union[str, Sequence[str]], user_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Removes a set of users from a role for the given scopes and entities https://docs.slack.dev/reference/methods/admin.roles.removeAssignments """ kwargs.update({"role_id": role_id}) if isinstance(entity_ids, (list, tuple)): kwargs.update({"entity_ids": ",".join(entity_ids)}) else: kwargs.update({"entity_ids": entity_ids}) if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) return self.api_call("admin.roles.removeAssignments", params=kwargs) ``` Removes a set of users from a role for the given scopes and entities [https://docs.slack.dev/reference/methods/admin.roles.removeAssignments](https://docs.slack.dev/reference/methods/admin.roles.removeAssignments) `def admin_teams_admins_list(self, *, team_id: str, cursor: str | None = None, limit: int | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_teams_admins_list( self, *, team_id: str, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """List all of the admins on a given workspace. https://docs.slack.dev/reference/methods/admin.inviteRequests.list """ kwargs.update( { "cursor": cursor, "limit": limit, "team_id": team_id, } ) return self.api_call("admin.teams.admins.list", http_verb="GET", params=kwargs) ``` List all of the admins on a given workspace. [https://docs.slack.dev/reference/methods/admin.inviteRequests.list](https://docs.slack.dev/reference/methods/admin.inviteRequests.list) `def admin_teams_create(self, *, team_domain: str, team_name: str, team_description: str | None = None, team_discoverability: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_teams_create( self, *, team_domain: str, team_name: str, team_description: Optional[str] = None, team_discoverability: Optional[str] = None, **kwargs, ) -> SlackResponse: """Create an Enterprise team. https://docs.slack.dev/reference/methods/admin.teams.create """ kwargs.update( { "team_domain": team_domain, "team_name": team_name, "team_description": team_description, "team_discoverability": team_discoverability, } ) return self.api_call("admin.teams.create", params=kwargs) ``` Create an Enterprise team. [https://docs.slack.dev/reference/methods/admin.teams.create](https://docs.slack.dev/reference/methods/admin.teams.create) `def admin_teams_list(self, *, cursor: str | None = None, limit: int | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_teams_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """List all teams on an Enterprise organization. https://docs.slack.dev/reference/methods/admin.teams.list """ kwargs.update({"cursor": cursor, "limit": limit}) return self.api_call("admin.teams.list", params=kwargs) ``` List all teams on an Enterprise organization. [https://docs.slack.dev/reference/methods/admin.teams.list](https://docs.slack.dev/reference/methods/admin.teams.list) `def admin_teams_owners_list(self, *, team_id: str, cursor: str | None = None, limit: int | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_teams_owners_list( self, *, team_id: str, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """List all of the admins on a given workspace. https://docs.slack.dev/reference/methods/admin.teams.owners.list """ kwargs.update({"team_id": team_id, "cursor": cursor, "limit": limit}) return self.api_call("admin.teams.owners.list", http_verb="GET", params=kwargs) ``` List all of the admins on a given workspace. [https://docs.slack.dev/reference/methods/admin.teams.owners.list](https://docs.slack.dev/reference/methods/admin.teams.owners.list) `def admin_teams_settings_info(self, *, team_id: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_teams_settings_info( self, *, team_id: str, **kwargs, ) -> SlackResponse: """Fetch information about settings in a workspace https://docs.slack.dev/reference/methods/admin.teams.settings.info """ kwargs.update({"team_id": team_id}) return self.api_call("admin.teams.settings.info", params=kwargs) ``` Fetch information about settings in a workspace [https://docs.slack.dev/reference/methods/admin.teams.settings.info](https://docs.slack.dev/reference/methods/admin.teams.settings.info) `def admin_teams_settings_setDefaultChannels(self, *, team_id: str, channel_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_teams_settings_setDefaultChannels( self, *, team_id: str, channel_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Set the default channels of a workspace. https://docs.slack.dev/reference/methods/admin.teams.settings.setDefaultChannels """ kwargs.update({"team_id": team_id}) if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) return self.api_call("admin.teams.settings.setDefaultChannels", http_verb="GET", params=kwargs) ``` Set the default channels of a workspace. [https://docs.slack.dev/reference/methods/admin.teams.settings.setDefaultChannels](https://docs.slack.dev/reference/methods/admin.teams.settings.setDefaultChannels) `def admin_teams_settings_setDescription(self, *, team_id: str, description: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_teams_settings_setDescription( self, *, team_id: str, description: str, **kwargs, ) -> SlackResponse: """Set the description of a given workspace. https://docs.slack.dev/reference/methods/admin.teams.settings.setDescription """ kwargs.update({"team_id": team_id, "description": description}) return self.api_call("admin.teams.settings.setDescription", params=kwargs) ``` Set the description of a given workspace. [https://docs.slack.dev/reference/methods/admin.teams.settings.setDescription](https://docs.slack.dev/reference/methods/admin.teams.settings.setDescription) `def admin_teams_settings_setDiscoverability(self, *, team_id: str, discoverability: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_teams_settings_setDiscoverability( self, *, team_id: str, discoverability: str, **kwargs, ) -> SlackResponse: """Sets the icon of a workspace. https://docs.slack.dev/reference/methods/admin.teams.settings.setDiscoverability """ kwargs.update({"team_id": team_id, "discoverability": discoverability}) return self.api_call("admin.teams.settings.setDiscoverability", params=kwargs) ``` Sets the icon of a workspace. [https://docs.slack.dev/reference/methods/admin.teams.settings.setDiscoverability](https://docs.slack.dev/reference/methods/admin.teams.settings.setDiscoverability) `def admin_teams_settings_setIcon(self, *, team_id: str, image_url: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_teams_settings_setIcon( self, *, team_id: str, image_url: str, **kwargs, ) -> SlackResponse: """Sets the icon of a workspace. https://docs.slack.dev/reference/methods/admin.teams.settings.setIcon """ kwargs.update({"team_id": team_id, "image_url": image_url}) return self.api_call("admin.teams.settings.setIcon", http_verb="GET", params=kwargs) ``` Sets the icon of a workspace. [https://docs.slack.dev/reference/methods/admin.teams.settings.setIcon](https://docs.slack.dev/reference/methods/admin.teams.settings.setIcon) `def admin_teams_settings_setName(self, *, team_id: str, name: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_teams_settings_setName( self, *, team_id: str, name: str, **kwargs, ) -> SlackResponse: """Sets the icon of a workspace. https://docs.slack.dev/reference/methods/admin.teams.settings.setName """ kwargs.update({"team_id": team_id, "name": name}) return self.api_call("admin.teams.settings.setName", params=kwargs) ``` Sets the icon of a workspace. [https://docs.slack.dev/reference/methods/admin.teams.settings.setName](https://docs.slack.dev/reference/methods/admin.teams.settings.setName) `def admin_usergroups_addChannels(self, *, channel_ids: str | Sequence[str], usergroup_id: str, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_usergroups_addChannels( self, *, channel_ids: Union[str, Sequence[str]], usergroup_id: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Add one or more default channels to an IDP group. https://docs.slack.dev/reference/methods/admin.usergroups.addChannels """ kwargs.update({"team_id": team_id, "usergroup_id": usergroup_id}) if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) return self.api_call("admin.usergroups.addChannels", params=kwargs) ``` Add one or more default channels to an IDP group. [https://docs.slack.dev/reference/methods/admin.usergroups.addChannels](https://docs.slack.dev/reference/methods/admin.usergroups.addChannels) `def admin_usergroups_addTeams(self, *, usergroup_id: str, team_ids: str | Sequence[str], auto_provision: bool | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_usergroups_addTeams( self, *, usergroup_id: str, team_ids: Union[str, Sequence[str]], auto_provision: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Associate one or more default workspaces with an organization-wide IDP group. https://docs.slack.dev/reference/methods/admin.usergroups.addTeams """ kwargs.update({"usergroup_id": usergroup_id, "auto_provision": auto_provision}) if isinstance(team_ids, (list, tuple)): kwargs.update({"team_ids": ",".join(team_ids)}) else: kwargs.update({"team_ids": team_ids}) return self.api_call("admin.usergroups.addTeams", params=kwargs) ``` Associate one or more default workspaces with an organization-wide IDP group. [https://docs.slack.dev/reference/methods/admin.usergroups.addTeams](https://docs.slack.dev/reference/methods/admin.usergroups.addTeams) `def admin_usergroups_listChannels(self, *, usergroup_id: str, include_num_members: bool | None = None, team_id: bool | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_usergroups_listChannels( self, *, usergroup_id: str, include_num_members: Optional[bool] = None, team_id: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Add one or more default channels to an IDP group. https://docs.slack.dev/reference/methods/admin.usergroups.listChannels """ kwargs.update( { "usergroup_id": usergroup_id, "include_num_members": include_num_members, "team_id": team_id, } ) return self.api_call("admin.usergroups.listChannels", params=kwargs) ``` Add one or more default channels to an IDP group. [https://docs.slack.dev/reference/methods/admin.usergroups.listChannels](https://docs.slack.dev/reference/methods/admin.usergroups.listChannels) `def admin_usergroups_removeChannels(self, *, usergroup_id: str, channel_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_usergroups_removeChannels( self, *, usergroup_id: str, channel_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Add one or more default channels to an IDP group. https://docs.slack.dev/reference/methods/admin.usergroups.removeChannels """ kwargs.update({"usergroup_id": usergroup_id}) if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) return self.api_call("admin.usergroups.removeChannels", params=kwargs) ``` Add one or more default channels to an IDP group. [https://docs.slack.dev/reference/methods/admin.usergroups.removeChannels](https://docs.slack.dev/reference/methods/admin.usergroups.removeChannels) `def admin_users_assign(self, *, team_id: str, user_id: str, channel_ids: str | Sequence[str] | None = None, is_restricted: bool | None = None, is_ultra_restricted: bool | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_users_assign( self, *, team_id: str, user_id: str, channel_ids: Optional[Union[str, Sequence[str]]] = None, is_restricted: Optional[bool] = None, is_ultra_restricted: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Add an Enterprise user to a workspace. https://docs.slack.dev/reference/methods/admin.users.assign """ kwargs.update( { "team_id": team_id, "user_id": user_id, "is_restricted": is_restricted, "is_ultra_restricted": is_ultra_restricted, } ) if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) return self.api_call("admin.users.assign", params=kwargs) ``` Add an Enterprise user to a workspace. [https://docs.slack.dev/reference/methods/admin.users.assign](https://docs.slack.dev/reference/methods/admin.users.assign) `def admin_users_invite(self, *, team_id: str, email: str, channel_ids: str | Sequence[str], custom_message: str | None = None, email_password_policy_enabled: bool | None = None, guest_expiration_ts: str | float | None = None, is_restricted: bool | None = None, is_ultra_restricted: bool | None = None, real_name: str | None = None, resend: bool | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_users_invite( self, *, team_id: str, email: str, channel_ids: Union[str, Sequence[str]], custom_message: Optional[str] = None, email_password_policy_enabled: Optional[bool] = None, guest_expiration_ts: Optional[Union[str, float]] = None, is_restricted: Optional[bool] = None, is_ultra_restricted: Optional[bool] = None, real_name: Optional[str] = None, resend: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Invite a user to a workspace. https://docs.slack.dev/reference/methods/admin.users.invite """ kwargs.update( { "team_id": team_id, "email": email, "custom_message": custom_message, "email_password_policy_enabled": email_password_policy_enabled, "guest_expiration_ts": str(guest_expiration_ts) if guest_expiration_ts is not None else None, "is_restricted": is_restricted, "is_ultra_restricted": is_ultra_restricted, "real_name": real_name, "resend": resend, } ) if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) return self.api_call("admin.users.invite", params=kwargs) ``` Invite a user to a workspace. [https://docs.slack.dev/reference/methods/admin.users.invite](https://docs.slack.dev/reference/methods/admin.users.invite) `def admin_users_list(self, *, team_id: str | None = None, include_deactivated_user_workspaces: bool | None = None, is_active: bool | None = None, cursor: str | None = None, limit: int | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_users_list( self, *, team_id: Optional[str] = None, include_deactivated_user_workspaces: Optional[bool] = None, is_active: Optional[bool] = None, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """List users on a workspace https://docs.slack.dev/reference/methods/admin.users.list """ kwargs.update( { "team_id": team_id, "include_deactivated_user_workspaces": include_deactivated_user_workspaces, "is_active": is_active, "cursor": cursor, "limit": limit, } ) return self.api_call("admin.users.list", params=kwargs) ``` List users on a workspace [https://docs.slack.dev/reference/methods/admin.users.list](https://docs.slack.dev/reference/methods/admin.users.list) `def admin_users_remove(self, *, team_id: str, user_id: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_users_remove( self, *, team_id: str, user_id: str, **kwargs, ) -> SlackResponse: """Remove a user from a workspace. https://docs.slack.dev/reference/methods/admin.users.remove """ kwargs.update({"team_id": team_id, "user_id": user_id}) return self.api_call("admin.users.remove", params=kwargs) ``` Remove a user from a workspace. [https://docs.slack.dev/reference/methods/admin.users.remove](https://docs.slack.dev/reference/methods/admin.users.remove) `def admin_users_session_clearSettings(self, *, user_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_users_session_clearSettings( self, *, user_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Clear user-specific session settings—the session duration and what happens when the client closes—for a list of users. https://docs.slack.dev/reference/methods/admin.users.session.clearSettings """ if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) return self.api_call("admin.users.session.clearSettings", params=kwargs) ``` Clear user-specific session settings—the session duration and what happens when the client closes—for a list of users. [https://docs.slack.dev/reference/methods/admin.users.session.clearSettings](https://docs.slack.dev/reference/methods/admin.users.session.clearSettings) `def admin_users_session_getSettings(self, *, user_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_users_session_getSettings( self, *, user_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Get user-specific session settings—the session duration and what happens when the client closes—given a list of users. https://docs.slack.dev/reference/methods/admin.users.session.getSettings """ if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) return self.api_call("admin.users.session.getSettings", params=kwargs) ``` Get user-specific session settings—the session duration and what happens when the client closes—given a list of users. [https://docs.slack.dev/reference/methods/admin.users.session.getSettings](https://docs.slack.dev/reference/methods/admin.users.session.getSettings) `def admin_users_session_invalidate(self, *, session_id: str, team_id: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_users_session_invalidate( self, *, session_id: str, team_id: str, **kwargs, ) -> SlackResponse: """Invalidate a single session for a user by session_id. https://docs.slack.dev/reference/methods/admin.users.session.invalidate """ kwargs.update({"session_id": session_id, "team_id": team_id}) return self.api_call("admin.users.session.invalidate", params=kwargs) ``` Invalidate a single session for a user by session\_id. [https://docs.slack.dev/reference/methods/admin.users.session.invalidate](https://docs.slack.dev/reference/methods/admin.users.session.invalidate) `def admin_users_session_list(self, *, cursor: str | None = None, limit: int | None = None, team_id: str | None = None, user_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_users_session_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, team_id: Optional[str] = None, user_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists all active user sessions for an organization https://docs.slack.dev/reference/methods/admin.users.session.list """ kwargs.update( { "cursor": cursor, "limit": limit, "team_id": team_id, "user_id": user_id, } ) return self.api_call("admin.users.session.list", params=kwargs) ``` Lists all active user sessions for an organization [https://docs.slack.dev/reference/methods/admin.users.session.list](https://docs.slack.dev/reference/methods/admin.users.session.list) `def admin_users_session_reset(self, *, user_id: str, mobile_only: bool | None = None, web_only: bool | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_users_session_reset( self, *, user_id: str, mobile_only: Optional[bool] = None, web_only: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Wipes all valid sessions on all devices for a given user. https://docs.slack.dev/reference/methods/admin.users.session.reset """ kwargs.update( { "user_id": user_id, "mobile_only": mobile_only, "web_only": web_only, } ) return self.api_call("admin.users.session.reset", params=kwargs) ``` Wipes all valid sessions on all devices for a given user. [https://docs.slack.dev/reference/methods/admin.users.session.reset](https://docs.slack.dev/reference/methods/admin.users.session.reset) `def admin_users_session_resetBulk(self, *, user_ids: str | Sequence[str], mobile_only: bool | None = None, web_only: bool | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_users_session_resetBulk( self, *, user_ids: Union[str, Sequence[str]], mobile_only: Optional[bool] = None, web_only: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Enqueues an asynchronous job to wipe all valid sessions on all devices for a given list of users https://docs.slack.dev/reference/methods/admin.users.session.resetBulk """ if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) kwargs.update( { "mobile_only": mobile_only, "web_only": web_only, } ) return self.api_call("admin.users.session.resetBulk", params=kwargs) ``` Enqueues an asynchronous job to wipe all valid sessions on all devices for a given list of users [https://docs.slack.dev/reference/methods/admin.users.session.resetBulk](https://docs.slack.dev/reference/methods/admin.users.session.resetBulk) `def admin_users_session_setSettings(self, *, user_ids: str | Sequence[str], desktop_app_browser_quit: bool | None = None, duration: int | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_users_session_setSettings( self, *, user_ids: Union[str, Sequence[str]], desktop_app_browser_quit: Optional[bool] = None, duration: Optional[int] = None, **kwargs, ) -> SlackResponse: """Configure the user-level session settings—the session duration and what happens when the client closes—for one or more users. https://docs.slack.dev/reference/methods/admin.users.session.setSettings """ if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) kwargs.update( { "desktop_app_browser_quit": desktop_app_browser_quit, "duration": duration, } ) return self.api_call("admin.users.session.setSettings", params=kwargs) ``` Configure the user-level session settings—the session duration and what happens when the client closes—for one or more users. [https://docs.slack.dev/reference/methods/admin.users.session.setSettings](https://docs.slack.dev/reference/methods/admin.users.session.setSettings) `def admin_users_setAdmin(self, *, team_id: str, user_id: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_users_setAdmin( self, *, team_id: str, user_id: str, **kwargs, ) -> SlackResponse: """Set an existing guest, regular user, or owner to be an admin user. https://docs.slack.dev/reference/methods/admin.users.setAdmin """ kwargs.update({"team_id": team_id, "user_id": user_id}) return self.api_call("admin.users.setAdmin", params=kwargs) ``` Set an existing guest, regular user, or owner to be an admin user. [https://docs.slack.dev/reference/methods/admin.users.setAdmin](https://docs.slack.dev/reference/methods/admin.users.setAdmin) `def admin_users_setExpiration(self, *, expiration_ts: int, user_id: str, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_users_setExpiration( self, *, expiration_ts: int, user_id: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Set an expiration for a guest user. https://docs.slack.dev/reference/methods/admin.users.setExpiration """ kwargs.update({"expiration_ts": expiration_ts, "team_id": team_id, "user_id": user_id}) return self.api_call("admin.users.setExpiration", params=kwargs) ``` Set an expiration for a guest user. [https://docs.slack.dev/reference/methods/admin.users.setExpiration](https://docs.slack.dev/reference/methods/admin.users.setExpiration) `def admin_users_setOwner(self, *, team_id: str, user_id: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_users_setOwner( self, *, team_id: str, user_id: str, **kwargs, ) -> SlackResponse: """Set an existing guest, regular user, or admin user to be a workspace owner. https://docs.slack.dev/reference/methods/admin.users.setOwner """ kwargs.update({"team_id": team_id, "user_id": user_id}) return self.api_call("admin.users.setOwner", params=kwargs) ``` Set an existing guest, regular user, or admin user to be a workspace owner. [https://docs.slack.dev/reference/methods/admin.users.setOwner](https://docs.slack.dev/reference/methods/admin.users.setOwner) `def admin_users_setRegular(self, *, team_id: str, user_id: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_users_setRegular( self, *, team_id: str, user_id: str, **kwargs, ) -> SlackResponse: """Set an existing guest user, admin user, or owner to be a regular user. https://docs.slack.dev/reference/methods/admin.users.setRegular """ kwargs.update({"team_id": team_id, "user_id": user_id}) return self.api_call("admin.users.setRegular", params=kwargs) ``` Set an existing guest user, admin user, or owner to be a regular user. [https://docs.slack.dev/reference/methods/admin.users.setRegular](https://docs.slack.dev/reference/methods/admin.users.setRegular) `def admin_users_unsupportedVersions_export(self, *, date_end_of_support: str | int | None = None, date_sessions_started: str | int | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_users_unsupportedVersions_export( self, *, date_end_of_support: Optional[Union[str, int]] = None, date_sessions_started: Optional[Union[str, int]] = None, **kwargs, ) -> SlackResponse: """Ask Slackbot to send you an export listing all workspace members using unsupported software, presented as a zipped CSV file. https://docs.slack.dev/reference/methods/admin.users.unsupportedVersions.export """ kwargs.update( { "date_end_of_support": date_end_of_support, "date_sessions_started": date_sessions_started, } ) return self.api_call("admin.users.unsupportedVersions.export", params=kwargs) ``` Ask Slackbot to send you an export listing all workspace members using unsupported software, presented as a zipped CSV file. [https://docs.slack.dev/reference/methods/admin.users.unsupportedVersions.export](https://docs.slack.dev/reference/methods/admin.users.unsupportedVersions.export) `def admin_workflows_collaborators_add(self, *, collaborator_ids: str | Sequence[str], workflow_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_workflows_collaborators_add( self, *, collaborator_ids: Union[str, Sequence[str]], workflow_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Add collaborators to workflows within the team or enterprise https://docs.slack.dev/reference/methods/admin.workflows.collaborators.add """ if isinstance(collaborator_ids, (list, tuple)): kwargs.update({"collaborator_ids": ",".join(collaborator_ids)}) else: kwargs.update({"collaborator_ids": collaborator_ids}) if isinstance(workflow_ids, (list, tuple)): kwargs.update({"workflow_ids": ",".join(workflow_ids)}) else: kwargs.update({"workflow_ids": workflow_ids}) return self.api_call("admin.workflows.collaborators.add", params=kwargs) ``` Add collaborators to workflows within the team or enterprise [https://docs.slack.dev/reference/methods/admin.workflows.collaborators.add](https://docs.slack.dev/reference/methods/admin.workflows.collaborators.add) `def admin_workflows_collaborators_remove(self, *, collaborator_ids: str | Sequence[str], workflow_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_workflows_collaborators_remove( self, *, collaborator_ids: Union[str, Sequence[str]], workflow_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Remove collaborators from workflows within the team or enterprise https://docs.slack.dev/reference/methods/admin.workflows.collaborators.remove """ if isinstance(collaborator_ids, (list, tuple)): kwargs.update({"collaborator_ids": ",".join(collaborator_ids)}) else: kwargs.update({"collaborator_ids": collaborator_ids}) if isinstance(workflow_ids, (list, tuple)): kwargs.update({"workflow_ids": ",".join(workflow_ids)}) else: kwargs.update({"workflow_ids": workflow_ids}) return self.api_call("admin.workflows.collaborators.remove", params=kwargs) ``` Remove collaborators from workflows within the team or enterprise [https://docs.slack.dev/reference/methods/admin.workflows.collaborators.remove](https://docs.slack.dev/reference/methods/admin.workflows.collaborators.remove) `def admin_workflows_permissions_lookup(self, *, workflow_ids: str | Sequence[str], max_workflow_triggers: int | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_workflows_permissions_lookup( self, *, workflow_ids: Union[str, Sequence[str]], max_workflow_triggers: Optional[int] = None, **kwargs, ) -> SlackResponse: """Look up the permissions for a set of workflows https://docs.slack.dev/reference/methods/admin.workflows.permissions.lookup """ if isinstance(workflow_ids, (list, tuple)): kwargs.update({"workflow_ids": ",".join(workflow_ids)}) else: kwargs.update({"workflow_ids": workflow_ids}) kwargs.update( { "max_workflow_triggers": max_workflow_triggers, } ) return self.api_call("admin.workflows.permissions.lookup", params=kwargs) ``` Look up the permissions for a set of workflows [https://docs.slack.dev/reference/methods/admin.workflows.permissions.lookup](https://docs.slack.dev/reference/methods/admin.workflows.permissions.lookup) `def admin_workflows_search(self, *, app_id: str | None = None, collaborator_ids: str | Sequence[str] | None = None, cursor: str | None = None, limit: int | None = None, no_collaborators: bool | None = None, num_trigger_ids: int | None = None, query: str | None = None, sort: str | None = None, sort_dir: str | None = None, source: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_workflows_search( self, *, app_id: Optional[str] = None, collaborator_ids: Optional[Union[str, Sequence[str]]] = None, cursor: Optional[str] = None, limit: Optional[int] = None, no_collaborators: Optional[bool] = None, num_trigger_ids: Optional[int] = None, query: Optional[str] = None, sort: Optional[str] = None, sort_dir: Optional[str] = None, source: Optional[str] = None, **kwargs, ) -> SlackResponse: """Search workflows within the team or enterprise https://docs.slack.dev/reference/methods/admin.workflows.search """ if collaborator_ids is not None: if isinstance(collaborator_ids, (list, tuple)): kwargs.update({"collaborator_ids": ",".join(collaborator_ids)}) else: kwargs.update({"collaborator_ids": collaborator_ids}) kwargs.update( { "app_id": app_id, "cursor": cursor, "limit": limit, "no_collaborators": no_collaborators, "num_trigger_ids": num_trigger_ids, "query": query, "sort": sort, "sort_dir": sort_dir, "source": source, } ) return self.api_call("admin.workflows.search", params=kwargs) ``` Search workflows within the team or enterprise [https://docs.slack.dev/reference/methods/admin.workflows.search](https://docs.slack.dev/reference/methods/admin.workflows.search) `def admin_workflows_unpublish(self, *, workflow_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_workflows_unpublish( self, *, workflow_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Unpublish workflows within the team or enterprise https://docs.slack.dev/reference/methods/admin.workflows.unpublish """ if isinstance(workflow_ids, (list, tuple)): kwargs.update({"workflow_ids": ",".join(workflow_ids)}) else: kwargs.update({"workflow_ids": workflow_ids}) return self.api_call("admin.workflows.unpublish", params=kwargs) ``` Unpublish workflows within the team or enterprise [https://docs.slack.dev/reference/methods/admin.workflows.unpublish](https://docs.slack.dev/reference/methods/admin.workflows.unpublish) `def api_test(self, *, error: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def api_test( self, *, error: Optional[str] = None, **kwargs, ) -> SlackResponse: """Checks API calling code. https://docs.slack.dev/reference/methods/api.test """ kwargs.update({"error": error}) return self.api_call("api.test", params=kwargs) ``` Checks API calling code. [https://docs.slack.dev/reference/methods/api.test](https://docs.slack.dev/reference/methods/api.test) `def apps_connections_open(self, *, app_token: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def apps_connections_open( self, *, app_token: str, **kwargs, ) -> SlackResponse: """Generate a temporary Socket Mode WebSocket URL that your app can connect to in order to receive events and interactive payloads https://docs.slack.dev/reference/methods/apps.connections.open """ kwargs.update({"token": app_token}) return self.api_call("apps.connections.open", http_verb="POST", params=kwargs) ``` Generate a temporary Socket Mode WebSocket URL that your app can connect to in order to receive events and interactive payloads [https://docs.slack.dev/reference/methods/apps.connections.open](https://docs.slack.dev/reference/methods/apps.connections.open) `def apps_event_authorizations_list(self, *, event_context: str, cursor: str | None = None, limit: int | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def apps_event_authorizations_list( self, *, event_context: str, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Get a list of authorizations for the given event context. Each authorization represents an app installation that the event is visible to. https://docs.slack.dev/reference/methods/apps.event.authorizations.list """ kwargs.update({"event_context": event_context, "cursor": cursor, "limit": limit}) return self.api_call("apps.event.authorizations.list", params=kwargs) ``` Get a list of authorizations for the given event context. Each authorization represents an app installation that the event is visible to. [https://docs.slack.dev/reference/methods/apps.event.authorizations.list](https://docs.slack.dev/reference/methods/apps.event.authorizations.list) `def apps_manifest_create(self, *, manifest: str | Dict[str, Any], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def apps_manifest_create( self, *, manifest: Union[str, Dict[str, Any]], **kwargs, ) -> SlackResponse: """Create an app from an app manifest https://docs.slack.dev/reference/methods/apps.manifest.create """ if isinstance(manifest, str): kwargs.update({"manifest": manifest}) else: kwargs.update({"manifest": json.dumps(manifest)}) return self.api_call("apps.manifest.create", params=kwargs) ``` Create an app from an app manifest [https://docs.slack.dev/reference/methods/apps.manifest.create](https://docs.slack.dev/reference/methods/apps.manifest.create) `def apps_manifest_delete(self, *, app_id: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def apps_manifest_delete( self, *, app_id: str, **kwargs, ) -> SlackResponse: """Permanently deletes an app created through app manifests https://docs.slack.dev/reference/methods/apps.manifest.delete """ kwargs.update({"app_id": app_id}) return self.api_call("apps.manifest.delete", params=kwargs) ``` Permanently deletes an app created through app manifests [https://docs.slack.dev/reference/methods/apps.manifest.delete](https://docs.slack.dev/reference/methods/apps.manifest.delete) `def apps_manifest_export(self, *, app_id: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def apps_manifest_export( self, *, app_id: str, **kwargs, ) -> SlackResponse: """Export an app manifest from an existing app https://docs.slack.dev/reference/methods/apps.manifest.export """ kwargs.update({"app_id": app_id}) return self.api_call("apps.manifest.export", params=kwargs) ``` Export an app manifest from an existing app [https://docs.slack.dev/reference/methods/apps.manifest.export](https://docs.slack.dev/reference/methods/apps.manifest.export) `def apps_manifest_update(self, *, app_id: str, manifest: str | Dict[str, Any], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def apps_manifest_update( self, *, app_id: str, manifest: Union[str, Dict[str, Any]], **kwargs, ) -> SlackResponse: """Update an app from an app manifest https://docs.slack.dev/reference/methods/apps.manifest.update """ if isinstance(manifest, str): kwargs.update({"manifest": manifest}) else: kwargs.update({"manifest": json.dumps(manifest)}) kwargs.update({"app_id": app_id}) return self.api_call("apps.manifest.update", params=kwargs) ``` Update an app from an app manifest [https://docs.slack.dev/reference/methods/apps.manifest.update](https://docs.slack.dev/reference/methods/apps.manifest.update) `def apps_manifest_validate(self, *, manifest: str | Dict[str, Any], app_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def apps_manifest_validate( self, *, manifest: Union[str, Dict[str, Any]], app_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Validate an app manifest https://docs.slack.dev/reference/methods/apps.manifest.validate """ if isinstance(manifest, str): kwargs.update({"manifest": manifest}) else: kwargs.update({"manifest": json.dumps(manifest)}) kwargs.update({"app_id": app_id}) return self.api_call("apps.manifest.validate", params=kwargs) ``` Validate an app manifest [https://docs.slack.dev/reference/methods/apps.manifest.validate](https://docs.slack.dev/reference/methods/apps.manifest.validate) `def apps_uninstall(self, *, client_id: str, client_secret: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def apps_uninstall( self, *, client_id: str, client_secret: str, **kwargs, ) -> SlackResponse: """Uninstalls your app from a workspace. https://docs.slack.dev/reference/methods/apps.uninstall """ kwargs.update({"client_id": client_id, "client_secret": client_secret}) return self.api_call("apps.uninstall", params=kwargs) ``` Uninstalls your app from a workspace. [https://docs.slack.dev/reference/methods/apps.uninstall](https://docs.slack.dev/reference/methods/apps.uninstall) `def apps_user_connection_update(self, *, user_id: str, status: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def apps_user_connection_update( self, *, user_id: str, status: str, **kwargs, ) -> SlackResponse: """Updates the connection status between a user and an app. https://docs.slack.dev/reference/methods/apps.user.connection.update """ kwargs.update({"user_id": user_id, "status": status}) return self.api_call("apps.user.connection.update", params=kwargs) ``` Updates the connection status between a user and an app. [https://docs.slack.dev/reference/methods/apps.user.connection.update](https://docs.slack.dev/reference/methods/apps.user.connection.update) `def assistant_threads_setStatus(self, *, channel_id: str, thread_ts: str, status: str, loading_messages: List[str] | None = None, icon_emoji: str | None = None, icon_url: str | None = None, username: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def assistant_threads_setStatus( self, *, channel_id: str, thread_ts: str, status: str, loading_messages: Optional[List[str]] = None, icon_emoji: Optional[str] = None, icon_url: Optional[str] = None, username: Optional[str] = None, **kwargs, ) -> SlackResponse: """Set the status for an AI assistant thread. https://docs.slack.dev/reference/methods/assistant.threads.setStatus """ kwargs.update( { "channel_id": channel_id, "thread_ts": thread_ts, "status": status, "loading_messages": loading_messages, "icon_emoji": icon_emoji, "icon_url": icon_url, "username": username, } ) kwargs = _remove_none_values(kwargs) return self.api_call("assistant.threads.setStatus", json=kwargs) ``` Set the status for an AI assistant thread. [https://docs.slack.dev/reference/methods/assistant.threads.setStatus](https://docs.slack.dev/reference/methods/assistant.threads.setStatus) `def assistant_threads_setSuggestedPrompts(self, *, channel_id: str, thread_ts: str | None = None, title: str | None = None, prompts: List[Dict[str, str]], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def assistant_threads_setSuggestedPrompts( self, *, channel_id: str, thread_ts: Optional[str] = None, title: Optional[str] = None, prompts: List[Dict[str, str]], **kwargs, ) -> SlackResponse: """Set suggested prompts for the given assistant thread. https://docs.slack.dev/reference/methods/assistant.threads.setSuggestedPrompts """ kwargs.update({"channel_id": channel_id, "prompts": prompts}) if thread_ts is not None: kwargs.update({"thread_ts": thread_ts}) if title is not None: kwargs.update({"title": title}) return self.api_call("assistant.threads.setSuggestedPrompts", json=kwargs) ``` Set suggested prompts for the given assistant thread. [https://docs.slack.dev/reference/methods/assistant.threads.setSuggestedPrompts](https://docs.slack.dev/reference/methods/assistant.threads.setSuggestedPrompts) `def assistant_threads_setTitle(self, *, channel_id: str, thread_ts: str, title: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def assistant_threads_setTitle( self, *, channel_id: str, thread_ts: str, title: str, **kwargs, ) -> SlackResponse: """Set the title for the given assistant thread. https://docs.slack.dev/reference/methods/assistant.threads.setTitle """ kwargs.update({"channel_id": channel_id, "thread_ts": thread_ts, "title": title}) return self.api_call("assistant.threads.setTitle", params=kwargs) ``` Set the title for the given assistant thread. [https://docs.slack.dev/reference/methods/assistant.threads.setTitle](https://docs.slack.dev/reference/methods/assistant.threads.setTitle) `def auth_revoke(self, *, test: bool | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def auth_revoke( self, *, test: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Revokes a token. https://docs.slack.dev/reference/methods/auth.revoke """ kwargs.update({"test": test}) return self.api_call("auth.revoke", http_verb="GET", params=kwargs) ``` Revokes a token. [https://docs.slack.dev/reference/methods/auth.revoke](https://docs.slack.dev/reference/methods/auth.revoke) `def auth_teams_list(self, cursor: str | None = None, limit: int | None = None, include_icon: bool | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def auth_teams_list( self, cursor: Optional[str] = None, limit: Optional[int] = None, include_icon: Optional[bool] = None, **kwargs, ) -> SlackResponse: """List the workspaces a token can access. https://docs.slack.dev/reference/methods/auth.teams.list """ kwargs.update({"cursor": cursor, "limit": limit, "include_icon": include_icon}) return self.api_call("auth.teams.list", params=kwargs) ``` List the workspaces a token can access. [https://docs.slack.dev/reference/methods/auth.teams.list](https://docs.slack.dev/reference/methods/auth.teams.list) `def auth_test(self, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def auth_test( self, **kwargs, ) -> SlackResponse: """Checks authentication & identity. https://docs.slack.dev/reference/methods/auth.test """ return self.api_call("auth.test", params=kwargs) ``` Checks authentication & identity. [https://docs.slack.dev/reference/methods/auth.test](https://docs.slack.dev/reference/methods/auth.test) `def bookmarks_add(self, *, channel_id: str, title: str, type: str, emoji: str | None = None, entity_id: str | None = None, link: str | None = None, parent_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def bookmarks_add( self, *, channel_id: str, title: str, type: str, emoji: Optional[str] = None, entity_id: Optional[str] = None, link: Optional[str] = None, # include when type is 'link' parent_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Add bookmark to a channel. https://docs.slack.dev/reference/methods/bookmarks.add """ kwargs.update( { "channel_id": channel_id, "title": title, "type": type, "emoji": emoji, "entity_id": entity_id, "link": link, "parent_id": parent_id, } ) return self.api_call("bookmarks.add", http_verb="POST", params=kwargs) ``` Add bookmark to a channel. [https://docs.slack.dev/reference/methods/bookmarks.add](https://docs.slack.dev/reference/methods/bookmarks.add) `def bookmarks_edit(self, *, bookmark_id: str, channel_id: str, emoji: str | None = None, link: str | None = None, title: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def bookmarks_edit( self, *, bookmark_id: str, channel_id: str, emoji: Optional[str] = None, link: Optional[str] = None, title: Optional[str] = None, **kwargs, ) -> SlackResponse: """Edit bookmark. https://docs.slack.dev/reference/methods/bookmarks.edit """ kwargs.update( { "bookmark_id": bookmark_id, "channel_id": channel_id, "emoji": emoji, "link": link, "title": title, } ) return self.api_call("bookmarks.edit", http_verb="POST", params=kwargs) ``` Edit bookmark. [https://docs.slack.dev/reference/methods/bookmarks.edit](https://docs.slack.dev/reference/methods/bookmarks.edit) `def bookmarks_list(self, *, channel_id: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def bookmarks_list( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """List bookmark for the channel. https://docs.slack.dev/reference/methods/bookmarks.list """ kwargs.update({"channel_id": channel_id}) return self.api_call("bookmarks.list", http_verb="POST", params=kwargs) ``` List bookmark for the channel. [https://docs.slack.dev/reference/methods/bookmarks.list](https://docs.slack.dev/reference/methods/bookmarks.list) `def bookmarks_remove(self, *, bookmark_id: str, channel_id: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def bookmarks_remove( self, *, bookmark_id: str, channel_id: str, **kwargs, ) -> SlackResponse: """Remove bookmark from the channel. https://docs.slack.dev/reference/methods/bookmarks.remove """ kwargs.update({"bookmark_id": bookmark_id, "channel_id": channel_id}) return self.api_call("bookmarks.remove", http_verb="POST", params=kwargs) ``` Remove bookmark from the channel. [https://docs.slack.dev/reference/methods/bookmarks.remove](https://docs.slack.dev/reference/methods/bookmarks.remove) `def bots_info(self, *, bot: str | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def bots_info( self, *, bot: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Gets information about a bot user. https://docs.slack.dev/reference/methods/bots.info """ kwargs.update({"bot": bot, "team_id": team_id}) return self.api_call("bots.info", http_verb="GET", params=kwargs) ``` Gets information about a bot user. [https://docs.slack.dev/reference/methods/bots.info](https://docs.slack.dev/reference/methods/bots.info) `def calls_add(self, *, external_unique_id: str, join_url: str, created_by: str | None = None, date_start: int | None = None, desktop_app_join_url: str | None = None, external_display_id: str | None = None, title: str | None = None, users: str | Sequence[Dict[str, str]] | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def calls_add( self, *, external_unique_id: str, join_url: str, created_by: Optional[str] = None, date_start: Optional[int] = None, desktop_app_join_url: Optional[str] = None, external_display_id: Optional[str] = None, title: Optional[str] = None, users: Optional[Union[str, Sequence[Dict[str, str]]]] = None, **kwargs, ) -> SlackResponse: """Registers a new Call. https://docs.slack.dev/reference/methods/calls.add """ kwargs.update( { "external_unique_id": external_unique_id, "join_url": join_url, "created_by": created_by, "date_start": date_start, "desktop_app_join_url": desktop_app_join_url, "external_display_id": external_display_id, "title": title, } ) _update_call_participants( kwargs, users if users is not None else kwargs.get("users"), # type: ignore[arg-type] ) return self.api_call("calls.add", http_verb="POST", params=kwargs) ``` Registers a new Call. [https://docs.slack.dev/reference/methods/calls.add](https://docs.slack.dev/reference/methods/calls.add) `def calls_end(self, *, id: str, duration: int | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def calls_end( self, *, id: str, duration: Optional[int] = None, **kwargs, ) -> SlackResponse: """Ends a Call. https://docs.slack.dev/reference/methods/calls.end """ kwargs.update({"id": id, "duration": duration}) return self.api_call("calls.end", http_verb="POST", params=kwargs) ``` Ends a Call. [https://docs.slack.dev/reference/methods/calls.end](https://docs.slack.dev/reference/methods/calls.end) `def calls_info(self, *, id: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def calls_info( self, *, id: str, **kwargs, ) -> SlackResponse: """Returns information about a Call. https://docs.slack.dev/reference/methods/calls.info """ kwargs.update({"id": id}) return self.api_call("calls.info", http_verb="POST", params=kwargs) ``` Returns information about a Call. [https://docs.slack.dev/reference/methods/calls.info](https://docs.slack.dev/reference/methods/calls.info) `def calls_participants_add(self, *, id: str, users: str | Sequence[Dict[str, str]], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def calls_participants_add( self, *, id: str, users: Union[str, Sequence[Dict[str, str]]], **kwargs, ) -> SlackResponse: """Registers new participants added to a Call. https://docs.slack.dev/reference/methods/calls.participants.add """ kwargs.update({"id": id}) _update_call_participants(kwargs, users) return self.api_call("calls.participants.add", http_verb="POST", params=kwargs) ``` Registers new participants added to a Call. [https://docs.slack.dev/reference/methods/calls.participants.add](https://docs.slack.dev/reference/methods/calls.participants.add) `def calls_participants_remove(self, *, id: str, users: str | Sequence[Dict[str, str]], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def calls_participants_remove( self, *, id: str, users: Union[str, Sequence[Dict[str, str]]], **kwargs, ) -> SlackResponse: """Registers participants removed from a Call. https://docs.slack.dev/reference/methods/calls.participants.remove """ kwargs.update({"id": id}) _update_call_participants(kwargs, users) return self.api_call("calls.participants.remove", http_verb="POST", params=kwargs) ``` Registers participants removed from a Call. [https://docs.slack.dev/reference/methods/calls.participants.remove](https://docs.slack.dev/reference/methods/calls.participants.remove) `def calls_update(self, *, id: str, desktop_app_join_url: str | None = None, join_url: str | None = None, title: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def calls_update( self, *, id: str, desktop_app_join_url: Optional[str] = None, join_url: Optional[str] = None, title: Optional[str] = None, **kwargs, ) -> SlackResponse: """Updates information about a Call. https://docs.slack.dev/reference/methods/calls.update """ kwargs.update( { "id": id, "desktop_app_join_url": desktop_app_join_url, "join_url": join_url, "title": title, } ) return self.api_call("calls.update", http_verb="POST", params=kwargs) ``` Updates information about a Call. [https://docs.slack.dev/reference/methods/calls.update](https://docs.slack.dev/reference/methods/calls.update) `def canvases_access_delete(self, *, canvas_id: str, channel_ids: str | Sequence[str] | None = None, user_ids: str | Sequence[str] | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def canvases_access_delete( self, *, canvas_id: str, channel_ids: Optional[Union[Sequence[str], str]] = None, user_ids: Optional[Union[Sequence[str], str]] = None, **kwargs, ) -> SlackResponse: """Create a Channel Canvas for a channel https://docs.slack.dev/reference/methods/canvases.access.delete """ kwargs.update({"canvas_id": canvas_id}) if channel_ids is not None: if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) if user_ids is not None: if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) return self.api_call("canvases.access.delete", params=kwargs) ``` Create a Channel Canvas for a channel [https://docs.slack.dev/reference/methods/canvases.access.delete](https://docs.slack.dev/reference/methods/canvases.access.delete) `def canvases_access_set(self, *, canvas_id: str, access_level: str, channel_ids: str | Sequence[str] | None = None, user_ids: str | Sequence[str] | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def canvases_access_set( self, *, canvas_id: str, access_level: str, channel_ids: Optional[Union[Sequence[str], str]] = None, user_ids: Optional[Union[Sequence[str], str]] = None, **kwargs, ) -> SlackResponse: """Sets the access level to a canvas for specified entities https://docs.slack.dev/reference/methods/canvases.access.set """ kwargs.update({"canvas_id": canvas_id, "access_level": access_level}) if channel_ids is not None: if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) if user_ids is not None: if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) return self.api_call("canvases.access.set", params=kwargs) ``` Sets the access level to a canvas for specified entities [https://docs.slack.dev/reference/methods/canvases.access.set](https://docs.slack.dev/reference/methods/canvases.access.set) `def canvases_create(self, *, title: str | None = None, document_content: Dict[str, str], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def canvases_create( self, *, title: Optional[str] = None, document_content: Dict[str, str], **kwargs, ) -> SlackResponse: """Create Canvas for a user https://docs.slack.dev/reference/methods/canvases.create """ kwargs.update({"title": title, "document_content": document_content}) return self.api_call("canvases.create", json=kwargs) ``` Create Canvas for a user [https://docs.slack.dev/reference/methods/canvases.create](https://docs.slack.dev/reference/methods/canvases.create) `def canvases_delete(self, *, canvas_id: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def canvases_delete( self, *, canvas_id: str, **kwargs, ) -> SlackResponse: """Deletes a canvas https://docs.slack.dev/reference/methods/canvases.delete """ kwargs.update({"canvas_id": canvas_id}) return self.api_call("canvases.delete", params=kwargs) ``` Deletes a canvas [https://docs.slack.dev/reference/methods/canvases.delete](https://docs.slack.dev/reference/methods/canvases.delete) `def canvases_edit(self, *, canvas_id: str, changes: Sequence[Dict[str, Any]], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def canvases_edit( self, *, canvas_id: str, changes: Sequence[Dict[str, Any]], **kwargs, ) -> SlackResponse: """Update an existing canvas https://docs.slack.dev/reference/methods/canvases.edit """ kwargs.update({"canvas_id": canvas_id, "changes": changes}) return self.api_call("canvases.edit", json=kwargs) ``` Update an existing canvas [https://docs.slack.dev/reference/methods/canvases.edit](https://docs.slack.dev/reference/methods/canvases.edit) `def canvases_sections_lookup(self, *, canvas_id: str, criteria: Dict[str, Any], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def canvases_sections_lookup( self, *, canvas_id: str, criteria: Dict[str, Any], **kwargs, ) -> SlackResponse: """Find sections matching the provided criteria https://docs.slack.dev/reference/methods/canvases.sections.lookup """ kwargs.update({"canvas_id": canvas_id, "criteria": json.dumps(criteria)}) return self.api_call("canvases.sections.lookup", params=kwargs) ``` Find sections matching the provided criteria [https://docs.slack.dev/reference/methods/canvases.sections.lookup](https://docs.slack.dev/reference/methods/canvases.sections.lookup) `def channels_archive(self, *, channel: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def channels_archive( self, *, channel: str, **kwargs, ) -> SlackResponse: """Archives a channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.archive", json=kwargs) ``` Archives a channel. `def channels_create(self, *, name: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def channels_create( self, *, name: str, **kwargs, ) -> SlackResponse: """Creates a channel.""" kwargs.update({"name": name}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.create", json=kwargs) ``` Creates a channel. `def channels_history(self, *, channel: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def channels_history( self, *, channel: str, **kwargs, ) -> SlackResponse: """Fetches history of messages and events from a channel.""" kwargs.update({"channel": channel}) return self.api_call("channels.history", http_verb="GET", params=kwargs) ``` Fetches history of messages and events from a channel. `def channels_info(self, *, channel: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def channels_info( self, *, channel: str, **kwargs, ) -> SlackResponse: """Gets information about a channel.""" kwargs.update({"channel": channel}) return self.api_call("channels.info", http_verb="GET", params=kwargs) ``` Gets information about a channel. `def channels_invite(self, *, channel: str, user: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def channels_invite( self, *, channel: str, user: str, **kwargs, ) -> SlackResponse: """Invites a user to a channel.""" kwargs.update({"channel": channel, "user": user}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.invite", json=kwargs) ``` Invites a user to a channel. `def channels_join(self, *, name: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def channels_join( self, *, name: str, **kwargs, ) -> SlackResponse: """Joins a channel, creating it if needed.""" kwargs.update({"name": name}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.join", json=kwargs) ``` Joins a channel, creating it if needed. `def channels_kick(self, *, channel: str, user: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def channels_kick( self, *, channel: str, user: str, **kwargs, ) -> SlackResponse: """Removes a user from a channel.""" kwargs.update({"channel": channel, "user": user}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.kick", json=kwargs) ``` Removes a user from a channel. `def channels_leave(self, *, channel: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def channels_leave( self, *, channel: str, **kwargs, ) -> SlackResponse: """Leaves a channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.leave", json=kwargs) ``` Leaves a channel. `def channels_list(self, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def channels_list( self, **kwargs, ) -> SlackResponse: """Lists all channels in a Slack team.""" return self.api_call("channels.list", http_verb="GET", params=kwargs) ``` Lists all channels in a Slack team. `def channels_mark(self, *, channel: str, ts: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def channels_mark( self, *, channel: str, ts: str, **kwargs, ) -> SlackResponse: """Sets the read cursor in a channel.""" kwargs.update({"channel": channel, "ts": ts}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.mark", json=kwargs) ``` Sets the read cursor in a channel. `def channels_rename(self, *, channel: str, name: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def channels_rename( self, *, channel: str, name: str, **kwargs, ) -> SlackResponse: """Renames a channel.""" kwargs.update({"channel": channel, "name": name}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.rename", json=kwargs) ``` Renames a channel. `def channels_replies(self, *, channel: str, thread_ts: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def channels_replies( self, *, channel: str, thread_ts: str, **kwargs, ) -> SlackResponse: """Retrieve a thread of messages posted to a channel""" kwargs.update({"channel": channel, "thread_ts": thread_ts}) return self.api_call("channels.replies", http_verb="GET", params=kwargs) ``` Retrieve a thread of messages posted to a channel `def channels_setPurpose(self, *, channel: str, purpose: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def channels_setPurpose( self, *, channel: str, purpose: str, **kwargs, ) -> SlackResponse: """Sets the purpose for a channel.""" kwargs.update({"channel": channel, "purpose": purpose}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.setPurpose", json=kwargs) ``` Sets the purpose for a channel. `def channels_setTopic(self, *, channel: str, topic: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def channels_setTopic( self, *, channel: str, topic: str, **kwargs, ) -> SlackResponse: """Sets the topic for a channel.""" kwargs.update({"channel": channel, "topic": topic}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.setTopic", json=kwargs) ``` Sets the topic for a channel. `def channels_unarchive(self, *, channel: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def channels_unarchive( self, *, channel: str, **kwargs, ) -> SlackResponse: """Unarchives a channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.unarchive", json=kwargs) ``` Unarchives a channel. `def chat_appendStream(self, *, channel: str, ts: str, markdown_text: str | None = None, chunks: Sequence[Dict | [Chunk](models/messages/chunk.html#slack_sdk.models.messages.chunk.Chunk "slack_sdk.models.messages.chunk.Chunk")] | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def chat_appendStream( self, *, channel: str, ts: str, markdown_text: Optional[str] = None, chunks: Optional[Sequence[Union[Dict, Chunk]]] = None, **kwargs, ) -> SlackResponse: """Appends text to an existing streaming conversation. https://docs.slack.dev/reference/methods/chat.appendStream """ kwargs.update( { "channel": channel, "ts": ts, "markdown_text": markdown_text, "chunks": chunks, } ) _parse_web_class_objects(kwargs) kwargs = _remove_none_values(kwargs) return self.api_call("chat.appendStream", json=kwargs) ``` Appends text to an existing streaming conversation. [https://docs.slack.dev/reference/methods/chat.appendStream](https://docs.slack.dev/reference/methods/chat.appendStream) `def chat_delete(self, *, channel: str, ts: str, as_user: bool | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def chat_delete( self, *, channel: str, ts: str, as_user: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Deletes a message. https://docs.slack.dev/reference/methods/chat.delete """ kwargs.update({"channel": channel, "ts": ts, "as_user": as_user}) return self.api_call("chat.delete", params=kwargs) ``` Deletes a message. [https://docs.slack.dev/reference/methods/chat.delete](https://docs.slack.dev/reference/methods/chat.delete) `def chat_deleteScheduledMessage(self, *, channel: str, scheduled_message_id: str, as_user: bool | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def chat_deleteScheduledMessage( self, *, channel: str, scheduled_message_id: str, as_user: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Deletes a scheduled message. https://docs.slack.dev/reference/methods/chat.deleteScheduledMessage """ kwargs.update( { "channel": channel, "scheduled_message_id": scheduled_message_id, "as_user": as_user, } ) return self.api_call("chat.deleteScheduledMessage", params=kwargs) ``` Deletes a scheduled message. [https://docs.slack.dev/reference/methods/chat.deleteScheduledMessage](https://docs.slack.dev/reference/methods/chat.deleteScheduledMessage) `def chat_getPermalink(self, *, channel: str, message_ts: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def chat_getPermalink( self, *, channel: str, message_ts: str, **kwargs, ) -> SlackResponse: """Retrieve a permalink URL for a specific extant message https://docs.slack.dev/reference/methods/chat.getPermalink """ kwargs.update({"channel": channel, "message_ts": message_ts}) return self.api_call("chat.getPermalink", http_verb="GET", params=kwargs) ``` Retrieve a permalink URL for a specific extant message [https://docs.slack.dev/reference/methods/chat.getPermalink](https://docs.slack.dev/reference/methods/chat.getPermalink) `def chat_meMessage(self, *, channel: str, text: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def chat_meMessage( self, *, channel: str, text: str, **kwargs, ) -> SlackResponse: """Share a me message into a channel. https://docs.slack.dev/reference/methods/chat.meMessage """ kwargs.update({"channel": channel, "text": text}) return self.api_call("chat.meMessage", params=kwargs) ``` Share a me message into a channel. [https://docs.slack.dev/reference/methods/chat.meMessage](https://docs.slack.dev/reference/methods/chat.meMessage) `def chat_postEphemeral(self, *, channel: str, user: str, text: str | None = None, as_user: bool | None = None, attachments: str | Sequence[Dict | [Attachment](models/attachments/index.html#slack_sdk.models.attachments.Attachment "slack_sdk.models.attachments.Attachment")] | None = None, blocks: str | Sequence[Dict | [Block](models/blocks/blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")] | None = None, thread_ts: str | None = None, icon_emoji: str | None = None, icon_url: str | None = None, link_names: bool | None = None, username: str | None = None, parse: str | None = None, markdown_text: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def chat_postEphemeral( self, *, channel: str, user: str, text: Optional[str] = None, as_user: Optional[bool] = None, attachments: Optional[Union[str, Sequence[Union[Dict, Attachment]]]] = None, blocks: Optional[Union[str, Sequence[Union[Dict, Block]]]] = None, thread_ts: Optional[str] = None, icon_emoji: Optional[str] = None, icon_url: Optional[str] = None, link_names: Optional[bool] = None, username: Optional[str] = None, parse: Optional[str] = None, markdown_text: Optional[str] = None, **kwargs, ) -> SlackResponse: """Sends an ephemeral message to a user in a channel. https://docs.slack.dev/reference/methods/chat.postEphemeral """ kwargs.update( { "channel": channel, "user": user, "text": text, "as_user": as_user, "attachments": attachments, "blocks": blocks, "thread_ts": thread_ts, "icon_emoji": icon_emoji, "icon_url": icon_url, "link_names": link_names, "username": username, "parse": parse, "markdown_text": markdown_text, } ) _parse_web_class_objects(kwargs) kwargs = _remove_none_values(kwargs) _warn_if_message_text_content_is_missing("chat.postEphemeral", kwargs) # NOTE: intentionally using json over params for the API methods using blocks/attachments return self.api_call("chat.postEphemeral", json=kwargs) ``` Sends an ephemeral message to a user in a channel. [https://docs.slack.dev/reference/methods/chat.postEphemeral](https://docs.slack.dev/reference/methods/chat.postEphemeral) `def chat_postMessage(self, *, channel: str, text: str | None = None, as_user: bool | None = None, attachments: str | Sequence[Dict | [Attachment](models/attachments/index.html#slack_sdk.models.attachments.Attachment "slack_sdk.models.attachments.Attachment")] | None = None, blocks: str | Sequence[Dict | [Block](models/blocks/blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")] | None = None, thread_ts: str | None = None, reply_broadcast: bool | None = None, unfurl_links: bool | None = None, unfurl_media: bool | None = None, container_id: str | None = None, icon_emoji: str | None = None, icon_url: str | None = None, mrkdwn: bool | None = None, link_names: bool | None = None, username: str | None = None, parse: str | None = None, metadata: Dict | [Metadata](models/metadata/index.html#slack_sdk.models.metadata.Metadata "slack_sdk.models.metadata.Metadata") | [EventAndEntityMetadata](models/metadata/index.html#slack_sdk.models.metadata.EventAndEntityMetadata "slack_sdk.models.metadata.EventAndEntityMetadata") | None = None, markdown_text: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def chat_postMessage( self, *, channel: str, text: Optional[str] = None, as_user: Optional[bool] = None, attachments: Optional[Union[str, Sequence[Union[Dict, Attachment]]]] = None, blocks: Optional[Union[str, Sequence[Union[Dict, Block]]]] = None, thread_ts: Optional[str] = None, reply_broadcast: Optional[bool] = None, unfurl_links: Optional[bool] = None, unfurl_media: Optional[bool] = None, container_id: Optional[str] = None, icon_emoji: Optional[str] = None, icon_url: Optional[str] = None, mrkdwn: Optional[bool] = None, link_names: Optional[bool] = None, username: Optional[str] = None, parse: Optional[str] = None, # none, full metadata: Optional[Union[Dict, Metadata, EventAndEntityMetadata]] = None, markdown_text: Optional[str] = None, **kwargs, ) -> SlackResponse: """Sends a message to a channel. https://docs.slack.dev/reference/methods/chat.postMessage """ kwargs.update( { "channel": channel, "text": text, "as_user": as_user, "attachments": attachments, "blocks": blocks, "thread_ts": thread_ts, "reply_broadcast": reply_broadcast, "unfurl_links": unfurl_links, "unfurl_media": unfurl_media, "container_id": container_id, "icon_emoji": icon_emoji, "icon_url": icon_url, "mrkdwn": mrkdwn, "link_names": link_names, "username": username, "parse": parse, "metadata": metadata, "markdown_text": markdown_text, } ) _parse_web_class_objects(kwargs) kwargs = _remove_none_values(kwargs) _warn_if_message_text_content_is_missing("chat.postMessage", kwargs) # NOTE: intentionally using json over params for the API methods using blocks/attachments return self.api_call("chat.postMessage", json=kwargs) ``` Sends a message to a channel. [https://docs.slack.dev/reference/methods/chat.postMessage](https://docs.slack.dev/reference/methods/chat.postMessage) `def chat_scheduleMessage(self, *, channel: str, post_at: str | int, text: str | None = None, as_user: bool | None = None, attachments: str | Sequence[Dict | [Attachment](models/attachments/index.html#slack_sdk.models.attachments.Attachment "slack_sdk.models.attachments.Attachment")] | None = None, blocks: str | Sequence[Dict | [Block](models/blocks/blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")] | None = None, thread_ts: str | None = None, parse: str | None = None, reply_broadcast: bool | None = None, unfurl_links: bool | None = None, unfurl_media: bool | None = None, link_names: bool | None = None, metadata: Dict | [Metadata](models/metadata/index.html#slack_sdk.models.metadata.Metadata "slack_sdk.models.metadata.Metadata") | None = None, markdown_text: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def chat_scheduleMessage( self, *, channel: str, post_at: Union[str, int], text: Optional[str] = None, as_user: Optional[bool] = None, attachments: Optional[Union[str, Sequence[Union[Dict, Attachment]]]] = None, blocks: Optional[Union[str, Sequence[Union[Dict, Block]]]] = None, thread_ts: Optional[str] = None, parse: Optional[str] = None, reply_broadcast: Optional[bool] = None, unfurl_links: Optional[bool] = None, unfurl_media: Optional[bool] = None, link_names: Optional[bool] = None, metadata: Optional[Union[Dict, Metadata]] = None, markdown_text: Optional[str] = None, **kwargs, ) -> SlackResponse: """Schedules a message. https://docs.slack.dev/reference/methods/chat.scheduleMessage """ kwargs.update( { "channel": channel, "post_at": post_at, "text": text, "as_user": as_user, "attachments": attachments, "blocks": blocks, "thread_ts": thread_ts, "reply_broadcast": reply_broadcast, "parse": parse, "unfurl_links": unfurl_links, "unfurl_media": unfurl_media, "link_names": link_names, "metadata": metadata, "markdown_text": markdown_text, } ) _parse_web_class_objects(kwargs) kwargs = _remove_none_values(kwargs) _warn_if_message_text_content_is_missing("chat.scheduleMessage", kwargs) # NOTE: intentionally using json over params for the API methods using blocks/attachments return self.api_call("chat.scheduleMessage", json=kwargs) ``` Schedules a message. [https://docs.slack.dev/reference/methods/chat.scheduleMessage](https://docs.slack.dev/reference/methods/chat.scheduleMessage) `def chat_scheduledMessages_list(self, *, channel: str | None = None, cursor: str | None = None, latest: str | None = None, limit: int | None = None, oldest: str | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def chat_scheduledMessages_list( self, *, channel: Optional[str] = None, cursor: Optional[str] = None, latest: Optional[str] = None, limit: Optional[int] = None, oldest: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists all scheduled messages. https://docs.slack.dev/reference/methods/chat.scheduledMessages.list """ kwargs.update( { "channel": channel, "cursor": cursor, "latest": latest, "limit": limit, "oldest": oldest, "team_id": team_id, } ) return self.api_call("chat.scheduledMessages.list", params=kwargs) ``` Lists all scheduled messages. [https://docs.slack.dev/reference/methods/chat.scheduledMessages.list](https://docs.slack.dev/reference/methods/chat.scheduledMessages.list) `def chat_startStream(self, *, channel: str, thread_ts: str, markdown_text: str | None = None, recipient_team_id: str | None = None, recipient_user_id: str | None = None, chunks: Sequence[Dict | [Chunk](models/messages/chunk.html#slack_sdk.models.messages.chunk.Chunk "slack_sdk.models.messages.chunk.Chunk")] | None = None, task_display_mode: str | None = None, icon_emoji: str | None = None, icon_url: str | None = None, username: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def chat_startStream( self, *, channel: str, thread_ts: str, markdown_text: Optional[str] = None, recipient_team_id: Optional[str] = None, recipient_user_id: Optional[str] = None, chunks: Optional[Sequence[Union[Dict, Chunk]]] = None, task_display_mode: Optional[str] = None, # timeline, plan icon_emoji: Optional[str] = None, icon_url: Optional[str] = None, username: Optional[str] = None, **kwargs, ) -> SlackResponse: """Starts a new streaming conversation. https://docs.slack.dev/reference/methods/chat.startStream """ kwargs.update( { "channel": channel, "thread_ts": thread_ts, "markdown_text": markdown_text, "recipient_team_id": recipient_team_id, "recipient_user_id": recipient_user_id, "chunks": chunks, "task_display_mode": task_display_mode, "icon_emoji": icon_emoji, "icon_url": icon_url, "username": username, } ) _parse_web_class_objects(kwargs) kwargs = _remove_none_values(kwargs) return self.api_call("chat.startStream", json=kwargs) ``` Starts a new streaming conversation. [https://docs.slack.dev/reference/methods/chat.startStream](https://docs.slack.dev/reference/methods/chat.startStream) `def chat_stopStream(self, *, channel: str, ts: str, markdown_text: str | None = None, blocks: str | Sequence[Dict | [Block](models/blocks/blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")] | None = None, metadata: Dict | [Metadata](models/metadata/index.html#slack_sdk.models.metadata.Metadata "slack_sdk.models.metadata.Metadata") | None = None, chunks: Sequence[Dict | [Chunk](models/messages/chunk.html#slack_sdk.models.messages.chunk.Chunk "slack_sdk.models.messages.chunk.Chunk")] | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def chat_stopStream( self, *, channel: str, ts: str, markdown_text: Optional[str] = None, blocks: Optional[Union[str, Sequence[Union[Dict, Block]]]] = None, metadata: Optional[Union[Dict, Metadata]] = None, chunks: Optional[Sequence[Union[Dict, Chunk]]] = None, **kwargs, ) -> SlackResponse: """Stops a streaming conversation. https://docs.slack.dev/reference/methods/chat.stopStream """ kwargs.update( { "channel": channel, "ts": ts, "markdown_text": markdown_text, "blocks": blocks, "metadata": metadata, "chunks": chunks, } ) _parse_web_class_objects(kwargs) kwargs = _remove_none_values(kwargs) return self.api_call("chat.stopStream", json=kwargs) ``` Stops a streaming conversation. [https://docs.slack.dev/reference/methods/chat.stopStream](https://docs.slack.dev/reference/methods/chat.stopStream) `def chat_stream(self, *, buffer_size: int = 256, channel: str, thread_ts: str, recipient_team_id: str | None = None, recipient_user_id: str | None = None, task_display_mode: str | None = None, icon_emoji: str | None = None, icon_url: str | None = None, username: str | None = None, **kwargs) ‑> [ChatStream](web/chat_stream.html#slack_sdk.web.chat_stream.ChatStream "slack_sdk.web.chat_stream.ChatStream")` Expand source code ``` def chat_stream( self, *, buffer_size: int = 256, channel: str, thread_ts: str, recipient_team_id: Optional[str] = None, recipient_user_id: Optional[str] = None, task_display_mode: Optional[str] = None, icon_emoji: Optional[str] = None, icon_url: Optional[str] = None, username: Optional[str] = None, **kwargs, ) -> ChatStream: """Stream markdown text into a conversation. This method starts a new chat stream in a conversation that can be appended to. After appending an entire message, the stream can be stopped with concluding arguments such as "blocks" for gathering feedback. The following methods are used: - chat.startStream: Starts a new streaming conversation. [Reference](https://docs.slack.dev/reference/methods/chat.startStream). - chat.appendStream: Appends text to an existing streaming conversation. [Reference](https://docs.slack.dev/reference/methods/chat.appendStream). - chat.stopStream: Stops a streaming conversation. [Reference](https://docs.slack.dev/reference/methods/chat.stopStream). Args: buffer_size: The length of markdown_text to buffer in-memory before calling a stream method. Increasing this value decreases the number of method calls made for the same amount of text, which is useful to avoid rate limits. Default: 256. channel: An encoded ID that represents a channel, private group, or DM. thread_ts: Provide another message's ts value to reply to. Streamed messages should always be replies to a user request. recipient_team_id: The encoded ID of the team the user receiving the streaming text belongs to. Required when streaming to channels. recipient_user_id: The encoded ID of the user to receive the streaming text. Required when streaming to channels. task_display_mode: Specifies how tasks are displayed in the message. A "timeline" displays individual tasks with text and "plan" displays all tasks together. icon_emoji: Emoji to use as the icon for this message. Overrides icon_url. icon_url: Image URL to use as the icon for this message. username: The bot's username to display. **kwargs: Additional arguments passed to the underlying API calls. Returns: ChatStream instance for managing the stream Example: ```python streamer = client.chat_stream( channel="C0123456789", thread_ts="1700000001.123456", recipient_team_id="T0123456789", recipient_user_id="U0123456789", ) streamer.append(markdown_text="**hello wo") streamer.append(markdown_text="rld!**") streamer.stop() ``` """ return ChatStream( self, logger=self._logger, channel=channel, thread_ts=thread_ts, recipient_team_id=recipient_team_id, recipient_user_id=recipient_user_id, task_display_mode=task_display_mode, icon_emoji=icon_emoji, icon_url=icon_url, username=username, buffer_size=buffer_size, **kwargs, ) ``` Stream markdown text into a conversation. This method starts a new chat stream in a conversation that can be appended to. After appending an entire message, the stream can be stopped with concluding arguments such as "blocks" for gathering feedback. The following methods are used: * chat.startStream: Starts a new streaming conversation. [Reference](https://docs.slack.dev/reference/methods/chat.startStream). * chat.appendStream: Appends text to an existing streaming conversation. [Reference](https://docs.slack.dev/reference/methods/chat.appendStream). * chat.stopStream: Stops a streaming conversation. [Reference](https://docs.slack.dev/reference/methods/chat.stopStream). ## Args **`buffer_size`** The length of markdown\_text to buffer in-memory before calling a stream method. Increasing this value decreases the number of method calls made for the same amount of text, which is useful to avoid rate limits. Default: 256. **`channel`** An encoded ID that represents a channel, private group, or DM. **`thread_ts`** Provide another message's ts value to reply to. Streamed messages should always be replies to a user request. **`recipient_team_id`** The encoded ID of the team the user receiving the streaming text belongs to. Required when streaming to channels. **`recipient_user_id`** The encoded ID of the user to receive the streaming text. Required when streaming to channels. **`task_display_mode`** Specifies how tasks are displayed in the message. A "timeline" displays individual tasks with text and "plan" displays all tasks together. **`icon_emoji`** Emoji to use as the icon for this message. Overrides icon\_url. **`icon_url`** Image URL to use as the icon for this message. **`username`** The bot's username to display. **`**kwargs`** Additional arguments passed to the underlying API calls. ## Returns ChatStream instance for managing the stream ## Example ```python streamer = client.chat_stream( channel="C0123456789", thread_ts="1700000001.123456", recipient_team_id="T0123456789", recipient_user_id="U0123456789", ) streamer.append(markdown_text="**hello wo") streamer.append(markdown_text="rld!**") streamer.stop() ``` `def chat_unfurl(self, *, channel: str | None = None, ts: str | None = None, source: str | None = None, unfurl_id: str | None = None, unfurls: Dict[str, Dict] | None = None, metadata: Dict | [EventAndEntityMetadata](models/metadata/index.html#slack_sdk.models.metadata.EventAndEntityMetadata "slack_sdk.models.metadata.EventAndEntityMetadata") | None = None, user_auth_blocks: str | Sequence[Dict | [Block](models/blocks/blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")] | None = None, user_auth_message: str | None = None, user_auth_required: bool | None = None, user_auth_url: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def chat_unfurl( self, *, channel: Optional[str] = None, ts: Optional[str] = None, source: Optional[str] = None, unfurl_id: Optional[str] = None, unfurls: Optional[Dict[str, Dict]] = None, # or user_auth_* metadata: Optional[Union[Dict, EventAndEntityMetadata]] = None, user_auth_blocks: Optional[Union[str, Sequence[Union[Dict, Block]]]] = None, user_auth_message: Optional[str] = None, user_auth_required: Optional[bool] = None, user_auth_url: Optional[str] = None, **kwargs, ) -> SlackResponse: """Provide custom unfurl behavior for user-posted URLs. https://docs.slack.dev/reference/methods/chat.unfurl """ kwargs.update( { "channel": channel, "ts": ts, "source": source, "unfurl_id": unfurl_id, "unfurls": unfurls, "metadata": metadata, "user_auth_blocks": user_auth_blocks, "user_auth_message": user_auth_message, "user_auth_required": user_auth_required, "user_auth_url": user_auth_url, } ) _parse_web_class_objects(kwargs) # for user_auth_blocks kwargs = _remove_none_values(kwargs) # NOTE: intentionally using json over params for API methods using blocks/attachments return self.api_call("chat.unfurl", json=kwargs) ``` Provide custom unfurl behavior for user-posted URLs. [https://docs.slack.dev/reference/methods/chat.unfurl](https://docs.slack.dev/reference/methods/chat.unfurl) `def chat_update(self, *, channel: str, ts: str, text: str | None = None, attachments: str | Sequence[Dict | [Attachment](models/attachments/index.html#slack_sdk.models.attachments.Attachment "slack_sdk.models.attachments.Attachment")] | None = None, blocks: str | Sequence[Dict | [Block](models/blocks/blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")] | None = None, as_user: bool | None = None, file_ids: str | Sequence[str] | None = None, link_names: bool | None = None, parse: str | None = None, reply_broadcast: bool | None = None, metadata: Dict | [Metadata](models/metadata/index.html#slack_sdk.models.metadata.Metadata "slack_sdk.models.metadata.Metadata") | None = None, markdown_text: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def chat_update( self, *, channel: str, ts: str, text: Optional[str] = None, attachments: Optional[Union[str, Sequence[Union[Dict, Attachment]]]] = None, blocks: Optional[Union[str, Sequence[Union[Dict, Block]]]] = None, as_user: Optional[bool] = None, file_ids: Optional[Union[str, Sequence[str]]] = None, link_names: Optional[bool] = None, parse: Optional[str] = None, # none, full reply_broadcast: Optional[bool] = None, metadata: Optional[Union[Dict, Metadata]] = None, markdown_text: Optional[str] = None, **kwargs, ) -> SlackResponse: """Updates a message in a channel. https://docs.slack.dev/reference/methods/chat.update """ kwargs.update( { "channel": channel, "ts": ts, "text": text, "attachments": attachments, "blocks": blocks, "as_user": as_user, "link_names": link_names, "parse": parse, "reply_broadcast": reply_broadcast, "metadata": metadata, "markdown_text": markdown_text, } ) if isinstance(file_ids, (list, tuple)): kwargs.update({"file_ids": ",".join(file_ids)}) else: kwargs.update({"file_ids": file_ids}) _parse_web_class_objects(kwargs) kwargs = _remove_none_values(kwargs) _warn_if_message_text_content_is_missing("chat.update", kwargs) # NOTE: intentionally using json over params for API methods using blocks/attachments return self.api_call("chat.update", json=kwargs) ``` Updates a message in a channel. [https://docs.slack.dev/reference/methods/chat.update](https://docs.slack.dev/reference/methods/chat.update) `def conversations_acceptSharedInvite(self, *, channel_name: str, channel_id: str | None = None, invite_id: str | None = None, free_trial_accepted: bool | None = None, is_private: bool | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_acceptSharedInvite( self, *, channel_name: str, channel_id: Optional[str] = None, invite_id: Optional[str] = None, free_trial_accepted: Optional[bool] = None, is_private: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Accepts an invitation to a Slack Connect channel. https://docs.slack.dev/reference/methods/conversations.acceptSharedInvite """ if channel_id is None and invite_id is None: raise e.SlackRequestError("Either channel_id or invite_id must be provided.") kwargs.update( { "channel_name": channel_name, "channel_id": channel_id, "invite_id": invite_id, "free_trial_accepted": free_trial_accepted, "is_private": is_private, "team_id": team_id, } ) return self.api_call("conversations.acceptSharedInvite", http_verb="POST", params=kwargs) ``` Accepts an invitation to a Slack Connect channel. [https://docs.slack.dev/reference/methods/conversations.acceptSharedInvite](https://docs.slack.dev/reference/methods/conversations.acceptSharedInvite) `def conversations_approveSharedInvite(self, *, invite_id: str, target_team: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_approveSharedInvite( self, *, invite_id: str, target_team: Optional[str] = None, **kwargs, ) -> SlackResponse: """Approves an invitation to a Slack Connect channel. https://docs.slack.dev/reference/methods/conversations.approveSharedInvite """ kwargs.update({"invite_id": invite_id, "target_team": target_team}) return self.api_call("conversations.approveSharedInvite", http_verb="POST", params=kwargs) ``` Approves an invitation to a Slack Connect channel. [https://docs.slack.dev/reference/methods/conversations.approveSharedInvite](https://docs.slack.dev/reference/methods/conversations.approveSharedInvite) `def conversations_archive(self, *, channel: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_archive( self, *, channel: str, **kwargs, ) -> SlackResponse: """Archives a conversation. https://docs.slack.dev/reference/methods/conversations.archive """ kwargs.update({"channel": channel}) return self.api_call("conversations.archive", params=kwargs) ``` Archives a conversation. [https://docs.slack.dev/reference/methods/conversations.archive](https://docs.slack.dev/reference/methods/conversations.archive) `def conversations_canvases_create(self, *, channel_id: str, document_content: Dict[str, str], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_canvases_create( self, *, channel_id: str, document_content: Dict[str, str], **kwargs, ) -> SlackResponse: """Create a Channel Canvas for a channel https://docs.slack.dev/reference/methods/conversations.canvases.create """ kwargs.update({"channel_id": channel_id, "document_content": document_content}) return self.api_call("conversations.canvases.create", json=kwargs) ``` Create a Channel Canvas for a channel [https://docs.slack.dev/reference/methods/conversations.canvases.create](https://docs.slack.dev/reference/methods/conversations.canvases.create) `def conversations_close(self, *, channel: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_close( self, *, channel: str, **kwargs, ) -> SlackResponse: """Closes a direct message or multi-person direct message. https://docs.slack.dev/reference/methods/conversations.close """ kwargs.update({"channel": channel}) return self.api_call("conversations.close", params=kwargs) ``` Closes a direct message or multi-person direct message. [https://docs.slack.dev/reference/methods/conversations.close](https://docs.slack.dev/reference/methods/conversations.close) `def conversations_create(self, *, name: str, is_private: bool | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_create( self, *, name: str, is_private: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Initiates a public or private channel-based conversation https://docs.slack.dev/reference/methods/conversations.create """ kwargs.update({"name": name, "is_private": is_private, "team_id": team_id}) return self.api_call("conversations.create", params=kwargs) ``` Initiates a public or private channel-based conversation [https://docs.slack.dev/reference/methods/conversations.create](https://docs.slack.dev/reference/methods/conversations.create) `def conversations_declineSharedInvite(self, *, invite_id: str, target_team: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_declineSharedInvite( self, *, invite_id: str, target_team: Optional[str] = None, **kwargs, ) -> SlackResponse: """Declines a Slack Connect channel invite. https://docs.slack.dev/reference/methods/conversations.declineSharedInvite """ kwargs.update({"invite_id": invite_id, "target_team": target_team}) return self.api_call("conversations.declineSharedInvite", http_verb="GET", params=kwargs) ``` Declines a Slack Connect channel invite. [https://docs.slack.dev/reference/methods/conversations.declineSharedInvite](https://docs.slack.dev/reference/methods/conversations.declineSharedInvite) `def conversations_externalInvitePermissions_set(self, *, action: str, channel: str, target_team: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_externalInvitePermissions_set( self, *, action: str, channel: str, target_team: str, **kwargs ) -> SlackResponse: """Sets a team in a shared External Limited channel to a shared Slack Connect channel or vice versa. https://docs.slack.dev/reference/methods/conversations.externalInvitePermissions.set """ kwargs.update( { "action": action, "channel": channel, "target_team": target_team, } ) return self.api_call("conversations.externalInvitePermissions.set", params=kwargs) ``` Sets a team in a shared External Limited channel to a shared Slack Connect channel or vice versa. [https://docs.slack.dev/reference/methods/conversations.externalInvitePermissions.set](https://docs.slack.dev/reference/methods/conversations.externalInvitePermissions.set) `def conversations_history(self, *, channel: str, cursor: str | None = None, inclusive: bool | None = None, include_all_metadata: bool | None = None, latest: str | None = None, limit: int | None = None, oldest: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_history( self, *, channel: str, cursor: Optional[str] = None, inclusive: Optional[bool] = None, include_all_metadata: Optional[bool] = None, latest: Optional[str] = None, limit: Optional[int] = None, oldest: Optional[str] = None, **kwargs, ) -> SlackResponse: """Fetches a conversation's history of messages and events. https://docs.slack.dev/reference/methods/conversations.history """ kwargs.update( { "channel": channel, "cursor": cursor, "inclusive": inclusive, "include_all_metadata": include_all_metadata, "limit": limit, "latest": latest, "oldest": oldest, } ) return self.api_call("conversations.history", http_verb="GET", params=kwargs) ``` Fetches a conversation's history of messages and events. [https://docs.slack.dev/reference/methods/conversations.history](https://docs.slack.dev/reference/methods/conversations.history) `def conversations_info(self, *, channel: str, include_locale: bool | None = None, include_num_members: bool | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_info( self, *, channel: str, include_locale: Optional[bool] = None, include_num_members: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Retrieve information about a conversation. https://docs.slack.dev/reference/methods/conversations.info """ kwargs.update( { "channel": channel, "include_locale": include_locale, "include_num_members": include_num_members, } ) return self.api_call("conversations.info", http_verb="GET", params=kwargs) ``` Retrieve information about a conversation. [https://docs.slack.dev/reference/methods/conversations.info](https://docs.slack.dev/reference/methods/conversations.info) `def conversations_invite(self, *, channel: str, users: str | Sequence[str], force: bool | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_invite( self, *, channel: str, users: Union[str, Sequence[str]], force: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Invites users to a channel. https://docs.slack.dev/reference/methods/conversations.invite """ kwargs.update( { "channel": channel, "force": force, } ) if isinstance(users, (list, tuple)): kwargs.update({"users": ",".join(users)}) else: kwargs.update({"users": users}) return self.api_call("conversations.invite", params=kwargs) ``` Invites users to a channel. [https://docs.slack.dev/reference/methods/conversations.invite](https://docs.slack.dev/reference/methods/conversations.invite) `def conversations_inviteShared(self, *, channel: str, emails: str | Sequence[str] | None = None, user_ids: str | Sequence[str] | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_inviteShared( self, *, channel: str, emails: Optional[Union[str, Sequence[str]]] = None, user_ids: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """Sends an invitation to a Slack Connect channel. https://docs.slack.dev/reference/methods/conversations.inviteShared """ if emails is None and user_ids is None: raise e.SlackRequestError("Either emails or user ids must be provided.") kwargs.update({"channel": channel}) if isinstance(emails, (list, tuple)): kwargs.update({"emails": ",".join(emails)}) else: kwargs.update({"emails": emails}) if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) return self.api_call("conversations.inviteShared", http_verb="GET", params=kwargs) ``` Sends an invitation to a Slack Connect channel. [https://docs.slack.dev/reference/methods/conversations.inviteShared](https://docs.slack.dev/reference/methods/conversations.inviteShared) `def conversations_join(self, *, channel: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_join( self, *, channel: str, **kwargs, ) -> SlackResponse: """Joins an existing conversation. https://docs.slack.dev/reference/methods/conversations.join """ kwargs.update({"channel": channel}) return self.api_call("conversations.join", params=kwargs) ``` Joins an existing conversation. [https://docs.slack.dev/reference/methods/conversations.join](https://docs.slack.dev/reference/methods/conversations.join) `def conversations_kick(self, *, channel: str, user: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_kick( self, *, channel: str, user: str, **kwargs, ) -> SlackResponse: """Removes a user from a conversation. https://docs.slack.dev/reference/methods/conversations.kick """ kwargs.update({"channel": channel, "user": user}) return self.api_call("conversations.kick", params=kwargs) ``` Removes a user from a conversation. [https://docs.slack.dev/reference/methods/conversations.kick](https://docs.slack.dev/reference/methods/conversations.kick) `def conversations_leave(self, *, channel: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_leave( self, *, channel: str, **kwargs, ) -> SlackResponse: """Leaves a conversation. https://docs.slack.dev/reference/methods/conversations.leave """ kwargs.update({"channel": channel}) return self.api_call("conversations.leave", params=kwargs) ``` Leaves a conversation. [https://docs.slack.dev/reference/methods/conversations.leave](https://docs.slack.dev/reference/methods/conversations.leave) `def conversations_list(self, *, cursor: str | None = None, exclude_archived: bool | None = None, limit: int | None = None, team_id: str | None = None, types: str | Sequence[str] | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_list( self, *, cursor: Optional[str] = None, exclude_archived: Optional[bool] = None, limit: Optional[int] = None, team_id: Optional[str] = None, types: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """Lists all channels in a Slack team. https://docs.slack.dev/reference/methods/conversations.list """ kwargs.update( { "cursor": cursor, "exclude_archived": exclude_archived, "limit": limit, "team_id": team_id, } ) if isinstance(types, (list, tuple)): kwargs.update({"types": ",".join(types)}) else: kwargs.update({"types": types}) return self.api_call("conversations.list", http_verb="GET", params=kwargs) ``` Lists all channels in a Slack team. [https://docs.slack.dev/reference/methods/conversations.list](https://docs.slack.dev/reference/methods/conversations.list) `def conversations_listConnectInvites(self, *, count: int | None = None, cursor: str | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_listConnectInvites( self, *, count: Optional[int] = None, cursor: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List shared channel invites that have been generated or received but have not yet been approved by all parties. https://docs.slack.dev/reference/methods/conversations.listConnectInvites """ kwargs.update({"count": count, "cursor": cursor, "team_id": team_id}) return self.api_call("conversations.listConnectInvites", params=kwargs) ``` List shared channel invites that have been generated or received but have not yet been approved by all parties. [https://docs.slack.dev/reference/methods/conversations.listConnectInvites](https://docs.slack.dev/reference/methods/conversations.listConnectInvites) `def conversations_mark(self, *, channel: str, ts: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_mark( self, *, channel: str, ts: str, **kwargs, ) -> SlackResponse: """Sets the read cursor in a channel. https://docs.slack.dev/reference/methods/conversations.mark """ kwargs.update({"channel": channel, "ts": ts}) return self.api_call("conversations.mark", params=kwargs) ``` Sets the read cursor in a channel. [https://docs.slack.dev/reference/methods/conversations.mark](https://docs.slack.dev/reference/methods/conversations.mark) `def conversations_members(self, *, channel: str, cursor: str | None = None, limit: int | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_members( self, *, channel: str, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Retrieve members of a conversation. https://docs.slack.dev/reference/methods/conversations.members """ kwargs.update({"channel": channel, "cursor": cursor, "limit": limit}) return self.api_call("conversations.members", http_verb="GET", params=kwargs) ``` Retrieve members of a conversation. [https://docs.slack.dev/reference/methods/conversations.members](https://docs.slack.dev/reference/methods/conversations.members) `def conversations_open(self, *, channel: str | None = None, return_im: bool | None = None, users: str | Sequence[str] | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_open( self, *, channel: Optional[str] = None, return_im: Optional[bool] = None, users: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """Opens or resumes a direct message or multi-person direct message. https://docs.slack.dev/reference/methods/conversations.open """ if channel is None and users is None: raise e.SlackRequestError("Either channel or users must be provided.") kwargs.update({"channel": channel, "return_im": return_im}) if isinstance(users, (list, tuple)): kwargs.update({"users": ",".join(users)}) else: kwargs.update({"users": users}) return self.api_call("conversations.open", params=kwargs) ``` Opens or resumes a direct message or multi-person direct message. [https://docs.slack.dev/reference/methods/conversations.open](https://docs.slack.dev/reference/methods/conversations.open) `def conversations_rename(self, *, channel: str, name: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_rename( self, *, channel: str, name: str, **kwargs, ) -> SlackResponse: """Renames a conversation. https://docs.slack.dev/reference/methods/conversations.rename """ kwargs.update({"channel": channel, "name": name}) return self.api_call("conversations.rename", params=kwargs) ``` Renames a conversation. [https://docs.slack.dev/reference/methods/conversations.rename](https://docs.slack.dev/reference/methods/conversations.rename) `def conversations_replies(self, *, channel: str, ts: str, cursor: str | None = None, inclusive: bool | None = None, include_all_metadata: bool | None = None, latest: str | None = None, limit: int | None = None, oldest: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_replies( self, *, channel: str, ts: str, cursor: Optional[str] = None, inclusive: Optional[bool] = None, include_all_metadata: Optional[bool] = None, latest: Optional[str] = None, limit: Optional[int] = None, oldest: Optional[str] = None, **kwargs, ) -> SlackResponse: """Retrieve a thread of messages posted to a conversation https://docs.slack.dev/reference/methods/conversations.replies """ kwargs.update( { "channel": channel, "ts": ts, "cursor": cursor, "inclusive": inclusive, "include_all_metadata": include_all_metadata, "limit": limit, "latest": latest, "oldest": oldest, } ) return self.api_call("conversations.replies", http_verb="GET", params=kwargs) ``` Retrieve a thread of messages posted to a conversation [https://docs.slack.dev/reference/methods/conversations.replies](https://docs.slack.dev/reference/methods/conversations.replies) `def conversations_requestSharedInvite_approve(self, *, invite_id: str, channel_id: str | None = None, is_external_limited: str | None = None, message: Dict[str, Any] | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_requestSharedInvite_approve( self, *, invite_id: str, channel_id: Optional[str] = None, is_external_limited: Optional[str] = None, message: Optional[Dict[str, Any]] = None, **kwargs, ) -> SlackResponse: """Approve a request to add an external user to a channel. This also sends them a Slack Connect invite. https://docs.slack.dev/reference/methods/conversations.requestSharedInvite.approve """ kwargs.update( { "invite_id": invite_id, "channel_id": channel_id, "is_external_limited": is_external_limited, } ) if message is not None: kwargs.update({"message": json.dumps(message)}) return self.api_call("conversations.requestSharedInvite.approve", params=kwargs) ``` Approve a request to add an external user to a channel. This also sends them a Slack Connect invite. [https://docs.slack.dev/reference/methods/conversations.requestSharedInvite.approve](https://docs.slack.dev/reference/methods/conversations.requestSharedInvite.approve) `def conversations_requestSharedInvite_deny(self, *, invite_id: str, message: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_requestSharedInvite_deny( self, *, invite_id: str, message: Optional[str] = None, **kwargs, ) -> SlackResponse: """Deny a request to invite an external user to a channel. https://docs.slack.dev/reference/methods/conversations.requestSharedInvite.deny """ kwargs.update({"invite_id": invite_id, "message": message}) return self.api_call("conversations.requestSharedInvite.deny", params=kwargs) ``` Deny a request to invite an external user to a channel. [https://docs.slack.dev/reference/methods/conversations.requestSharedInvite.deny](https://docs.slack.dev/reference/methods/conversations.requestSharedInvite.deny) `def conversations_requestSharedInvite_list(self, *, cursor: str | None = None, include_approved: bool | None = None, include_denied: bool | None = None, include_expired: bool | None = None, invite_ids: str | Sequence[str] | None = None, limit: int | None = None, user_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_requestSharedInvite_list( self, *, cursor: Optional[str] = None, include_approved: Optional[bool] = None, include_denied: Optional[bool] = None, include_expired: Optional[bool] = None, invite_ids: Optional[Union[str, Sequence[str]]] = None, limit: Optional[int] = None, user_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists requests to add external users to channels with ability to filter. https://docs.slack.dev/reference/methods/conversations.requestSharedInvite.list """ kwargs.update( { "cursor": cursor, "include_approved": include_approved, "include_denied": include_denied, "include_expired": include_expired, "limit": limit, "user_id": user_id, } ) if invite_ids is not None: if isinstance(invite_ids, (list, tuple)): kwargs.update({"invite_ids": ",".join(invite_ids)}) else: kwargs.update({"invite_ids": invite_ids}) return self.api_call("conversations.requestSharedInvite.list", params=kwargs) ``` Lists requests to add external users to channels with ability to filter. [https://docs.slack.dev/reference/methods/conversations.requestSharedInvite.list](https://docs.slack.dev/reference/methods/conversations.requestSharedInvite.list) `def conversations_setPurpose(self, *, channel: str, purpose: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_setPurpose( self, *, channel: str, purpose: str, **kwargs, ) -> SlackResponse: """Sets the purpose for a conversation. https://docs.slack.dev/reference/methods/conversations.setPurpose """ kwargs.update({"channel": channel, "purpose": purpose}) return self.api_call("conversations.setPurpose", params=kwargs) ``` Sets the purpose for a conversation. [https://docs.slack.dev/reference/methods/conversations.setPurpose](https://docs.slack.dev/reference/methods/conversations.setPurpose) `def conversations_setTopic(self, *, channel: str, topic: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_setTopic( self, *, channel: str, topic: str, **kwargs, ) -> SlackResponse: """Sets the topic for a conversation. https://docs.slack.dev/reference/methods/conversations.setTopic """ kwargs.update({"channel": channel, "topic": topic}) return self.api_call("conversations.setTopic", params=kwargs) ``` Sets the topic for a conversation. [https://docs.slack.dev/reference/methods/conversations.setTopic](https://docs.slack.dev/reference/methods/conversations.setTopic) `def conversations_unarchive(self, *, channel: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_unarchive( self, *, channel: str, **kwargs, ) -> SlackResponse: """Reverses conversation archival. https://docs.slack.dev/reference/methods/conversations.unarchive """ kwargs.update({"channel": channel}) return self.api_call("conversations.unarchive", params=kwargs) ``` Reverses conversation archival. [https://docs.slack.dev/reference/methods/conversations.unarchive](https://docs.slack.dev/reference/methods/conversations.unarchive) `def dialog_open(self, *, dialog: Dict[str, Any], trigger_id: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def dialog_open( self, *, dialog: Dict[str, Any], trigger_id: str, **kwargs, ) -> SlackResponse: """Open a dialog with a user. https://docs.slack.dev/reference/methods/dialog.open """ kwargs.update({"dialog": dialog, "trigger_id": trigger_id}) kwargs = _remove_none_values(kwargs) # NOTE: As the dialog can be a dict, this API call works only with json format. return self.api_call("dialog.open", json=kwargs) ``` Open a dialog with a user. [https://docs.slack.dev/reference/methods/dialog.open](https://docs.slack.dev/reference/methods/dialog.open) `def dnd_endDnd(self, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def dnd_endDnd( self, **kwargs, ) -> SlackResponse: """Ends the current user's Do Not Disturb session immediately. https://docs.slack.dev/reference/methods/dnd.endDnd """ return self.api_call("dnd.endDnd", params=kwargs) ``` Ends the current user's Do Not Disturb session immediately. [https://docs.slack.dev/reference/methods/dnd.endDnd](https://docs.slack.dev/reference/methods/dnd.endDnd) `def dnd_endSnooze(self, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def dnd_endSnooze( self, **kwargs, ) -> SlackResponse: """Ends the current user's snooze mode immediately. https://docs.slack.dev/reference/methods/dnd.endSnooze """ return self.api_call("dnd.endSnooze", params=kwargs) ``` Ends the current user's snooze mode immediately. [https://docs.slack.dev/reference/methods/dnd.endSnooze](https://docs.slack.dev/reference/methods/dnd.endSnooze) `def dnd_info(self, *, team_id: str | None = None, user: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def dnd_info( self, *, team_id: Optional[str] = None, user: Optional[str] = None, **kwargs, ) -> SlackResponse: """Retrieves a user's current Do Not Disturb status. https://docs.slack.dev/reference/methods/dnd.info """ kwargs.update({"team_id": team_id, "user": user}) return self.api_call("dnd.info", http_verb="GET", params=kwargs) ``` Retrieves a user's current Do Not Disturb status. [https://docs.slack.dev/reference/methods/dnd.info](https://docs.slack.dev/reference/methods/dnd.info) `def dnd_setSnooze(self, *, num_minutes: str | int, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def dnd_setSnooze( self, *, num_minutes: Union[int, str], **kwargs, ) -> SlackResponse: """Turns on Do Not Disturb mode for the current user, or changes its duration. https://docs.slack.dev/reference/methods/dnd.setSnooze """ kwargs.update({"num_minutes": num_minutes}) return self.api_call("dnd.setSnooze", http_verb="GET", params=kwargs) ``` Turns on Do Not Disturb mode for the current user, or changes its duration. [https://docs.slack.dev/reference/methods/dnd.setSnooze](https://docs.slack.dev/reference/methods/dnd.setSnooze) `def dnd_teamInfo(self, users: str | Sequence[str], team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def dnd_teamInfo( self, users: Union[str, Sequence[str]], team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Retrieves the Do Not Disturb status for users on a team. https://docs.slack.dev/reference/methods/dnd.teamInfo """ if isinstance(users, (list, tuple)): kwargs.update({"users": ",".join(users)}) else: kwargs.update({"users": users}) kwargs.update({"team_id": team_id}) return self.api_call("dnd.teamInfo", http_verb="GET", params=kwargs) ``` Retrieves the Do Not Disturb status for users on a team. [https://docs.slack.dev/reference/methods/dnd.teamInfo](https://docs.slack.dev/reference/methods/dnd.teamInfo) `def emoji_list(self, include_categories: bool | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def emoji_list( self, include_categories: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Lists custom emoji for a team. https://docs.slack.dev/reference/methods/emoji.list """ kwargs.update({"include_categories": include_categories}) return self.api_call("emoji.list", http_verb="GET", params=kwargs) ``` Lists custom emoji for a team. [https://docs.slack.dev/reference/methods/emoji.list](https://docs.slack.dev/reference/methods/emoji.list) `def entity_presentDetails(self, trigger_id: str, metadata: Dict | [EntityMetadata](models/metadata/index.html#slack_sdk.models.metadata.EntityMetadata "slack_sdk.models.metadata.EntityMetadata") | None = None, user_auth_required: bool | None = None, user_auth_url: str | None = None, error: Dict[str, Any] | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def entity_presentDetails( self, trigger_id: str, metadata: Optional[Union[Dict, EntityMetadata]] = None, user_auth_required: Optional[bool] = None, user_auth_url: Optional[str] = None, error: Optional[Dict[str, Any]] = None, **kwargs, ) -> SlackResponse: """Provides entity details for the flexpane. https://docs.slack.dev/reference/methods/entity.presentDetails/ """ kwargs.update({"trigger_id": trigger_id}) if metadata is not None: kwargs.update({"metadata": metadata}) if user_auth_required is not None: kwargs.update({"user_auth_required": user_auth_required}) if user_auth_url is not None: kwargs.update({"user_auth_url": user_auth_url}) if error is not None: kwargs.update({"error": error}) _parse_web_class_objects(kwargs) return self.api_call("entity.presentDetails", json=kwargs) ``` Provides entity details for the flexpane. [https://docs.slack.dev/reference/methods/entity.presentDetails/](https://docs.slack.dev/reference/methods/entity.presentDetails/) `def files_comments_delete(self, *, file: str, id: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def files_comments_delete( self, *, file: str, id: str, **kwargs, ) -> SlackResponse: """Deletes an existing comment on a file. https://docs.slack.dev/reference/methods/files.comments.delete """ kwargs.update({"file": file, "id": id}) return self.api_call("files.comments.delete", params=kwargs) ``` Deletes an existing comment on a file. [https://docs.slack.dev/reference/methods/files.comments.delete](https://docs.slack.dev/reference/methods/files.comments.delete) `def files_completeUploadExternal(self, *, files: List[Dict[str, str | None]], channel_id: str | None = None, channels: List[str] | None = None, initial_comment: str | None = None, thread_ts: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def files_completeUploadExternal( self, *, files: List[Dict[str, Optional[str]]], channel_id: Optional[str] = None, channels: Optional[List[str]] = None, initial_comment: Optional[str] = None, thread_ts: Optional[str] = None, **kwargs, ) -> SlackResponse: """Finishes an upload started with files.getUploadURLExternal. https://docs.slack.dev/reference/methods/files.completeUploadExternal """ _files = [{k: v for k, v in f.items() if v is not None} for f in files] kwargs.update( { "files": json.dumps(_files), "channel_id": channel_id, "initial_comment": initial_comment, "thread_ts": thread_ts, } ) if channels: kwargs["channels"] = ",".join(channels) return self.api_call("files.completeUploadExternal", params=kwargs) ``` Finishes an upload started with files.getUploadURLExternal. [https://docs.slack.dev/reference/methods/files.completeUploadExternal](https://docs.slack.dev/reference/methods/files.completeUploadExternal) `def files_delete(self, *, file: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def files_delete( self, *, file: str, **kwargs, ) -> SlackResponse: """Deletes a file. https://docs.slack.dev/reference/methods/files.delete """ kwargs.update({"file": file}) return self.api_call("files.delete", params=kwargs) ``` Deletes a file. [https://docs.slack.dev/reference/methods/files.delete](https://docs.slack.dev/reference/methods/files.delete) `def files_getUploadURLExternal(self, *, filename: str, length: int, alt_txt: str | None = None, snippet_type: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def files_getUploadURLExternal( self, *, filename: str, length: int, alt_txt: Optional[str] = None, snippet_type: Optional[str] = None, **kwargs, ) -> SlackResponse: """Gets a URL for an edge external upload. https://docs.slack.dev/reference/methods/files.getUploadURLExternal """ kwargs.update( { "filename": filename, "length": length, "alt_txt": alt_txt, "snippet_type": snippet_type, } ) return self.api_call("files.getUploadURLExternal", params=kwargs) ``` Gets a URL for an edge external upload. [https://docs.slack.dev/reference/methods/files.getUploadURLExternal](https://docs.slack.dev/reference/methods/files.getUploadURLExternal) `def files_info(self, *, file: str, count: int | None = None, cursor: str | None = None, limit: int | None = None, page: int | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def files_info( self, *, file: str, count: Optional[int] = None, cursor: Optional[str] = None, limit: Optional[int] = None, page: Optional[int] = None, **kwargs, ) -> SlackResponse: """Gets information about a team file. https://docs.slack.dev/reference/methods/files.info """ kwargs.update( { "file": file, "count": count, "cursor": cursor, "limit": limit, "page": page, } ) return self.api_call("files.info", http_verb="GET", params=kwargs) ``` Gets information about a team file. [https://docs.slack.dev/reference/methods/files.info](https://docs.slack.dev/reference/methods/files.info) `def files_list(self, *, channel: str | None = None, count: int | None = None, page: int | None = None, show_files_hidden_by_limit: bool | None = None, team_id: str | None = None, ts_from: str | None = None, ts_to: str | None = None, types: str | Sequence[str] | None = None, user: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def files_list( self, *, channel: Optional[str] = None, count: Optional[int] = None, page: Optional[int] = None, show_files_hidden_by_limit: Optional[bool] = None, team_id: Optional[str] = None, ts_from: Optional[str] = None, ts_to: Optional[str] = None, types: Optional[Union[str, Sequence[str]]] = None, user: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists & filters team files. https://docs.slack.dev/reference/methods/files.list """ kwargs.update( { "channel": channel, "count": count, "page": page, "show_files_hidden_by_limit": show_files_hidden_by_limit, "team_id": team_id, "ts_from": ts_from, "ts_to": ts_to, "user": user, } ) if isinstance(types, (list, tuple)): kwargs.update({"types": ",".join(types)}) else: kwargs.update({"types": types}) return self.api_call("files.list", http_verb="GET", params=kwargs) ``` Lists & filters team files. [https://docs.slack.dev/reference/methods/files.list](https://docs.slack.dev/reference/methods/files.list) `def files_remote_add(self, *, external_id: str, external_url: str, title: str, filetype: str | None = None, indexable_file_contents: str | bytes | io.IOBase | None = None, preview_image: str | bytes | io.IOBase | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def files_remote_add( self, *, external_id: str, external_url: str, title: str, filetype: Optional[str] = None, indexable_file_contents: Optional[Union[str, bytes, IOBase]] = None, preview_image: Optional[Union[str, bytes, IOBase]] = None, **kwargs, ) -> SlackResponse: """Adds a file from a remote service. https://docs.slack.dev/reference/methods/files.remote.add """ kwargs.update( { "external_id": external_id, "external_url": external_url, "title": title, "filetype": filetype, } ) files = None # preview_image (file): Preview of the document via multipart/form-data. if preview_image is not None or indexable_file_contents is not None: files = { "preview_image": preview_image, "indexable_file_contents": indexable_file_contents, } return self.api_call( # Intentionally using "POST" method over "GET" here "files.remote.add", http_verb="POST", data=kwargs, files=files, ) ``` Adds a file from a remote service. [https://docs.slack.dev/reference/methods/files.remote.add](https://docs.slack.dev/reference/methods/files.remote.add) `def files_remote_info(self, *, external_id: str | None = None, file: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def files_remote_info( self, *, external_id: Optional[str] = None, file: Optional[str] = None, **kwargs, ) -> SlackResponse: """Retrieve information about a remote file added to Slack. https://docs.slack.dev/reference/methods/files.remote.info """ kwargs.update({"external_id": external_id, "file": file}) return self.api_call("files.remote.info", http_verb="GET", params=kwargs) ``` Retrieve information about a remote file added to Slack. [https://docs.slack.dev/reference/methods/files.remote.info](https://docs.slack.dev/reference/methods/files.remote.info) `def files_remote_list(self, *, channel: str | None = None, cursor: str | None = None, limit: int | None = None, ts_from: str | None = None, ts_to: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def files_remote_list( self, *, channel: Optional[str] = None, cursor: Optional[str] = None, limit: Optional[int] = None, ts_from: Optional[str] = None, ts_to: Optional[str] = None, **kwargs, ) -> SlackResponse: """Retrieve information about a remote file added to Slack. https://docs.slack.dev/reference/methods/files.remote.list """ kwargs.update( { "channel": channel, "cursor": cursor, "limit": limit, "ts_from": ts_from, "ts_to": ts_to, } ) return self.api_call("files.remote.list", http_verb="GET", params=kwargs) ``` Retrieve information about a remote file added to Slack. [https://docs.slack.dev/reference/methods/files.remote.list](https://docs.slack.dev/reference/methods/files.remote.list) `def files_remote_remove(self, *, external_id: str | None = None, file: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def files_remote_remove( self, *, external_id: Optional[str] = None, file: Optional[str] = None, **kwargs, ) -> SlackResponse: """Remove a remote file. https://docs.slack.dev/reference/methods/files.remote.remove """ kwargs.update({"external_id": external_id, "file": file}) return self.api_call("files.remote.remove", http_verb="POST", params=kwargs) ``` Remove a remote file. [https://docs.slack.dev/reference/methods/files.remote.remove](https://docs.slack.dev/reference/methods/files.remote.remove) `def files_remote_share(self, *, channels: str | Sequence[str], external_id: str | None = None, file: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def files_remote_share( self, *, channels: Union[str, Sequence[str]], external_id: Optional[str] = None, file: Optional[str] = None, **kwargs, ) -> SlackResponse: """Share a remote file into a channel. https://docs.slack.dev/reference/methods/files.remote.share """ if external_id is None and file is None: raise e.SlackRequestError("Either external_id or file must be provided.") if isinstance(channels, (list, tuple)): kwargs.update({"channels": ",".join(channels)}) else: kwargs.update({"channels": channels}) kwargs.update({"external_id": external_id, "file": file}) return self.api_call("files.remote.share", http_verb="GET", params=kwargs) ``` Share a remote file into a channel. [https://docs.slack.dev/reference/methods/files.remote.share](https://docs.slack.dev/reference/methods/files.remote.share) `def files_remote_update(self, *, external_id: str | None = None, external_url: str | None = None, file: str | None = None, title: str | None = None, filetype: str | None = None, indexable_file_contents: str | None = None, preview_image: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def files_remote_update( self, *, external_id: Optional[str] = None, external_url: Optional[str] = None, file: Optional[str] = None, title: Optional[str] = None, filetype: Optional[str] = None, indexable_file_contents: Optional[str] = None, preview_image: Optional[str] = None, **kwargs, ) -> SlackResponse: """Updates an existing remote file. https://docs.slack.dev/reference/methods/files.remote.update """ kwargs.update( { "external_id": external_id, "external_url": external_url, "file": file, "title": title, "filetype": filetype, } ) files = None # preview_image (file): Preview of the document via multipart/form-data. if preview_image is not None or indexable_file_contents is not None: files = { "preview_image": preview_image, "indexable_file_contents": indexable_file_contents, } return self.api_call( # Intentionally using "POST" method over "GET" here "files.remote.update", http_verb="POST", data=kwargs, files=files, ) ``` Updates an existing remote file. [https://docs.slack.dev/reference/methods/files.remote.update](https://docs.slack.dev/reference/methods/files.remote.update) `def files_revokePublicURL(self, *, file: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def files_revokePublicURL( self, *, file: str, **kwargs, ) -> SlackResponse: """Revokes public/external sharing access for a file https://docs.slack.dev/reference/methods/files.revokePublicURL """ kwargs.update({"file": file}) return self.api_call("files.revokePublicURL", params=kwargs) ``` Revokes public/external sharing access for a file [https://docs.slack.dev/reference/methods/files.revokePublicURL](https://docs.slack.dev/reference/methods/files.revokePublicURL) `def files_sharedPublicURL(self, *, file: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def files_sharedPublicURL( self, *, file: str, **kwargs, ) -> SlackResponse: """Enables a file for public/external sharing. https://docs.slack.dev/reference/methods/files.sharedPublicURL """ kwargs.update({"file": file}) return self.api_call("files.sharedPublicURL", params=kwargs) ``` Enables a file for public/external sharing. [https://docs.slack.dev/reference/methods/files.sharedPublicURL](https://docs.slack.dev/reference/methods/files.sharedPublicURL) `def files_upload(self, *, file: str | bytes | io.IOBase | None = None, content: str | bytes | None = None, filename: str | None = None, filetype: str | None = None, initial_comment: str | None = None, thread_ts: str | None = None, title: str | None = None, channels: str | Sequence[str] | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def files_upload( self, *, file: Optional[Union[str, bytes, IOBase]] = None, content: Optional[Union[str, bytes]] = None, filename: Optional[str] = None, filetype: Optional[str] = None, initial_comment: Optional[str] = None, thread_ts: Optional[str] = None, title: Optional[str] = None, channels: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """Uploads or creates a file. https://docs.slack.dev/reference/methods/files.upload """ _print_files_upload_v2_suggestion() if file is None and content is None: raise e.SlackRequestError("The file or content argument must be specified.") if file is not None and content is not None: raise e.SlackRequestError("You cannot specify both the file and the content argument.") if isinstance(channels, (list, tuple)): kwargs.update({"channels": ",".join(channels)}) else: kwargs.update({"channels": channels}) kwargs.update( { "filename": filename, "filetype": filetype, "initial_comment": initial_comment, "thread_ts": thread_ts, "title": title, } ) if file: if kwargs.get("filename") is None and isinstance(file, str): # use the local filename if filename is missing if kwargs.get("filename") is None: kwargs["filename"] = file.split(os.path.sep)[-1] return self.api_call("files.upload", files={"file": file}, data=kwargs) else: kwargs["content"] = content return self.api_call("files.upload", data=kwargs) ``` Uploads or creates a file. [https://docs.slack.dev/reference/methods/files.upload](https://docs.slack.dev/reference/methods/files.upload) `def files_upload_v2(self, *, filename: str | None = None, file: str | bytes | io.IOBase | os.PathLike | None = None, content: str | bytes | None = None, title: str | None = None, alt_txt: str | None = None, highlight_type: str | None = None, snippet_type: str | None = None, file_uploads: List[Dict[str, Any]] | None = None, channel: str | None = None, channels: List[str] | None = None, initial_comment: str | None = None, thread_ts: str | None = None, request_file_info: bool = True, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def files_upload_v2( self, *, # for sending a single file filename: Optional[str] = None, # you can skip this only when sending along with content parameter file: Optional[Union[str, bytes, IOBase, os.PathLike]] = None, content: Optional[Union[str, bytes]] = None, title: Optional[str] = None, alt_txt: Optional[str] = None, highlight_type: Optional[str] = None, snippet_type: Optional[str] = None, # To upload multiple files at a time file_uploads: Optional[List[Dict[str, Any]]] = None, channel: Optional[str] = None, channels: Optional[List[str]] = None, initial_comment: Optional[str] = None, thread_ts: Optional[str] = None, request_file_info: bool = True, # since v3.23, this flag is no longer necessary **kwargs, ) -> SlackResponse: """This wrapper method provides an easy way to upload files using the following endpoints: - step1: https://docs.slack.dev/reference/methods/files.getUploadURLExternal - step2: "https://files.slack.com/upload/v1/..." URLs returned from files.getUploadURLExternal API - step3: https://docs.slack.dev/reference/methods/files.completeUploadExternal and https://docs.slack.dev/reference/methods/files.info """ if file is None and content is None and file_uploads is None: raise e.SlackRequestError("Any of file, content, and file_uploads must be specified.") if file is not None and content is not None: raise e.SlackRequestError("You cannot specify both the file and the content argument.") # deprecated arguments: filetype = kwargs.get("filetype") if filetype is not None: warnings.warn("The filetype parameter is no longer supported. Please remove it from the arguments.") # step1: files.getUploadURLExternal per file files: List[Dict[str, Any]] = [] if file_uploads is not None: for f in file_uploads: files.append(_to_v2_file_upload_item(f)) else: f = _to_v2_file_upload_item( { "filename": filename, "file": file, "content": content, "title": title, "alt_txt": alt_txt, "highlight_type": highlight_type, "snippet_type": snippet_type, } ) files.append(f) for f in files: url_response = self.files_getUploadURLExternal( filename=f.get("filename"), # type: ignore[arg-type] length=f.get("length"), # type: ignore[arg-type] alt_txt=f.get("alt_txt"), snippet_type=f.get("snippet_type"), token=kwargs.get("token"), ) _validate_for_legacy_client(url_response) f["file_id"] = url_response.get("file_id") # type: ignore[union-attr, unused-ignore] f["upload_url"] = url_response.get("upload_url") # type: ignore[union-attr, unused-ignore] # step2: "https://files.slack.com/upload/v1/..." per file for f in files: upload_result = self._upload_file( url=f["upload_url"], data=f["data"], logger=self._logger, timeout=self.timeout, proxy=self.proxy, ssl=self.ssl, ) if upload_result.status != 200: status = upload_result.status body = upload_result.body message = ( "Failed to upload a file " f"(status: {status}, body: {body}, filename: {f.get('filename')}, title: {f.get('title')})" ) raise e.SlackRequestError(message) # step3: files.completeUploadExternal with all the sets of (file_id + title) completion = self.files_completeUploadExternal( files=[{"id": f["file_id"], "title": f["title"], "highlight_type": f.get("highlight_type")} for f in files], channel_id=channel, channels=channels, initial_comment=initial_comment, thread_ts=thread_ts, **kwargs, ) if len(completion.get("files")) == 1: # type: ignore[arg-type, union-attr, unused-ignore] completion.data["file"] = completion.get("files")[0] # type: ignore[index, union-attr, unused-ignore] return completion ``` This wrapper method provides an easy way to upload files using the following endpoints: * step1: [https://docs.slack.dev/reference/methods/files.getUploadURLExternal](https://docs.slack.dev/reference/methods/files.getUploadURLExternal) * step2: "https://files.slack.com/upload/v1/…" URLs returned from files.getUploadURLExternal API * step3: [https://docs.slack.dev/reference/methods/files.completeUploadExternal](https://docs.slack.dev/reference/methods/files.completeUploadExternal) and [https://docs.slack.dev/reference/methods/files.info](https://docs.slack.dev/reference/methods/files.info) `def functions_completeError(self, *, function_execution_id: str, error: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def functions_completeError( self, *, function_execution_id: str, error: str, **kwargs, ) -> SlackResponse: """Signal the failure to execute a function https://docs.slack.dev/reference/methods/functions.completeError """ kwargs.update({"function_execution_id": function_execution_id, "error": error}) return self.api_call("functions.completeError", params=kwargs) ``` Signal the failure to execute a function [https://docs.slack.dev/reference/methods/functions.completeError](https://docs.slack.dev/reference/methods/functions.completeError) `def functions_completeSuccess(self, *, function_execution_id: str, outputs: Dict[str, Any], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def functions_completeSuccess( self, *, function_execution_id: str, outputs: Dict[str, Any], **kwargs, ) -> SlackResponse: """Signal the successful completion of a function https://docs.slack.dev/reference/methods/functions.completeSuccess """ kwargs.update({"function_execution_id": function_execution_id, "outputs": json.dumps(outputs)}) return self.api_call("functions.completeSuccess", params=kwargs) ``` Signal the successful completion of a function [https://docs.slack.dev/reference/methods/functions.completeSuccess](https://docs.slack.dev/reference/methods/functions.completeSuccess) `def groups_archive(self, *, channel: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def groups_archive( self, *, channel: str, **kwargs, ) -> SlackResponse: """Archives a private channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.archive", json=kwargs) ``` Archives a private channel. `def groups_create(self, *, name: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def groups_create( self, *, name: str, **kwargs, ) -> SlackResponse: """Creates a private channel.""" kwargs.update({"name": name}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.create", json=kwargs) ``` Creates a private channel. `def groups_createChild(self, *, channel: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def groups_createChild( self, *, channel: str, **kwargs, ) -> SlackResponse: """Clones and archives a private channel.""" kwargs.update({"channel": channel}) return self.api_call("groups.createChild", http_verb="GET", params=kwargs) ``` Clones and archives a private channel. `def groups_history(self, *, channel: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def groups_history( self, *, channel: str, **kwargs, ) -> SlackResponse: """Fetches history of messages and events from a private channel.""" kwargs.update({"channel": channel}) return self.api_call("groups.history", http_verb="GET", params=kwargs) ``` Fetches history of messages and events from a private channel. `def groups_info(self, *, channel: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def groups_info( self, *, channel: str, **kwargs, ) -> SlackResponse: """Gets information about a private channel.""" kwargs.update({"channel": channel}) return self.api_call("groups.info", http_verb="GET", params=kwargs) ``` Gets information about a private channel. `def groups_invite(self, *, channel: str, user: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def groups_invite( self, *, channel: str, user: str, **kwargs, ) -> SlackResponse: """Invites a user to a private channel.""" kwargs.update({"channel": channel, "user": user}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.invite", json=kwargs) ``` Invites a user to a private channel. `def groups_kick(self, *, channel: str, user: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def groups_kick( self, *, channel: str, user: str, **kwargs, ) -> SlackResponse: """Removes a user from a private channel.""" kwargs.update({"channel": channel, "user": user}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.kick", json=kwargs) ``` Removes a user from a private channel. `def groups_leave(self, *, channel: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def groups_leave( self, *, channel: str, **kwargs, ) -> SlackResponse: """Leaves a private channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.leave", json=kwargs) ``` Leaves a private channel. `def groups_list(self, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def groups_list( self, **kwargs, ) -> SlackResponse: """Lists private channels that the calling user has access to.""" return self.api_call("groups.list", http_verb="GET", params=kwargs) ``` Lists private channels that the calling user has access to. `def groups_mark(self, *, channel: str, ts: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def groups_mark( self, *, channel: str, ts: str, **kwargs, ) -> SlackResponse: """Sets the read cursor in a private channel.""" kwargs.update({"channel": channel, "ts": ts}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.mark", json=kwargs) ``` Sets the read cursor in a private channel. `def groups_open(self, *, channel: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def groups_open( self, *, channel: str, **kwargs, ) -> SlackResponse: """Opens a private channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.open", json=kwargs) ``` Opens a private channel. `def groups_rename(self, *, channel: str, name: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def groups_rename( self, *, channel: str, name: str, **kwargs, ) -> SlackResponse: """Renames a private channel.""" kwargs.update({"channel": channel, "name": name}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.rename", json=kwargs) ``` Renames a private channel. `def groups_replies(self, *, channel: str, thread_ts: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def groups_replies( self, *, channel: str, thread_ts: str, **kwargs, ) -> SlackResponse: """Retrieve a thread of messages posted to a private channel""" kwargs.update({"channel": channel, "thread_ts": thread_ts}) return self.api_call("groups.replies", http_verb="GET", params=kwargs) ``` Retrieve a thread of messages posted to a private channel `def groups_setPurpose(self, *, channel: str, purpose: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def groups_setPurpose( self, *, channel: str, purpose: str, **kwargs, ) -> SlackResponse: """Sets the purpose for a private channel.""" kwargs.update({"channel": channel, "purpose": purpose}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.setPurpose", json=kwargs) ``` Sets the purpose for a private channel. `def groups_setTopic(self, *, channel: str, topic: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def groups_setTopic( self, *, channel: str, topic: str, **kwargs, ) -> SlackResponse: """Sets the topic for a private channel.""" kwargs.update({"channel": channel, "topic": topic}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.setTopic", json=kwargs) ``` Sets the topic for a private channel. `def groups_unarchive(self, *, channel: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def groups_unarchive( self, *, channel: str, **kwargs, ) -> SlackResponse: """Unarchives a private channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.unarchive", json=kwargs) ``` Unarchives a private channel. `def im_close(self, *, channel: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def im_close( self, *, channel: str, **kwargs, ) -> SlackResponse: """Close a direct message channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("im.close", json=kwargs) ``` Close a direct message channel. `def im_history(self, *, channel: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def im_history( self, *, channel: str, **kwargs, ) -> SlackResponse: """Fetches history of messages and events from direct message channel.""" kwargs.update({"channel": channel}) return self.api_call("im.history", http_verb="GET", params=kwargs) ``` Fetches history of messages and events from direct message channel. `def im_list(self, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def im_list( self, **kwargs, ) -> SlackResponse: """Lists direct message channels for the calling user.""" return self.api_call("im.list", http_verb="GET", params=kwargs) ``` Lists direct message channels for the calling user. `def im_mark(self, *, channel: str, ts: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def im_mark( self, *, channel: str, ts: str, **kwargs, ) -> SlackResponse: """Sets the read cursor in a direct message channel.""" kwargs.update({"channel": channel, "ts": ts}) kwargs = _remove_none_values(kwargs) return self.api_call("im.mark", json=kwargs) ``` Sets the read cursor in a direct message channel. `def im_open(self, *, user: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def im_open( self, *, user: str, **kwargs, ) -> SlackResponse: """Opens a direct message channel.""" kwargs.update({"user": user}) kwargs = _remove_none_values(kwargs) return self.api_call("im.open", json=kwargs) ``` Opens a direct message channel. `def im_replies(self, *, channel: str, thread_ts: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def im_replies( self, *, channel: str, thread_ts: str, **kwargs, ) -> SlackResponse: """Retrieve a thread of messages posted to a direct message conversation""" kwargs.update({"channel": channel, "thread_ts": thread_ts}) return self.api_call("im.replies", http_verb="GET", params=kwargs) ``` Retrieve a thread of messages posted to a direct message conversation `def migration_exchange(self, *, users: str | Sequence[str], team_id: str | None = None, to_old: bool | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def migration_exchange( self, *, users: Union[str, Sequence[str]], team_id: Optional[str] = None, to_old: Optional[bool] = None, **kwargs, ) -> SlackResponse: """For Enterprise Grid workspaces, map local user IDs to global user IDs https://docs.slack.dev/reference/methods/migration.exchange """ if isinstance(users, (list, tuple)): kwargs.update({"users": ",".join(users)}) else: kwargs.update({"users": users}) kwargs.update({"team_id": team_id, "to_old": to_old}) return self.api_call("migration.exchange", http_verb="GET", params=kwargs) ``` For Enterprise Grid workspaces, map local user IDs to global user IDs [https://docs.slack.dev/reference/methods/migration.exchange](https://docs.slack.dev/reference/methods/migration.exchange) `def mpim_close(self, *, channel: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def mpim_close( self, *, channel: str, **kwargs, ) -> SlackResponse: """Closes a multiparty direct message channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("mpim.close", json=kwargs) ``` Closes a multiparty direct message channel. `def mpim_history(self, *, channel: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def mpim_history( self, *, channel: str, **kwargs, ) -> SlackResponse: """Fetches history of messages and events from a multiparty direct message.""" kwargs.update({"channel": channel}) return self.api_call("mpim.history", http_verb="GET", params=kwargs) ``` Fetches history of messages and events from a multiparty direct message. `def mpim_list(self, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def mpim_list( self, **kwargs, ) -> SlackResponse: """Lists multiparty direct message channels for the calling user.""" return self.api_call("mpim.list", http_verb="GET", params=kwargs) ``` Lists multiparty direct message channels for the calling user. `def mpim_mark(self, *, channel: str, ts: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def mpim_mark( self, *, channel: str, ts: str, **kwargs, ) -> SlackResponse: """Sets the read cursor in a multiparty direct message channel.""" kwargs.update({"channel": channel, "ts": ts}) kwargs = _remove_none_values(kwargs) return self.api_call("mpim.mark", json=kwargs) ``` Sets the read cursor in a multiparty direct message channel. `def mpim_open(self, *, users: str | Sequence[str], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def mpim_open( self, *, users: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """This method opens a multiparty direct message.""" if isinstance(users, (list, tuple)): kwargs.update({"users": ",".join(users)}) else: kwargs.update({"users": users}) return self.api_call("mpim.open", params=kwargs) ``` This method opens a multiparty direct message. `def mpim_replies(self, *, channel: str, thread_ts: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def mpim_replies( self, *, channel: str, thread_ts: str, **kwargs, ) -> SlackResponse: """Retrieve a thread of messages posted to a direct message conversation from a multiparty direct message. """ kwargs.update({"channel": channel, "thread_ts": thread_ts}) return self.api_call("mpim.replies", http_verb="GET", params=kwargs) ``` Retrieve a thread of messages posted to a direct message conversation from a multiparty direct message. `def oauth_access(self, *, client_id: str, client_secret: str, code: str, redirect_uri: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def oauth_access( self, *, client_id: str, client_secret: str, code: str, redirect_uri: Optional[str] = None, **kwargs, ) -> SlackResponse: """Exchanges a temporary OAuth verifier code for an access token. https://docs.slack.dev/reference/methods/oauth.access """ if redirect_uri is not None: kwargs.update({"redirect_uri": redirect_uri}) kwargs.update({"code": code}) return self.api_call( "oauth.access", data=kwargs, auth={"client_id": client_id, "client_secret": client_secret}, ) ``` Exchanges a temporary OAuth verifier code for an access token. [https://docs.slack.dev/reference/methods/oauth.access](https://docs.slack.dev/reference/methods/oauth.access) `def oauth_v2_access(self, *, client_id: str, client_secret: str, code: str | None = None, redirect_uri: str | None = None, grant_type: str | None = None, refresh_token: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def oauth_v2_access( self, *, client_id: str, client_secret: str, # This field is required when processing the OAuth redirect URL requests # while it's absent for token rotation code: Optional[str] = None, redirect_uri: Optional[str] = None, # This field is required for token rotation grant_type: Optional[str] = None, # This field is required for token rotation refresh_token: Optional[str] = None, **kwargs, ) -> SlackResponse: """Exchanges a temporary OAuth verifier code for an access token. https://docs.slack.dev/reference/methods/oauth.v2.access """ if redirect_uri is not None: kwargs.update({"redirect_uri": redirect_uri}) if code is not None: kwargs.update({"code": code}) if grant_type is not None: kwargs.update({"grant_type": grant_type}) if refresh_token is not None: kwargs.update({"refresh_token": refresh_token}) return self.api_call( "oauth.v2.access", data=kwargs, auth={"client_id": client_id, "client_secret": client_secret}, ) ``` Exchanges a temporary OAuth verifier code for an access token. [https://docs.slack.dev/reference/methods/oauth.v2.access](https://docs.slack.dev/reference/methods/oauth.v2.access) `def oauth_v2_exchange(self, *, token: str, client_id: str, client_secret: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def oauth_v2_exchange( self, *, token: str, client_id: str, client_secret: str, **kwargs, ) -> SlackResponse: """Exchanges a legacy access token for a new expiring access token and refresh token https://docs.slack.dev/reference/methods/oauth.v2.exchange """ kwargs.update({"client_id": client_id, "client_secret": client_secret, "token": token}) return self.api_call("oauth.v2.exchange", params=kwargs) ``` Exchanges a legacy access token for a new expiring access token and refresh token [https://docs.slack.dev/reference/methods/oauth.v2.exchange](https://docs.slack.dev/reference/methods/oauth.v2.exchange) `def openid_connect_token(self, client_id: str, client_secret: str, code: str | None = None, redirect_uri: str | None = None, grant_type: str | None = None, refresh_token: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def openid_connect_token( self, client_id: str, client_secret: str, code: Optional[str] = None, redirect_uri: Optional[str] = None, grant_type: Optional[str] = None, refresh_token: Optional[str] = None, **kwargs, ) -> SlackResponse: """Exchanges a temporary OAuth verifier code for an access token for Sign in with Slack. https://docs.slack.dev/reference/methods/openid.connect.token """ if redirect_uri is not None: kwargs.update({"redirect_uri": redirect_uri}) if code is not None: kwargs.update({"code": code}) if grant_type is not None: kwargs.update({"grant_type": grant_type}) if refresh_token is not None: kwargs.update({"refresh_token": refresh_token}) return self.api_call( "openid.connect.token", data=kwargs, auth={"client_id": client_id, "client_secret": client_secret}, ) ``` Exchanges a temporary OAuth verifier code for an access token for Sign in with Slack. [https://docs.slack.dev/reference/methods/openid.connect.token](https://docs.slack.dev/reference/methods/openid.connect.token) `def openid_connect_userInfo(self, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def openid_connect_userInfo( self, **kwargs, ) -> SlackResponse: """Get the identity of a user who has authorized Sign in with Slack. https://docs.slack.dev/reference/methods/openid.connect.userInfo """ return self.api_call("openid.connect.userInfo", params=kwargs) ``` Get the identity of a user who has authorized Sign in with Slack. [https://docs.slack.dev/reference/methods/openid.connect.userInfo](https://docs.slack.dev/reference/methods/openid.connect.userInfo) `def pins_add(self, *, channel: str, timestamp: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def pins_add( self, *, channel: str, timestamp: Optional[str] = None, **kwargs, ) -> SlackResponse: """Pins an item to a channel. https://docs.slack.dev/reference/methods/pins.add """ kwargs.update({"channel": channel, "timestamp": timestamp}) return self.api_call("pins.add", params=kwargs) ``` Pins an item to a channel. [https://docs.slack.dev/reference/methods/pins.add](https://docs.slack.dev/reference/methods/pins.add) `def pins_list(self, *, channel: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def pins_list( self, *, channel: str, **kwargs, ) -> SlackResponse: """Lists items pinned to a channel. https://docs.slack.dev/reference/methods/pins.list """ kwargs.update({"channel": channel}) return self.api_call("pins.list", http_verb="GET", params=kwargs) ``` Lists items pinned to a channel. [https://docs.slack.dev/reference/methods/pins.list](https://docs.slack.dev/reference/methods/pins.list) `def pins_remove(self, *, channel: str, timestamp: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def pins_remove( self, *, channel: str, timestamp: Optional[str] = None, **kwargs, ) -> SlackResponse: """Un-pins an item from a channel. https://docs.slack.dev/reference/methods/pins.remove """ kwargs.update({"channel": channel, "timestamp": timestamp}) return self.api_call("pins.remove", params=kwargs) ``` Un-pins an item from a channel. [https://docs.slack.dev/reference/methods/pins.remove](https://docs.slack.dev/reference/methods/pins.remove) `def reactions_add(self, *, channel: str, name: str, timestamp: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def reactions_add( self, *, channel: str, name: str, timestamp: str, **kwargs, ) -> SlackResponse: """Adds a reaction to an item. https://docs.slack.dev/reference/methods/reactions.add """ kwargs.update({"channel": channel, "name": name, "timestamp": timestamp}) return self.api_call("reactions.add", params=kwargs) ``` Adds a reaction to an item. [https://docs.slack.dev/reference/methods/reactions.add](https://docs.slack.dev/reference/methods/reactions.add) `def reactions_get(self, *, channel: str | None = None, file: str | None = None, file_comment: str | None = None, full: bool | None = None, timestamp: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def reactions_get( self, *, channel: Optional[str] = None, file: Optional[str] = None, file_comment: Optional[str] = None, full: Optional[bool] = None, timestamp: Optional[str] = None, **kwargs, ) -> SlackResponse: """Gets reactions for an item. https://docs.slack.dev/reference/methods/reactions.get """ kwargs.update( { "channel": channel, "file": file, "file_comment": file_comment, "full": full, "timestamp": timestamp, } ) return self.api_call("reactions.get", http_verb="GET", params=kwargs) ``` Gets reactions for an item. [https://docs.slack.dev/reference/methods/reactions.get](https://docs.slack.dev/reference/methods/reactions.get) `def reactions_list(self, *, count: int | None = None, cursor: str | None = None, full: bool | None = None, limit: int | None = None, page: int | None = None, team_id: str | None = None, user: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def reactions_list( self, *, count: Optional[int] = None, cursor: Optional[str] = None, full: Optional[bool] = None, limit: Optional[int] = None, page: Optional[int] = None, team_id: Optional[str] = None, user: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists reactions made by a user. https://docs.slack.dev/reference/methods/reactions.list """ kwargs.update( { "count": count, "cursor": cursor, "full": full, "limit": limit, "page": page, "team_id": team_id, "user": user, } ) return self.api_call("reactions.list", http_verb="GET", params=kwargs) ``` Lists reactions made by a user. [https://docs.slack.dev/reference/methods/reactions.list](https://docs.slack.dev/reference/methods/reactions.list) `def reactions_remove(self, *, name: str, channel: str | None = None, file: str | None = None, file_comment: str | None = None, timestamp: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def reactions_remove( self, *, name: str, channel: Optional[str] = None, file: Optional[str] = None, file_comment: Optional[str] = None, timestamp: Optional[str] = None, **kwargs, ) -> SlackResponse: """Removes a reaction from an item. https://docs.slack.dev/reference/methods/reactions.remove """ kwargs.update( { "name": name, "channel": channel, "file": file, "file_comment": file_comment, "timestamp": timestamp, } ) return self.api_call("reactions.remove", params=kwargs) ``` Removes a reaction from an item. [https://docs.slack.dev/reference/methods/reactions.remove](https://docs.slack.dev/reference/methods/reactions.remove) `def reminders_add(self, *, text: str, time: str, team_id: str | None = None, user: str | None = None, recurrence: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def reminders_add( self, *, text: str, time: str, team_id: Optional[str] = None, user: Optional[str] = None, recurrence: Optional[str] = None, **kwargs, ) -> SlackResponse: """Creates a reminder. https://docs.slack.dev/reference/methods/reminders.add """ kwargs.update( { "text": text, "time": time, "team_id": team_id, "user": user, "recurrence": recurrence, } ) return self.api_call("reminders.add", params=kwargs) ``` Creates a reminder. [https://docs.slack.dev/reference/methods/reminders.add](https://docs.slack.dev/reference/methods/reminders.add) `def reminders_complete(self, *, reminder: str, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def reminders_complete( self, *, reminder: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Marks a reminder as complete. https://docs.slack.dev/reference/methods/reminders.complete """ kwargs.update({"reminder": reminder, "team_id": team_id}) return self.api_call("reminders.complete", params=kwargs) ``` Marks a reminder as complete. [https://docs.slack.dev/reference/methods/reminders.complete](https://docs.slack.dev/reference/methods/reminders.complete) `def reminders_delete(self, *, reminder: str, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def reminders_delete( self, *, reminder: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Deletes a reminder. https://docs.slack.dev/reference/methods/reminders.delete """ kwargs.update({"reminder": reminder, "team_id": team_id}) return self.api_call("reminders.delete", params=kwargs) ``` Deletes a reminder. [https://docs.slack.dev/reference/methods/reminders.delete](https://docs.slack.dev/reference/methods/reminders.delete) `def reminders_info(self, *, reminder: str, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def reminders_info( self, *, reminder: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Gets information about a reminder. https://docs.slack.dev/reference/methods/reminders.info """ kwargs.update({"reminder": reminder, "team_id": team_id}) return self.api_call("reminders.info", http_verb="GET", params=kwargs) ``` Gets information about a reminder. [https://docs.slack.dev/reference/methods/reminders.info](https://docs.slack.dev/reference/methods/reminders.info) `def reminders_list(self, *, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def reminders_list( self, *, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists all reminders created by or for a given user. https://docs.slack.dev/reference/methods/reminders.list """ kwargs.update({"team_id": team_id}) return self.api_call("reminders.list", http_verb="GET", params=kwargs) ``` Lists all reminders created by or for a given user. [https://docs.slack.dev/reference/methods/reminders.list](https://docs.slack.dev/reference/methods/reminders.list) `def rtm_connect(self, *, batch_presence_aware: bool | None = None, presence_sub: bool | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def rtm_connect( self, *, batch_presence_aware: Optional[bool] = None, presence_sub: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Starts a Real Time Messaging session. https://docs.slack.dev/reference/methods/rtm.connect """ kwargs.update({"batch_presence_aware": batch_presence_aware, "presence_sub": presence_sub}) return self.api_call("rtm.connect", http_verb="GET", params=kwargs) ``` Starts a Real Time Messaging session. [https://docs.slack.dev/reference/methods/rtm.connect](https://docs.slack.dev/reference/methods/rtm.connect) `def rtm_start(self, *, batch_presence_aware: bool | None = None, include_locale: bool | None = None, mpim_aware: bool | None = None, no_latest: bool | None = None, no_unreads: bool | None = None, presence_sub: bool | None = None, simple_latest: bool | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def rtm_start( self, *, batch_presence_aware: Optional[bool] = None, include_locale: Optional[bool] = None, mpim_aware: Optional[bool] = None, no_latest: Optional[bool] = None, no_unreads: Optional[bool] = None, presence_sub: Optional[bool] = None, simple_latest: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Starts a Real Time Messaging session. https://docs.slack.dev/reference/methods/rtm.start """ kwargs.update( { "batch_presence_aware": batch_presence_aware, "include_locale": include_locale, "mpim_aware": mpim_aware, "no_latest": no_latest, "no_unreads": no_unreads, "presence_sub": presence_sub, "simple_latest": simple_latest, } ) return self.api_call("rtm.start", http_verb="GET", params=kwargs) ``` Starts a Real Time Messaging session. [https://docs.slack.dev/reference/methods/rtm.start](https://docs.slack.dev/reference/methods/rtm.start) `def search_all(self, *, query: str, count: int | None = None, highlight: bool | None = None, page: int | None = None, sort: str | None = None, sort_dir: str | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def search_all( self, *, query: str, count: Optional[int] = None, highlight: Optional[bool] = None, page: Optional[int] = None, sort: Optional[str] = None, sort_dir: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Searches for messages and files matching a query. https://docs.slack.dev/reference/methods/search.all """ kwargs.update( { "query": query, "count": count, "highlight": highlight, "page": page, "sort": sort, "sort_dir": sort_dir, "team_id": team_id, } ) return self.api_call("search.all", http_verb="GET", params=kwargs) ``` Searches for messages and files matching a query. [https://docs.slack.dev/reference/methods/search.all](https://docs.slack.dev/reference/methods/search.all) `def search_files(self, *, query: str, count: int | None = None, highlight: bool | None = None, page: int | None = None, sort: str | None = None, sort_dir: str | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def search_files( self, *, query: str, count: Optional[int] = None, highlight: Optional[bool] = None, page: Optional[int] = None, sort: Optional[str] = None, sort_dir: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Searches for files matching a query. https://docs.slack.dev/reference/methods/search.files """ kwargs.update( { "query": query, "count": count, "highlight": highlight, "page": page, "sort": sort, "sort_dir": sort_dir, "team_id": team_id, } ) return self.api_call("search.files", http_verb="GET", params=kwargs) ``` Searches for files matching a query. [https://docs.slack.dev/reference/methods/search.files](https://docs.slack.dev/reference/methods/search.files) `def search_messages(self, *, query: str, count: int | None = None, cursor: str | None = None, highlight: bool | None = None, page: int | None = None, sort: str | None = None, sort_dir: str | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def search_messages( self, *, query: str, count: Optional[int] = None, cursor: Optional[str] = None, highlight: Optional[bool] = None, page: Optional[int] = None, sort: Optional[str] = None, sort_dir: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Searches for messages matching a query. https://docs.slack.dev/reference/methods/search.messages """ kwargs.update( { "query": query, "count": count, "cursor": cursor, "highlight": highlight, "page": page, "sort": sort, "sort_dir": sort_dir, "team_id": team_id, } ) return self.api_call("search.messages", http_verb="GET", params=kwargs) ``` Searches for messages matching a query. [https://docs.slack.dev/reference/methods/search.messages](https://docs.slack.dev/reference/methods/search.messages) `def slackLists_access_delete(self, *, list_id: str, channel_ids: List[str] | None = None, user_ids: List[str] | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def slackLists_access_delete( self, *, list_id: str, channel_ids: Optional[List[str]] = None, user_ids: Optional[List[str]] = None, **kwargs, ) -> SlackResponse: """Revoke access to a List for specified entities. https://docs.slack.dev/reference/methods/slackLists.access.delete """ kwargs.update({"list_id": list_id, "channel_ids": channel_ids, "user_ids": user_ids}) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.access.delete", json=kwargs) ``` Revoke access to a List for specified entities. [https://docs.slack.dev/reference/methods/slackLists.access.delete](https://docs.slack.dev/reference/methods/slackLists.access.delete) `def slackLists_access_set(self, *, list_id: str, access_level: str, channel_ids: List[str] | None = None, user_ids: List[str] | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def slackLists_access_set( self, *, list_id: str, access_level: str, channel_ids: Optional[List[str]] = None, user_ids: Optional[List[str]] = None, **kwargs, ) -> SlackResponse: """Set the access level to a List for specified entities. https://docs.slack.dev/reference/methods/slackLists.access.set """ kwargs.update({"list_id": list_id, "access_level": access_level, "channel_ids": channel_ids, "user_ids": user_ids}) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.access.set", json=kwargs) ``` Set the access level to a List for specified entities. [https://docs.slack.dev/reference/methods/slackLists.access.set](https://docs.slack.dev/reference/methods/slackLists.access.set) `def slackLists_create(self, *, name: str, description_blocks: str | Sequence[Dict | [RichTextBlock](models/blocks/blocks.html#slack_sdk.models.blocks.blocks.RichTextBlock "slack_sdk.models.blocks.blocks.RichTextBlock")] | None = None, schema: List[Dict[str, Any]] | None = None, copy_from_list_id: str | None = None, include_copied_list_records: bool | None = None, todo_mode: bool | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def slackLists_create( self, *, name: str, description_blocks: Optional[Union[str, Sequence[Union[Dict, RichTextBlock]]]] = None, schema: Optional[List[Dict[str, Any]]] = None, copy_from_list_id: Optional[str] = None, include_copied_list_records: Optional[bool] = None, todo_mode: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Creates a List. https://docs.slack.dev/reference/methods/slackLists.create """ kwargs.update( { "name": name, "description_blocks": description_blocks, "schema": schema, "copy_from_list_id": copy_from_list_id, "include_copied_list_records": include_copied_list_records, "todo_mode": todo_mode, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.create", json=kwargs) ``` Creates a List. [https://docs.slack.dev/reference/methods/slackLists.create](https://docs.slack.dev/reference/methods/slackLists.create) `def slackLists_download_get(self, *, list_id: str, job_id: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def slackLists_download_get( self, *, list_id: str, job_id: str, **kwargs, ) -> SlackResponse: """Retrieve List download URL from an export job to download List contents. https://docs.slack.dev/reference/methods/slackLists.download.get """ kwargs.update( { "list_id": list_id, "job_id": job_id, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.download.get", json=kwargs) ``` Retrieve List download URL from an export job to download List contents. [https://docs.slack.dev/reference/methods/slackLists.download.get](https://docs.slack.dev/reference/methods/slackLists.download.get) `def slackLists_download_start(self, *, list_id: str, include_archived: bool | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def slackLists_download_start( self, *, list_id: str, include_archived: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Initiate a job to export List contents. https://docs.slack.dev/reference/methods/slackLists.download.start """ kwargs.update( { "list_id": list_id, "include_archived": include_archived, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.download.start", json=kwargs) ``` Initiate a job to export List contents. [https://docs.slack.dev/reference/methods/slackLists.download.start](https://docs.slack.dev/reference/methods/slackLists.download.start) `def slackLists_items_create(self, *, list_id: str, duplicated_item_id: str | None = None, parent_item_id: str | None = None, initial_fields: List[Dict[str, Any]] | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def slackLists_items_create( self, *, list_id: str, duplicated_item_id: Optional[str] = None, parent_item_id: Optional[str] = None, initial_fields: Optional[List[Dict[str, Any]]] = None, **kwargs, ) -> SlackResponse: """Add a new item to an existing List. https://docs.slack.dev/reference/methods/slackLists.items.create """ kwargs.update( { "list_id": list_id, "duplicated_item_id": duplicated_item_id, "parent_item_id": parent_item_id, "initial_fields": initial_fields, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.items.create", json=kwargs) ``` Add a new item to an existing List. [https://docs.slack.dev/reference/methods/slackLists.items.create](https://docs.slack.dev/reference/methods/slackLists.items.create) `def slackLists_items_delete(self, *, list_id: str, id: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def slackLists_items_delete( self, *, list_id: str, id: str, **kwargs, ) -> SlackResponse: """Deletes an item from an existing List. https://docs.slack.dev/reference/methods/slackLists.items.delete """ kwargs.update( { "list_id": list_id, "id": id, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.items.delete", json=kwargs) ``` Deletes an item from an existing List. [https://docs.slack.dev/reference/methods/slackLists.items.delete](https://docs.slack.dev/reference/methods/slackLists.items.delete) `def slackLists_items_deleteMultiple(self, *, list_id: str, ids: List[str], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def slackLists_items_deleteMultiple( self, *, list_id: str, ids: List[str], **kwargs, ) -> SlackResponse: """Deletes multiple items from an existing List. https://docs.slack.dev/reference/methods/slackLists.items.deleteMultiple """ kwargs.update( { "list_id": list_id, "ids": ids, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.items.deleteMultiple", json=kwargs) ``` Deletes multiple items from an existing List. [https://docs.slack.dev/reference/methods/slackLists.items.deleteMultiple](https://docs.slack.dev/reference/methods/slackLists.items.deleteMultiple) `def slackLists_items_info(self, *, list_id: str, id: str, include_is_subscribed: bool | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def slackLists_items_info( self, *, list_id: str, id: str, include_is_subscribed: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Get a row from a List. https://docs.slack.dev/reference/methods/slackLists.items.info """ kwargs.update( { "list_id": list_id, "id": id, "include_is_subscribed": include_is_subscribed, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.items.info", json=kwargs) ``` Get a row from a List. [https://docs.slack.dev/reference/methods/slackLists.items.info](https://docs.slack.dev/reference/methods/slackLists.items.info) `def slackLists_items_list(self, *, list_id: str, limit: int | None = None, cursor: str | None = None, archived: bool | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def slackLists_items_list( self, *, list_id: str, limit: Optional[int] = None, cursor: Optional[str] = None, archived: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Get records from a List. https://docs.slack.dev/reference/methods/slackLists.items.list """ kwargs.update( { "list_id": list_id, "limit": limit, "cursor": cursor, "archived": archived, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.items.list", json=kwargs) ``` Get records from a List. [https://docs.slack.dev/reference/methods/slackLists.items.list](https://docs.slack.dev/reference/methods/slackLists.items.list) `def slackLists_items_update(self, *, list_id: str, cells: List[Dict[str, Any]], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def slackLists_items_update( self, *, list_id: str, cells: List[Dict[str, Any]], **kwargs, ) -> SlackResponse: """Updates cells in a List. https://docs.slack.dev/reference/methods/slackLists.items.update """ kwargs.update( { "list_id": list_id, "cells": cells, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.items.update", json=kwargs) ``` Updates cells in a List. [https://docs.slack.dev/reference/methods/slackLists.items.update](https://docs.slack.dev/reference/methods/slackLists.items.update) `def slackLists_update(self, *, id: str, name: str | None = None, description_blocks: str | Sequence[Dict | [RichTextBlock](models/blocks/blocks.html#slack_sdk.models.blocks.blocks.RichTextBlock "slack_sdk.models.blocks.blocks.RichTextBlock")] | None = None, todo_mode: bool | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def slackLists_update( self, *, id: str, name: Optional[str] = None, description_blocks: Optional[Union[str, Sequence[Union[Dict, RichTextBlock]]]] = None, todo_mode: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Update a List. https://docs.slack.dev/reference/methods/slackLists.update """ kwargs.update( { "id": id, "name": name, "description_blocks": description_blocks, "todo_mode": todo_mode, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.update", json=kwargs) ``` Update a List. [https://docs.slack.dev/reference/methods/slackLists.update](https://docs.slack.dev/reference/methods/slackLists.update) `def stars_add(self, *, channel: str | None = None, file: str | None = None, file_comment: str | None = None, timestamp: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def stars_add( self, *, channel: Optional[str] = None, file: Optional[str] = None, file_comment: Optional[str] = None, timestamp: Optional[str] = None, **kwargs, ) -> SlackResponse: """Adds a star to an item. https://docs.slack.dev/reference/methods/stars.add """ kwargs.update( { "channel": channel, "file": file, "file_comment": file_comment, "timestamp": timestamp, } ) return self.api_call("stars.add", params=kwargs) ``` Adds a star to an item. [https://docs.slack.dev/reference/methods/stars.add](https://docs.slack.dev/reference/methods/stars.add) `def stars_list(self, *, count: int | None = None, cursor: str | None = None, limit: int | None = None, page: int | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def stars_list( self, *, count: Optional[int] = None, cursor: Optional[str] = None, limit: Optional[int] = None, page: Optional[int] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists stars for a user. https://docs.slack.dev/reference/methods/stars.list """ kwargs.update( { "count": count, "cursor": cursor, "limit": limit, "page": page, "team_id": team_id, } ) return self.api_call("stars.list", http_verb="GET", params=kwargs) ``` Lists stars for a user. [https://docs.slack.dev/reference/methods/stars.list](https://docs.slack.dev/reference/methods/stars.list) `def stars_remove(self, *, channel: str | None = None, file: str | None = None, file_comment: str | None = None, timestamp: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def stars_remove( self, *, channel: Optional[str] = None, file: Optional[str] = None, file_comment: Optional[str] = None, timestamp: Optional[str] = None, **kwargs, ) -> SlackResponse: """Removes a star from an item. https://docs.slack.dev/reference/methods/stars.remove """ kwargs.update( { "channel": channel, "file": file, "file_comment": file_comment, "timestamp": timestamp, } ) return self.api_call("stars.remove", params=kwargs) ``` Removes a star from an item. [https://docs.slack.dev/reference/methods/stars.remove](https://docs.slack.dev/reference/methods/stars.remove) `def team_accessLogs(self, *, before: str | int | None = None, count: str | int | None = None, page: str | int | None = None, team_id: str | None = None, cursor: str | None = None, limit: int | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def team_accessLogs( self, *, before: Optional[Union[int, str]] = None, count: Optional[Union[int, str]] = None, page: Optional[Union[int, str]] = None, team_id: Optional[str] = None, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Gets the access logs for the current team. https://docs.slack.dev/reference/methods/team.accessLogs """ kwargs.update( { "before": before, "count": count, "page": page, "team_id": team_id, "cursor": cursor, "limit": limit, } ) return self.api_call("team.accessLogs", http_verb="GET", params=kwargs) ``` Gets the access logs for the current team. [https://docs.slack.dev/reference/methods/team.accessLogs](https://docs.slack.dev/reference/methods/team.accessLogs) `def team_billableInfo(self, *, team_id: str | None = None, user: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def team_billableInfo( self, *, team_id: Optional[str] = None, user: Optional[str] = None, **kwargs, ) -> SlackResponse: """Gets billable users information for the current team. https://docs.slack.dev/reference/methods/team.billableInfo """ kwargs.update({"team_id": team_id, "user": user}) return self.api_call("team.billableInfo", http_verb="GET", params=kwargs) ``` Gets billable users information for the current team. [https://docs.slack.dev/reference/methods/team.billableInfo](https://docs.slack.dev/reference/methods/team.billableInfo) `def team_billing_info(self, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def team_billing_info( self, **kwargs, ) -> SlackResponse: """Reads a workspace's billing plan information. https://docs.slack.dev/reference/methods/team.billing.info """ return self.api_call("team.billing.info", params=kwargs) ``` Reads a workspace's billing plan information. [https://docs.slack.dev/reference/methods/team.billing.info](https://docs.slack.dev/reference/methods/team.billing.info) `def team_externalTeams_disconnect(self, *, target_team: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def team_externalTeams_disconnect( self, *, target_team: str, **kwargs, ) -> SlackResponse: """Disconnects an external organization. https://docs.slack.dev/reference/methods/team.externalTeams.disconnect """ kwargs.update( { "target_team": target_team, } ) return self.api_call("team.externalTeams.disconnect", params=kwargs) ``` Disconnects an external organization. [https://docs.slack.dev/reference/methods/team.externalTeams.disconnect](https://docs.slack.dev/reference/methods/team.externalTeams.disconnect) `def team_externalTeams_list(self, *, connection_status_filter: str | None = None, slack_connect_pref_filter: Sequence[str] | None = None, sort_direction: str | None = None, sort_field: str | None = None, workspace_filter: Sequence[str] | None = None, cursor: str | None = None, limit: int | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def team_externalTeams_list( self, *, connection_status_filter: Optional[str] = None, slack_connect_pref_filter: Optional[Sequence[str]] = None, sort_direction: Optional[str] = None, sort_field: Optional[str] = None, workspace_filter: Optional[Sequence[str]] = None, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Returns a list of all the external teams connected and details about the connection. https://docs.slack.dev/reference/methods/team.externalTeams.list """ kwargs.update( { "connection_status_filter": connection_status_filter, "sort_direction": sort_direction, "sort_field": sort_field, "cursor": cursor, "limit": limit, } ) if slack_connect_pref_filter is not None: if isinstance(slack_connect_pref_filter, (list, tuple)): kwargs.update({"slack_connect_pref_filter": ",".join(slack_connect_pref_filter)}) else: kwargs.update({"slack_connect_pref_filter": slack_connect_pref_filter}) if workspace_filter is not None: if isinstance(workspace_filter, (list, tuple)): kwargs.update({"workspace_filter": ",".join(workspace_filter)}) else: kwargs.update({"workspace_filter": workspace_filter}) return self.api_call("team.externalTeams.list", http_verb="GET", params=kwargs) ``` Returns a list of all the external teams connected and details about the connection. [https://docs.slack.dev/reference/methods/team.externalTeams.list](https://docs.slack.dev/reference/methods/team.externalTeams.list) `def team_info(self, *, team: str | None = None, domain: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def team_info( self, *, team: Optional[str] = None, domain: Optional[str] = None, **kwargs, ) -> SlackResponse: """Gets information about the current team. https://docs.slack.dev/reference/methods/team.info """ kwargs.update({"team": team, "domain": domain}) return self.api_call("team.info", http_verb="GET", params=kwargs) ``` Gets information about the current team. [https://docs.slack.dev/reference/methods/team.info](https://docs.slack.dev/reference/methods/team.info) `def team_integrationLogs(self, *, app_id: str | None = None, change_type: str | None = None, count: str | int | None = None, page: str | int | None = None, service_id: str | None = None, team_id: str | None = None, user: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def team_integrationLogs( self, *, app_id: Optional[str] = None, change_type: Optional[str] = None, count: Optional[Union[int, str]] = None, page: Optional[Union[int, str]] = None, service_id: Optional[str] = None, team_id: Optional[str] = None, user: Optional[str] = None, **kwargs, ) -> SlackResponse: """Gets the integration logs for the current team. https://docs.slack.dev/reference/methods/team.integrationLogs """ kwargs.update( { "app_id": app_id, "change_type": change_type, "count": count, "page": page, "service_id": service_id, "team_id": team_id, "user": user, } ) return self.api_call("team.integrationLogs", http_verb="GET", params=kwargs) ``` Gets the integration logs for the current team. [https://docs.slack.dev/reference/methods/team.integrationLogs](https://docs.slack.dev/reference/methods/team.integrationLogs) `def team_preferences_list(self, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def team_preferences_list( self, **kwargs, ) -> SlackResponse: """Retrieve a list of a workspace's team preferences. https://docs.slack.dev/reference/methods/team.preferences.list """ return self.api_call("team.preferences.list", params=kwargs) ``` Retrieve a list of a workspace's team preferences. [https://docs.slack.dev/reference/methods/team.preferences.list](https://docs.slack.dev/reference/methods/team.preferences.list) `def team_profile_get(self, *, visibility: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def team_profile_get( self, *, visibility: Optional[str] = None, **kwargs, ) -> SlackResponse: """Retrieve a team's profile. https://docs.slack.dev/reference/methods/team.profile.get """ kwargs.update({"visibility": visibility}) return self.api_call("team.profile.get", http_verb="GET", params=kwargs) ``` Retrieve a team's profile. [https://docs.slack.dev/reference/methods/team.profile.get](https://docs.slack.dev/reference/methods/team.profile.get) `def tooling_tokens_rotate(self, *, refresh_token: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def tooling_tokens_rotate( self, *, refresh_token: str, **kwargs, ) -> SlackResponse: """Exchanges a refresh token for a new app configuration token https://docs.slack.dev/reference/methods/tooling.tokens.rotate """ kwargs.update({"refresh_token": refresh_token}) return self.api_call("tooling.tokens.rotate", params=kwargs) ``` Exchanges a refresh token for a new app configuration token [https://docs.slack.dev/reference/methods/tooling.tokens.rotate](https://docs.slack.dev/reference/methods/tooling.tokens.rotate) `def usergroups_create(self, *, name: str, channels: str | Sequence[str] | None = None, description: str | None = None, handle: str | None = None, include_count: bool | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def usergroups_create( self, *, name: str, channels: Optional[Union[str, Sequence[str]]] = None, description: Optional[str] = None, handle: Optional[str] = None, include_count: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Create a User Group https://docs.slack.dev/reference/methods/usergroups.create """ kwargs.update( { "name": name, "description": description, "handle": handle, "include_count": include_count, "team_id": team_id, } ) if isinstance(channels, (list, tuple)): kwargs.update({"channels": ",".join(channels)}) else: kwargs.update({"channels": channels}) return self.api_call("usergroups.create", params=kwargs) ``` Create a User Group [https://docs.slack.dev/reference/methods/usergroups.create](https://docs.slack.dev/reference/methods/usergroups.create) `def usergroups_disable(self, *, usergroup: str, include_count: bool | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def usergroups_disable( self, *, usergroup: str, include_count: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Disable an existing User Group https://docs.slack.dev/reference/methods/usergroups.disable """ kwargs.update({"usergroup": usergroup, "include_count": include_count, "team_id": team_id}) return self.api_call("usergroups.disable", params=kwargs) ``` Disable an existing User Group [https://docs.slack.dev/reference/methods/usergroups.disable](https://docs.slack.dev/reference/methods/usergroups.disable) `def usergroups_enable(self, *, usergroup: str, include_count: bool | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def usergroups_enable( self, *, usergroup: str, include_count: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Enable a User Group https://docs.slack.dev/reference/methods/usergroups.enable """ kwargs.update({"usergroup": usergroup, "include_count": include_count, "team_id": team_id}) return self.api_call("usergroups.enable", params=kwargs) ``` Enable a User Group [https://docs.slack.dev/reference/methods/usergroups.enable](https://docs.slack.dev/reference/methods/usergroups.enable) `def usergroups_list(self, *, include_count: bool | None = None, include_disabled: bool | None = None, include_users: bool | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def usergroups_list( self, *, include_count: Optional[bool] = None, include_disabled: Optional[bool] = None, include_users: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List all User Groups for a team https://docs.slack.dev/reference/methods/usergroups.list """ kwargs.update( { "include_count": include_count, "include_disabled": include_disabled, "include_users": include_users, "team_id": team_id, } ) return self.api_call("usergroups.list", http_verb="GET", params=kwargs) ``` List all User Groups for a team [https://docs.slack.dev/reference/methods/usergroups.list](https://docs.slack.dev/reference/methods/usergroups.list) `def usergroups_update(self, *, usergroup: str, channels: str | Sequence[str] | None = None, description: str | None = None, handle: str | None = None, include_count: bool | None = None, name: str | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def usergroups_update( self, *, usergroup: str, channels: Optional[Union[str, Sequence[str]]] = None, description: Optional[str] = None, handle: Optional[str] = None, include_count: Optional[bool] = None, name: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Update an existing User Group https://docs.slack.dev/reference/methods/usergroups.update """ kwargs.update( { "usergroup": usergroup, "description": description, "handle": handle, "include_count": include_count, "name": name, "team_id": team_id, } ) if isinstance(channels, (list, tuple)): kwargs.update({"channels": ",".join(channels)}) else: kwargs.update({"channels": channels}) return self.api_call("usergroups.update", params=kwargs) ``` Update an existing User Group [https://docs.slack.dev/reference/methods/usergroups.update](https://docs.slack.dev/reference/methods/usergroups.update) `def usergroups_users_list(self, *, usergroup: str, include_disabled: bool | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def usergroups_users_list( self, *, usergroup: str, include_disabled: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List all users in a User Group https://docs.slack.dev/reference/methods/usergroups.users.list """ kwargs.update( { "usergroup": usergroup, "include_disabled": include_disabled, "team_id": team_id, } ) return self.api_call("usergroups.users.list", http_verb="GET", params=kwargs) ``` List all users in a User Group [https://docs.slack.dev/reference/methods/usergroups.users.list](https://docs.slack.dev/reference/methods/usergroups.users.list) `def usergroups_users_update(self, *, usergroup: str, users: str | Sequence[str], include_count: bool | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def usergroups_users_update( self, *, usergroup: str, users: Union[str, Sequence[str]], include_count: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Update the list of users for a User Group https://docs.slack.dev/reference/methods/usergroups.users.update """ kwargs.update( { "usergroup": usergroup, "include_count": include_count, "team_id": team_id, } ) if isinstance(users, (list, tuple)): kwargs.update({"users": ",".join(users)}) else: kwargs.update({"users": users}) return self.api_call("usergroups.users.update", params=kwargs) ``` Update the list of users for a User Group [https://docs.slack.dev/reference/methods/usergroups.users.update](https://docs.slack.dev/reference/methods/usergroups.users.update) `def users_conversations(self, *, cursor: str | None = None, exclude_archived: bool | None = None, limit: int | None = None, team_id: str | None = None, types: str | Sequence[str] | None = None, user: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def users_conversations( self, *, cursor: Optional[str] = None, exclude_archived: Optional[bool] = None, limit: Optional[int] = None, team_id: Optional[str] = None, types: Optional[Union[str, Sequence[str]]] = None, user: Optional[str] = None, **kwargs, ) -> SlackResponse: """List conversations the calling user may access. https://docs.slack.dev/reference/methods/users.conversations """ kwargs.update( { "cursor": cursor, "exclude_archived": exclude_archived, "limit": limit, "team_id": team_id, "user": user, } ) if isinstance(types, (list, tuple)): kwargs.update({"types": ",".join(types)}) else: kwargs.update({"types": types}) return self.api_call("users.conversations", http_verb="GET", params=kwargs) ``` List conversations the calling user may access. [https://docs.slack.dev/reference/methods/users.conversations](https://docs.slack.dev/reference/methods/users.conversations) `def users_deletePhoto(self, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def users_deletePhoto( self, **kwargs, ) -> SlackResponse: """Delete the user profile photo https://docs.slack.dev/reference/methods/users.deletePhoto """ return self.api_call("users.deletePhoto", http_verb="GET", params=kwargs) ``` Delete the user profile photo [https://docs.slack.dev/reference/methods/users.deletePhoto](https://docs.slack.dev/reference/methods/users.deletePhoto) `def users_discoverableContacts_lookup(self, email: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def users_discoverableContacts_lookup( self, email: str, **kwargs, ) -> SlackResponse: """Lookup an email address to see if someone is on Slack https://docs.slack.dev/reference/methods/users.discoverableContacts.lookup """ kwargs.update({"email": email}) return self.api_call("users.discoverableContacts.lookup", params=kwargs) ``` Lookup an email address to see if someone is on Slack [https://docs.slack.dev/reference/methods/users.discoverableContacts.lookup](https://docs.slack.dev/reference/methods/users.discoverableContacts.lookup) `def users_getPresence(self, *, user: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def users_getPresence( self, *, user: str, **kwargs, ) -> SlackResponse: """Gets user presence information. https://docs.slack.dev/reference/methods/users.getPresence """ kwargs.update({"user": user}) return self.api_call("users.getPresence", http_verb="GET", params=kwargs) ``` Gets user presence information. [https://docs.slack.dev/reference/methods/users.getPresence](https://docs.slack.dev/reference/methods/users.getPresence) `def users_identity(self, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def users_identity( self, **kwargs, ) -> SlackResponse: """Get a user's identity. https://docs.slack.dev/reference/methods/users.identity """ return self.api_call("users.identity", http_verb="GET", params=kwargs) ``` Get a user's identity. [https://docs.slack.dev/reference/methods/users.identity](https://docs.slack.dev/reference/methods/users.identity) `def users_info(self, *, user: str, include_locale: bool | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def users_info( self, *, user: str, include_locale: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Gets information about a user. https://docs.slack.dev/reference/methods/users.info """ kwargs.update({"user": user, "include_locale": include_locale}) return self.api_call("users.info", http_verb="GET", params=kwargs) ``` Gets information about a user. [https://docs.slack.dev/reference/methods/users.info](https://docs.slack.dev/reference/methods/users.info) `def users_list(self, *, cursor: str | None = None, include_locale: bool | None = None, limit: int | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def users_list( self, *, cursor: Optional[str] = None, include_locale: Optional[bool] = None, limit: Optional[int] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists all users in a Slack team. https://docs.slack.dev/reference/methods/users.list """ kwargs.update( { "cursor": cursor, "include_locale": include_locale, "limit": limit, "team_id": team_id, } ) return self.api_call("users.list", http_verb="GET", params=kwargs) ``` Lists all users in a Slack team. [https://docs.slack.dev/reference/methods/users.list](https://docs.slack.dev/reference/methods/users.list) `def users_lookupByEmail(self, *, email: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def users_lookupByEmail( self, *, email: str, **kwargs, ) -> SlackResponse: """Find a user with an email address. https://docs.slack.dev/reference/methods/users.lookupByEmail """ kwargs.update({"email": email}) return self.api_call("users.lookupByEmail", http_verb="GET", params=kwargs) ``` Find a user with an email address. [https://docs.slack.dev/reference/methods/users.lookupByEmail](https://docs.slack.dev/reference/methods/users.lookupByEmail) `def users_profile_get(self, *, user: str | None = None, include_labels: bool | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def users_profile_get( self, *, user: Optional[str] = None, include_labels: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Retrieves a user's profile information. https://docs.slack.dev/reference/methods/users.profile.get """ kwargs.update({"user": user, "include_labels": include_labels}) return self.api_call("users.profile.get", http_verb="GET", params=kwargs) ``` Retrieves a user's profile information. [https://docs.slack.dev/reference/methods/users.profile.get](https://docs.slack.dev/reference/methods/users.profile.get) `def users_profile_set(self, *, name: str | None = None, value: str | None = None, user: str | None = None, profile: Dict | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def users_profile_set( self, *, name: Optional[str] = None, value: Optional[str] = None, user: Optional[str] = None, profile: Optional[Dict] = None, **kwargs, ) -> SlackResponse: """Set the profile information for a user. https://docs.slack.dev/reference/methods/users.profile.set """ kwargs.update( { "name": name, "profile": profile, "user": user, "value": value, } ) kwargs = _remove_none_values(kwargs) # NOTE: Intentionally using json for the "profile" parameter return self.api_call("users.profile.set", json=kwargs) ``` Set the profile information for a user. [https://docs.slack.dev/reference/methods/users.profile.set](https://docs.slack.dev/reference/methods/users.profile.set) `def users_setPhoto(self, *, image: str | io.IOBase, crop_w: str | int | None = None, crop_x: str | int | None = None, crop_y: str | int | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def users_setPhoto( self, *, image: Union[str, IOBase], crop_w: Optional[Union[int, str]] = None, crop_x: Optional[Union[int, str]] = None, crop_y: Optional[Union[int, str]] = None, **kwargs, ) -> SlackResponse: """Set the user profile photo https://docs.slack.dev/reference/methods/users.setPhoto """ kwargs.update({"crop_w": crop_w, "crop_x": crop_x, "crop_y": crop_y}) return self.api_call("users.setPhoto", files={"image": image}, data=kwargs) ``` Set the user profile photo [https://docs.slack.dev/reference/methods/users.setPhoto](https://docs.slack.dev/reference/methods/users.setPhoto) `def users_setPresence(self, *, presence: str, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def users_setPresence( self, *, presence: str, **kwargs, ) -> SlackResponse: """Manually sets user presence. https://docs.slack.dev/reference/methods/users.setPresence """ kwargs.update({"presence": presence}) return self.api_call("users.setPresence", params=kwargs) ``` Manually sets user presence. [https://docs.slack.dev/reference/methods/users.setPresence](https://docs.slack.dev/reference/methods/users.setPresence) `def views_open(self, *, trigger_id: str | None = None, interactivity_pointer: str | None = None, view: dict | [View](models/views/index.html#slack_sdk.models.views.View "slack_sdk.models.views.View"), **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def views_open( self, *, trigger_id: Optional[str] = None, interactivity_pointer: Optional[str] = None, view: Union[dict, View], **kwargs, ) -> SlackResponse: """Open a view for a user. https://docs.slack.dev/reference/methods/views.open See https://docs.slack.dev/surfaces/modals/ for details. """ kwargs.update({"trigger_id": trigger_id, "interactivity_pointer": interactivity_pointer}) if isinstance(view, View): kwargs.update({"view": view.to_dict()}) else: kwargs.update({"view": view}) kwargs = _remove_none_values(kwargs) # NOTE: Intentionally using json for the "view" parameter return self.api_call("views.open", json=kwargs) ``` Open a view for a user. [https://docs.slack.dev/reference/methods/views.open](https://docs.slack.dev/reference/methods/views.open) See [https://docs.slack.dev/surfaces/modals/](https://docs.slack.dev/surfaces/modals/) for details. `def views_publish(self, *, user_id: str, view: dict | [View](models/views/index.html#slack_sdk.models.views.View "slack_sdk.models.views.View"), hash: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def views_publish( self, *, user_id: str, view: Union[dict, View], hash: Optional[str] = None, **kwargs, ) -> SlackResponse: """Publish a static view for a User. Create or update the view that comprises an app's Home tab (https://docs.slack.dev/surfaces/app-home/) https://docs.slack.dev/reference/methods/views.publish """ kwargs.update({"user_id": user_id, "hash": hash}) if isinstance(view, View): kwargs.update({"view": view.to_dict()}) else: kwargs.update({"view": view}) kwargs = _remove_none_values(kwargs) # NOTE: Intentionally using json for the "view" parameter return self.api_call("views.publish", json=kwargs) ``` Publish a static view for a User. Create or update the view that comprises an app's Home tab ([https://docs.slack.dev/surfaces/app-home/](https://docs.slack.dev/surfaces/app-home/)) [https://docs.slack.dev/reference/methods/views.publish](https://docs.slack.dev/reference/methods/views.publish) `def views_push(self, *, trigger_id: str | None = None, interactivity_pointer: str | None = None, view: dict | [View](models/views/index.html#slack_sdk.models.views.View "slack_sdk.models.views.View"), **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def views_push( self, *, trigger_id: Optional[str] = None, interactivity_pointer: Optional[str] = None, view: Union[dict, View], **kwargs, ) -> SlackResponse: """Push a view onto the stack of a root view. Push a new view onto the existing view stack by passing a view payload and a valid trigger_id generated from an interaction within the existing modal. Read the modals documentation (https://docs.slack.dev/surfaces/modals/) to learn more about the lifecycle and intricacies of views. https://docs.slack.dev/reference/methods/views.push """ kwargs.update({"trigger_id": trigger_id, "interactivity_pointer": interactivity_pointer}) if isinstance(view, View): kwargs.update({"view": view.to_dict()}) else: kwargs.update({"view": view}) kwargs = _remove_none_values(kwargs) # NOTE: Intentionally using json for the "view" parameter return self.api_call("views.push", json=kwargs) ``` Push a view onto the stack of a root view. Push a new view onto the existing view stack by passing a view payload and a valid trigger\_id generated from an interaction within the existing modal. Read the modals documentation ([https://docs.slack.dev/surfaces/modals/](https://docs.slack.dev/surfaces/modals/)) to learn more about the lifecycle and intricacies of views. [https://docs.slack.dev/reference/methods/views.push](https://docs.slack.dev/reference/methods/views.push) `def views_update(self, *, view: dict | [View](models/views/index.html#slack_sdk.models.views.View "slack_sdk.models.views.View"), external_id: str | None = None, view_id: str | None = None, hash: str | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def views_update( self, *, view: Union[dict, View], external_id: Optional[str] = None, view_id: Optional[str] = None, hash: Optional[str] = None, **kwargs, ) -> SlackResponse: """Update an existing view. Update a view by passing a new view definition along with the view_id returned in views.open or the external_id. See the modals documentation (https://docs.slack.dev/surfaces/modals/#updating_views) to learn more about updating views and avoiding race conditions with the hash argument. https://docs.slack.dev/reference/methods/views.update """ if isinstance(view, View): kwargs.update({"view": view.to_dict()}) else: kwargs.update({"view": view}) if external_id: kwargs.update({"external_id": external_id}) elif view_id: kwargs.update({"view_id": view_id}) else: raise e.SlackRequestError("Either view_id or external_id is required.") kwargs.update({"hash": hash}) kwargs = _remove_none_values(kwargs) # NOTE: Intentionally using json for the "view" parameter return self.api_call("views.update", json=kwargs) ``` Update an existing view. Update a view by passing a new view definition along with the view\_id returned in views.open or the external\_id. See the modals documentation ([https://docs.slack.dev/surfaces/modals/#updating\_views](https://docs.slack.dev/surfaces/modals/#updating_views)) to learn more about updating views and avoiding race conditions with the hash argument. [https://docs.slack.dev/reference/methods/views.update](https://docs.slack.dev/reference/methods/views.update) `def workflows_featured_add(self, *, channel_id: str, trigger_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def workflows_featured_add( self, *, channel_id: str, trigger_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Add featured workflows to a channel. https://docs.slack.dev/reference/methods/workflows.featured.add """ kwargs.update({"channel_id": channel_id}) if isinstance(trigger_ids, (list, tuple)): kwargs.update({"trigger_ids": ",".join(trigger_ids)}) else: kwargs.update({"trigger_ids": trigger_ids}) return self.api_call("workflows.featured.add", params=kwargs) ``` Add featured workflows to a channel. [https://docs.slack.dev/reference/methods/workflows.featured.add](https://docs.slack.dev/reference/methods/workflows.featured.add) `def workflows_featured_list(self, *, channel_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def workflows_featured_list( self, *, channel_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """List the featured workflows for specified channels. https://docs.slack.dev/reference/methods/workflows.featured.list """ if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) return self.api_call("workflows.featured.list", params=kwargs) ``` List the featured workflows for specified channels. [https://docs.slack.dev/reference/methods/workflows.featured.list](https://docs.slack.dev/reference/methods/workflows.featured.list) `def workflows_featured_remove(self, *, channel_id: str, trigger_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def workflows_featured_remove( self, *, channel_id: str, trigger_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Remove featured workflows from a channel. https://docs.slack.dev/reference/methods/workflows.featured.remove """ kwargs.update({"channel_id": channel_id}) if isinstance(trigger_ids, (list, tuple)): kwargs.update({"trigger_ids": ",".join(trigger_ids)}) else: kwargs.update({"trigger_ids": trigger_ids}) return self.api_call("workflows.featured.remove", params=kwargs) ``` Remove featured workflows from a channel. [https://docs.slack.dev/reference/methods/workflows.featured.remove](https://docs.slack.dev/reference/methods/workflows.featured.remove) `def workflows_featured_set(self, *, channel_id: str, trigger_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def workflows_featured_set( self, *, channel_id: str, trigger_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Set featured workflows for a channel. https://docs.slack.dev/reference/methods/workflows.featured.set """ kwargs.update({"channel_id": channel_id}) if isinstance(trigger_ids, (list, tuple)): kwargs.update({"trigger_ids": ",".join(trigger_ids)}) else: kwargs.update({"trigger_ids": trigger_ids}) return self.api_call("workflows.featured.set", params=kwargs) ``` Set featured workflows for a channel. [https://docs.slack.dev/reference/methods/workflows.featured.set](https://docs.slack.dev/reference/methods/workflows.featured.set) `def workflows_stepCompleted(self, *, workflow_step_execute_id: str, outputs: dict | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def workflows_stepCompleted( self, *, workflow_step_execute_id: str, outputs: Optional[dict] = None, **kwargs, ) -> SlackResponse: """Indicate a successful outcome of a workflow step's execution. https://docs.slack.dev/reference/methods/workflows.stepCompleted """ kwargs.update({"workflow_step_execute_id": workflow_step_execute_id}) if outputs is not None: kwargs.update({"outputs": outputs}) kwargs = _remove_none_values(kwargs) # NOTE: Intentionally using json for the "outputs" parameter return self.api_call("workflows.stepCompleted", json=kwargs) ``` Indicate a successful outcome of a workflow step's execution. [https://docs.slack.dev/reference/methods/workflows.stepCompleted](https://docs.slack.dev/reference/methods/workflows.stepCompleted) `def workflows_stepFailed(self, *, workflow_step_execute_id: str, error: Dict[str, str], **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def workflows_stepFailed( self, *, workflow_step_execute_id: str, error: Dict[str, str], **kwargs, ) -> SlackResponse: """Indicate an unsuccessful outcome of a workflow step's execution. https://docs.slack.dev/reference/methods/workflows.stepFailed """ kwargs.update( { "workflow_step_execute_id": workflow_step_execute_id, "error": error, } ) kwargs = _remove_none_values(kwargs) # NOTE: Intentionally using json for the "error" parameter return self.api_call("workflows.stepFailed", json=kwargs) ``` Indicate an unsuccessful outcome of a workflow step's execution. [https://docs.slack.dev/reference/methods/workflows.stepFailed](https://docs.slack.dev/reference/methods/workflows.stepFailed) `def workflows_updateStep(self, *, workflow_step_edit_id: str, inputs: Dict[str, Any] | None = None, outputs: List[Dict[str, str]] | None = None, **kwargs) ‑> [SlackResponse](web/slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def workflows_updateStep( self, *, workflow_step_edit_id: str, inputs: Optional[Dict[str, Any]] = None, outputs: Optional[List[Dict[str, str]]] = None, **kwargs, ) -> SlackResponse: """Update the configuration for a workflow extension step. https://docs.slack.dev/reference/methods/workflows.updateStep """ kwargs.update({"workflow_step_edit_id": workflow_step_edit_id}) if inputs is not None: kwargs.update({"inputs": inputs}) if outputs is not None: kwargs.update({"outputs": outputs}) kwargs = _remove_none_values(kwargs) # NOTE: Intentionally using json for the "inputs" / "outputs" parameters return self.api_call("workflows.updateStep", json=kwargs) ``` Update the configuration for a workflow extension step. [https://docs.slack.dev/reference/methods/workflows.updateStep](https://docs.slack.dev/reference/methods/workflows.updateStep) ### Inherited members * `**[BaseClient](web/base_client.html#slack_sdk.web.base_client.BaseClient "slack_sdk.web.base_client.BaseClient")**`: * `[BASE_URL](web/base_client.html#slack_sdk.web.base_client.BaseClient.BASE_URL "slack_sdk.web.base_client.BaseClient.BASE_URL")` * `[api_call](web/base_client.html#slack_sdk.web.base_client.BaseClient.api_call "slack_sdk.web.base_client.BaseClient.api_call")` * `[base_url](web/base_client.html#slack_sdk.web.base_client.BaseClient.base_url "slack_sdk.web.base_client.BaseClient.base_url")` * `[headers](web/base_client.html#slack_sdk.web.base_client.BaseClient.headers "slack_sdk.web.base_client.BaseClient.headers")` * `[logger](web/base_client.html#slack_sdk.web.base_client.BaseClient.logger "slack_sdk.web.base_client.BaseClient.logger")` * `[proxy](web/base_client.html#slack_sdk.web.base_client.BaseClient.proxy "slack_sdk.web.base_client.BaseClient.proxy")` * `[ssl](web/base_client.html#slack_sdk.web.base_client.BaseClient.ssl "slack_sdk.web.base_client.BaseClient.ssl")` * `[timeout](web/base_client.html#slack_sdk.web.base_client.BaseClient.timeout "slack_sdk.web.base_client.BaseClient.timeout")` * `[token](web/base_client.html#slack_sdk.web.base_client.BaseClient.token "slack_sdk.web.base_client.BaseClient.token")` * `[validate_slack_signature](web/base_client.html#slack_sdk.web.base_client.BaseClient.validate_slack_signature "slack_sdk.web.base_client.BaseClient.validate_slack_signature")` `class WebhookClient (url: str, timeout: int = 30, ssl: ssl.SSLContext | None = None, proxy: str | None = None, default_headers: Dict[str, str] | None = None, user_agent_prefix: str | None = None, user_agent_suffix: str | None = None, logger: logging.Logger | None = None, retry_handlers: List[[RetryHandler](http_retry/handler.html#slack_sdk.http_retry.handler.RetryHandler "slack_sdk.http_retry.handler.RetryHandler")] | None = None)` Expand source code ``` class WebhookClient: url: str timeout: int ssl: Optional[SSLContext] proxy: Optional[str] default_headers: Dict[str, str] logger: logging.Logger retry_handlers: List[RetryHandler] def __init__( self, url: str, timeout: int = 30, ssl: Optional[SSLContext] = None, proxy: Optional[str] = None, default_headers: Optional[Dict[str, str]] = None, user_agent_prefix: Optional[str] = None, user_agent_suffix: Optional[str] = None, logger: Optional[logging.Logger] = None, retry_handlers: Optional[List[RetryHandler]] = None, ): """API client for Incoming Webhooks and `response_url` https://docs.slack.dev/messaging/sending-messages-using-incoming-webhooks/ Args: url: Complete URL to send data (e.g., `https://hooks.slack.com/XXX`) timeout: Request timeout (in seconds) ssl: `ssl.SSLContext` to use for requests proxy: Proxy URL (e.g., `localhost:9000`, `http://localhost:9000`) default_headers: Request headers to add to all requests user_agent_prefix: Prefix for User-Agent header value user_agent_suffix: Suffix for User-Agent header value logger: Custom logger retry_handlers: Retry handlers """ self.url = url self.timeout = timeout self.ssl = ssl self.proxy = proxy self.default_headers = default_headers if default_headers else {} self.default_headers["User-Agent"] = get_user_agent(user_agent_prefix, user_agent_suffix) self.logger = logger if logger is not None else logging.getLogger(__name__) self.retry_handlers = retry_handlers if retry_handlers is not None else default_retry_handlers() if self.proxy is None or len(self.proxy.strip()) == 0: env_variable = load_http_proxy_from_env(self.logger) if env_variable is not None: self.proxy = env_variable def send( self, *, text: Optional[str] = None, attachments: Optional[Sequence[Union[Dict[str, Any], Attachment]]] = None, blocks: Optional[Sequence[Union[Dict[str, Any], Block]]] = None, 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, metadata: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None, ) -> WebhookResponse: """Performs a Slack API request and returns the result. Args: text: The text message (even when having blocks, setting this as well is recommended as it works as fallback) attachments: A collection of attachments blocks: A collection of Block Kit UI components 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 metadata: Metadata attached to the message headers: Request headers to append only for this request Returns: Webhook response """ return self.send_dict( # It's fine to have None value elements here # because _build_body() filters them out when constructing the actual body data body={ "text": text, "attachments": attachments, "blocks": blocks, "response_type": response_type, "replace_original": replace_original, "delete_original": delete_original, "unfurl_links": unfurl_links, "unfurl_media": unfurl_media, "metadata": metadata, }, headers=headers, ) def send_dict(self, body: Dict[str, Any], headers: Optional[Dict[str, str]] = None) -> WebhookResponse: """Performs a Slack API request and returns the result. Args: body: JSON data structure (it's still a dict at this point), if you give this argument, body_params and files will be skipped headers: Request headers to append only for this request Returns: Webhook response """ return self._perform_http_request( body=_build_body(body), # type: ignore[arg-type] headers=_build_request_headers(self.default_headers, headers), ) def _perform_http_request(self, *, body: Dict[str, Any], headers: Dict[str, str]) -> WebhookResponse: raw_body = json.dumps(body) headers["Content-Type"] = "application/json;charset=utf-8" if self.logger.level <= logging.DEBUG: self.logger.debug(f"Sending a request - url: {self.url}, body: {raw_body}, headers: {headers}") url = self.url # NOTE: Intentionally ignore the `http_verb` here # Slack APIs accepts any API method requests with POST methods req = Request(method="POST", url=url, data=raw_body.encode("utf-8"), headers=headers) resp = None last_error = Exception("undefined internal error") retry_state = RetryState() counter_for_safety = 0 while counter_for_safety < 100: counter_for_safety += 1 # If this is a retry, the next try started here. We can reset the flag. retry_state.next_attempt_requested = False try: resp = self._perform_http_request_internal(url, req) # The resp is a 200 OK response return resp except HTTPError as e: # read the response body here charset = e.headers.get_content_charset() or "utf-8" response_body: str = e.read().decode(charset) # As adding new values to HTTPError#headers can be ignored, building a new dict object here response_headers = dict(e.headers.items()) resp = WebhookResponse( url=url, status_code=e.code, body=response_body, headers=response_headers, ) if e.code == 429: # for backward-compatibility with WebClient (v.2.5.0 or older) if "retry-after" not in resp.headers and "Retry-After" in resp.headers: resp.headers["retry-after"] = resp.headers["Retry-After"] if "Retry-After" not in resp.headers and "retry-after" in resp.headers: resp.headers["Retry-After"] = resp.headers["retry-after"] _debug_log_response(self.logger, resp) # Try to find a retry handler for this error retry_request = RetryHttpRequest.from_urllib_http_request(req) retry_response = RetryHttpResponse( status_code=e.code, headers={k: [v] for k, v in e.headers.items()}, data=response_body.encode("utf-8") if response_body is not None else None, ) for handler in self.retry_handlers: if handler.can_retry( state=retry_state, request=retry_request, response=retry_response, error=e, ): if self.logger.level <= logging.DEBUG: self.logger.info( f"A retry handler found: {type(handler).__name__} for {req.method} {req.full_url} - {e}" ) handler.prepare_for_next_attempt( state=retry_state, request=retry_request, response=retry_response, error=e, ) break if retry_state.next_attempt_requested is False: return resp except Exception as err: last_error = err self.logger.error(f"Failed to send a request to Slack API server: {err}") # Try to find a retry handler for this error retry_request = RetryHttpRequest.from_urllib_http_request(req) for handler in self.retry_handlers: if handler.can_retry( state=retry_state, request=retry_request, response=None, error=err, ): if self.logger.level <= logging.DEBUG: self.logger.info( f"A retry handler found: {type(handler).__name__} for {req.method} {req.full_url} - {err}" ) handler.prepare_for_next_attempt( state=retry_state, request=retry_request, response=None, error=err, ) self.logger.info(f"Going to retry the same request: {req.method} {req.full_url}") break if retry_state.next_attempt_requested is False: raise err if resp is not None: return resp raise last_error def _perform_http_request_internal(self, url: str, req: Request): opener: Optional[OpenerDirector] = None # for security (BAN-B310) if url.lower().startswith("http"): if self.proxy is not None: if isinstance(self.proxy, str): opener = urllib.request.build_opener( ProxyHandler({"http": self.proxy, "https": self.proxy}), HTTPSHandler(context=self.ssl), ) else: raise SlackRequestError(f"Invalid proxy detected: {self.proxy} must be a str value") else: raise SlackRequestError(f"Invalid URL detected: {url}") http_resp: Optional[HTTPResponse] = None if opener: http_resp = opener.open(req, timeout=self.timeout) else: http_resp = urlopen(req, context=self.ssl, timeout=self.timeout) charset: str = http_resp.headers.get_content_charset() or "utf-8" response_body: str = http_resp.read().decode(charset) resp = WebhookResponse( url=url, status_code=http_resp.status, body=response_body, headers=http_resp.headers, # type: ignore[arg-type] ) _debug_log_response(self.logger, resp) return resp ``` API client for Incoming Webhooks and `response_url` [https://docs.slack.dev/messaging/sending-messages-using-incoming-webhooks/](https://docs.slack.dev/messaging/sending-messages-using-incoming-webhooks/) ## Args **`url`** Complete URL to send data (e.g., `https://hooks.slack.com/XXX`) **`timeout`** Request timeout (in seconds) **`ssl`** `ssl.SSLContext` to use for requests **`proxy`** Proxy URL (e.g., `localhost:9000`, `http://localhost:9000`) **`default_headers`** Request headers to add to all requests **`user_agent_prefix`** Prefix for User-Agent header value **`user_agent_suffix`** Suffix for User-Agent header value **`logger`** Custom logger **`retry_handlers`** Retry handlers ### Class variables `var default_headers : Dict[str, str]` The type of the None singleton. `var logger : logging.Logger` The type of the None singleton. `var proxy : str | None` The type of the None singleton. `var retry_handlers : List[[RetryHandler](http_retry/handler.html#slack_sdk.http_retry.handler.RetryHandler "slack_sdk.http_retry.handler.RetryHandler")]` The type of the None singleton. `var ssl : ssl.SSLContext | None` The type of the None singleton. `var timeout : int` The type of the None singleton. `var url : str` The type of the None singleton. ### Methods `def send(self, *, text: str | None = None, attachments: Sequence[Dict[str, Any] | [Attachment](models/attachments/index.html#slack_sdk.models.attachments.Attachment "slack_sdk.models.attachments.Attachment")] | None = None, blocks: Sequence[Dict[str, Any] | [Block](models/blocks/blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")] | None = None, response_type: str | None = None, replace_original: bool | None = None, delete_original: bool | None = None, unfurl_links: bool | None = None, unfurl_media: bool | None = None, metadata: Dict[str, Any] | None = None, headers: Dict[str, str] | None = None) ‑> [WebhookResponse](webhook/webhook_response.html#slack_sdk.webhook.webhook_response.WebhookResponse "slack_sdk.webhook.webhook_response.WebhookResponse")` Expand source code ``` def send( self, *, text: Optional[str] = None, attachments: Optional[Sequence[Union[Dict[str, Any], Attachment]]] = None, blocks: Optional[Sequence[Union[Dict[str, Any], Block]]] = None, 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, metadata: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None, ) -> WebhookResponse: """Performs a Slack API request and returns the result. Args: text: The text message (even when having blocks, setting this as well is recommended as it works as fallback) attachments: A collection of attachments blocks: A collection of Block Kit UI components 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 metadata: Metadata attached to the message headers: Request headers to append only for this request Returns: Webhook response """ return self.send_dict( # It's fine to have None value elements here # because _build_body() filters them out when constructing the actual body data body={ "text": text, "attachments": attachments, "blocks": blocks, "response_type": response_type, "replace_original": replace_original, "delete_original": delete_original, "unfurl_links": unfurl_links, "unfurl_media": unfurl_media, "metadata": metadata, }, headers=headers, ) ``` Performs a Slack API request and returns the result. ## Args **`text`** The text message (even when having blocks, setting this as well is recommended as it works as fallback) **`attachments`** A collection of attachments **`blocks`** A collection of Block Kit UI components **`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 **`metadata`** Metadata attached to the message **`headers`** Request headers to append only for this request ## Returns Webhook response `def send_dict(self, body: Dict[str, Any], headers: Dict[str, str] | None = None) ‑> [WebhookResponse](webhook/webhook_response.html#slack_sdk.webhook.webhook_response.WebhookResponse "slack_sdk.webhook.webhook_response.WebhookResponse")` Expand source code ``` def send_dict(self, body: Dict[str, Any], headers: Optional[Dict[str, str]] = None) -> WebhookResponse: """Performs a Slack API request and returns the result. Args: body: JSON data structure (it's still a dict at this point), if you give this argument, body_params and files will be skipped headers: Request headers to append only for this request Returns: Webhook response """ return self._perform_http_request( body=_build_body(body), # type: ignore[arg-type] headers=_build_request_headers(self.default_headers, headers), ) ``` Performs a Slack API request and returns the result. ## Args **`body`** JSON data structure (it's still a dict at this point), if you give this argument, body\_params and files will be skipped **`headers`** Request headers to append only for this request ## Returns Webhook response --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/audit_logs # Module slack_sdk.audit_logs Audit Logs API is a set of APIs for monitoring what’s happening in your Enterprise Grid organization. Refer to [https://docs.slack.dev/tools/python-slack-sdk/audit-logs](https://docs.slack.dev/tools/python-slack-sdk/audit-logs) for details. ## Sub-modules `[slack_sdk.audit_logs.async_client](async_client.html "slack_sdk.audit_logs.async_client")` `[slack_sdk.audit_logs.v1](v1/index.html "slack_sdk.audit_logs.v1")` Audit Logs API is a set of APIs for monitoring what’s happening in your Enterprise Grid organization … ## Classes `class AuditLogsClient (token: str, timeout: int = 30, ssl: ssl.SSLContext | None = None, proxy: str | None = None, base_url: str = 'https://api.slack.com/audit/v1/', default_headers: Dict[str, str] | None = None, user_agent_prefix: str | None = None, user_agent_suffix: str | None = None, logger: logging.Logger | None = None, retry_handlers: List[[RetryHandler](../http_retry/handler.html#slack_sdk.http_retry.handler.RetryHandler "slack_sdk.http_retry.handler.RetryHandler")] | None = None)` Expand source code ``` class AuditLogsClient: BASE_URL = "https://api.slack.com/audit/v1/" token: str timeout: int ssl: Optional[SSLContext] proxy: Optional[str] base_url: str default_headers: Dict[str, str] logger: logging.Logger retry_handlers: List[RetryHandler] def __init__( self, token: str, timeout: int = 30, ssl: Optional[SSLContext] = None, proxy: Optional[str] = None, base_url: str = BASE_URL, default_headers: Optional[Dict[str, str]] = None, user_agent_prefix: Optional[str] = None, user_agent_suffix: Optional[str] = None, logger: Optional[logging.Logger] = None, retry_handlers: Optional[List[RetryHandler]] = None, ): """API client for Audit Logs API See https://docs.slack.dev/admins/audit-logs-api/ for more details Args: token: An admin user's token, which starts with `xoxp-` timeout: Request timeout (in seconds) ssl: `ssl.SSLContext` to use for requests proxy: Proxy URL (e.g., `localhost:9000`, `http://localhost:9000`) base_url: The base URL for API calls default_headers: Request headers to add to all requests user_agent_prefix: Prefix for User-Agent header value user_agent_suffix: Suffix for User-Agent header value logger: Custom logger retry_handlers: Retry handlers """ self.token = token self.timeout = timeout self.ssl = ssl self.proxy = proxy self.base_url = base_url self.default_headers = default_headers if default_headers else {} self.default_headers["User-Agent"] = get_user_agent(user_agent_prefix, user_agent_suffix) self.logger = logger if logger is not None else logging.getLogger(__name__) self.retry_handlers = retry_handlers if retry_handlers is not None else default_retry_handlers() if self.proxy is None or len(self.proxy.strip()) == 0: env_variable = load_http_proxy_from_env(self.logger) if env_variable is not None: self.proxy = env_variable def schemas( self, *, query_params: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None, ) -> AuditLogsResponse: """Returns information about the kind of objects which the Audit Logs API returns as a list of all objects and a short description. Authentication not required. Args: query_params: Set any values if you want to add query params headers: Additional request headers Returns: API response """ return self.api_call( path="schemas", query_params=query_params, headers=headers, ) def actions( self, *, query_params: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None, ) -> AuditLogsResponse: """Returns information about the kind of actions that the Audit Logs API returns as a list of all actions and a short description of each. Authentication not required. Args: query_params: Set any values if you want to add query params headers: Additional request headers Returns: API response """ return self.api_call( path="actions", query_params=query_params, headers=headers, ) def logs( self, *, latest: Optional[int] = None, oldest: Optional[int] = None, limit: Optional[int] = None, action: Optional[str] = None, actor: Optional[str] = None, entity: Optional[str] = None, cursor: Optional[str] = None, additional_query_params: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None, ) -> AuditLogsResponse: """This is the primary endpoint for retrieving actual audit events from your organization. It will return a list of actions that have occurred on the installed workspace or grid organization. Authentication required. The following filters can be applied in order to narrow the range of actions returned. Filters are added as query string parameters and can be combined together. Multiple filter parameters are additive (a boolean AND) and are separated with an ampersand (&) in the query string. Filtering is entirely optional. Args: latest: Unix timestamp of the most recent audit event to include (inclusive). oldest: Unix timestamp of the least recent audit event to include (inclusive). Data is not available prior to March 2018. limit: Number of results to optimistically return, maximum 9999. action: Name of the action. actor: User ID who initiated the action. entity: ID of the target entity of the action (such as a channel, workspace, organization, file). cursor: The next page cursor of pagination additional_query_params: Add anything else if you need to use the ones this library does not support headers: Additional request headers Returns: API response """ query_params = { "latest": latest, "oldest": oldest, "limit": limit, "action": action, "actor": actor, "entity": entity, "cursor": cursor, } if additional_query_params is not None: query_params.update(additional_query_params) query_params = {k: v for k, v in query_params.items() if v is not None} return self.api_call( path="logs", query_params=query_params, headers=headers, ) def api_call( self, *, http_verb: str = "GET", path: str, query_params: Optional[Dict[str, Any]] = None, body_params: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None, ) -> AuditLogsResponse: """Performs a Slack API request and returns the result.""" url = f"{self.base_url}{path}" query = _build_query(query_params) if len(query) > 0: url += f"?{query}" return self._perform_http_request( http_verb=http_verb, url=url, body=body_params, headers=_build_request_headers( token=self.token, default_headers=self.default_headers, additional_headers=headers, ), ) def _perform_http_request( self, *, http_verb: str = "GET", url: str, body: Optional[Dict[str, Any]] = None, headers: Dict[str, str], ) -> AuditLogsResponse: if body is not None: body = json.dumps(body) # type: ignore[assignment] headers["Content-Type"] = "application/json;charset=utf-8" if self.logger.level <= logging.DEBUG: headers_for_logging = {k: "(redacted)" if k.lower() == "authorization" else v for k, v in headers.items()} self.logger.debug(f"Sending a request - url: {url}, body: {body}, headers: {headers_for_logging}") # NOTE: Intentionally ignore the `http_verb` here # Slack APIs accepts any API method requests with POST methods req = Request( method=http_verb, url=url, data=body.encode("utf-8") if body is not None else None, # type: ignore[attr-defined] headers=headers, ) resp = None last_error = None retry_state = RetryState() counter_for_safety = 0 while counter_for_safety < 100: counter_for_safety += 1 # If this is a retry, the next try started here. We can reset the flag. retry_state.next_attempt_requested = False try: resp = self._perform_http_request_internal(url, req) # The resp is a 200 OK response return resp except HTTPError as e: # read the response body here charset = e.headers.get_content_charset() or "utf-8" response_body: str = e.read().decode(charset) # As adding new values to HTTPError#headers can be ignored, building a new dict object here response_headers = dict(e.headers.items()) resp = AuditLogsResponse( url=url, status_code=e.code, raw_body=response_body, headers=response_headers, ) if e.code == 429: # for backward-compatibility with WebClient (v.2.5.0 or older) if "retry-after" not in resp.headers and "Retry-After" in resp.headers: resp.headers["retry-after"] = resp.headers["Retry-After"] if "Retry-After" not in resp.headers and "retry-after" in resp.headers: resp.headers["Retry-After"] = resp.headers["retry-after"] _debug_log_response(self.logger, resp) # Try to find a retry handler for this error retry_request = RetryHttpRequest.from_urllib_http_request(req) retry_response = RetryHttpResponse( status_code=e.code, headers={k: [v] for k, v in e.headers.items()}, data=response_body.encode("utf-8") if response_body is not None else None, ) for handler in self.retry_handlers: if handler.can_retry( state=retry_state, request=retry_request, response=retry_response, error=e, ): if self.logger.level <= logging.DEBUG: self.logger.info( f"A retry handler found: {type(handler).__name__} for {req.method} {req.full_url} - {e}" ) handler.prepare_for_next_attempt( state=retry_state, request=retry_request, response=retry_response, error=e, ) break if retry_state.next_attempt_requested is False: return resp except Exception as err: last_error = err self.logger.error(f"Failed to send a request to Slack API server: {err}") # Try to find a retry handler for this error retry_request = RetryHttpRequest.from_urllib_http_request(req) for handler in self.retry_handlers: if handler.can_retry( state=retry_state, request=retry_request, response=None, error=err, ): if self.logger.level <= logging.DEBUG: self.logger.info( f"A retry handler found: {type(handler).__name__} for {req.method} {req.full_url} - {err}" ) handler.prepare_for_next_attempt( state=retry_state, request=retry_request, response=None, error=err, ) self.logger.info(f"Going to retry the same request: {req.method} {req.full_url}") break if retry_state.next_attempt_requested is False: raise err if resp is not None: return resp raise last_error # type: ignore[misc] def _perform_http_request_internal(self, url: str, req: Request) -> AuditLogsResponse: opener: Optional[OpenerDirector] = None # for security (BAN-B310) if url.lower().startswith("http"): if self.proxy is not None: if isinstance(self.proxy, str): opener = urllib.request.build_opener( ProxyHandler({"http": self.proxy, "https": self.proxy}), HTTPSHandler(context=self.ssl), ) else: raise SlackRequestError(f"Invalid proxy detected: {self.proxy} must be a str value") else: raise SlackRequestError(f"Invalid URL detected: {url}") http_resp: HTTPResponse if opener: http_resp = opener.open(req, timeout=self.timeout) else: http_resp = urlopen(req, context=self.ssl, timeout=self.timeout) charset: str = http_resp.headers.get_content_charset() or "utf-8" response_body: str = http_resp.read().decode(charset) resp = AuditLogsResponse( url=url, status_code=http_resp.status, raw_body=response_body, headers=http_resp.headers, # type: ignore[arg-type] ) _debug_log_response(self.logger, resp) return resp ``` API client for Audit Logs API See [https://docs.slack.dev/admins/audit-logs-api/](https://docs.slack.dev/admins/audit-logs-api/) for more details ## Args **`token`** An admin user's token, which starts with `xoxp-` **`timeout`** Request timeout (in seconds) **`ssl`** `ssl.SSLContext` to use for requests **`proxy`** Proxy URL (e.g., `localhost:9000`, `http://localhost:9000`) **`base_url`** The base URL for API calls **`default_headers`** Request headers to add to all requests **`user_agent_prefix`** Prefix for User-Agent header value **`user_agent_suffix`** Suffix for User-Agent header value **`logger`** Custom logger **`retry_handlers`** Retry handlers ### Class variables `var BASE_URL` The type of the None singleton. `var base_url : str` The type of the None singleton. `var default_headers : Dict[str, str]` The type of the None singleton. `var logger : logging.Logger` The type of the None singleton. `var proxy : str | None` The type of the None singleton. `var retry_handlers : List[[RetryHandler](../http_retry/handler.html#slack_sdk.http_retry.handler.RetryHandler "slack_sdk.http_retry.handler.RetryHandler")]` The type of the None singleton. `var ssl : ssl.SSLContext | None` The type of the None singleton. `var timeout : int` The type of the None singleton. `var token : str` The type of the None singleton. ### Methods `def actions(self, *, query_params: Dict[str, Any] | None = None, headers: Dict[str, str] | None = None) ‑> [AuditLogsResponse](v1/response.html#slack_sdk.audit_logs.v1.response.AuditLogsResponse "slack_sdk.audit_logs.v1.response.AuditLogsResponse")` Expand source code ``` def actions( self, *, query_params: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None, ) -> AuditLogsResponse: """Returns information about the kind of actions that the Audit Logs API returns as a list of all actions and a short description of each. Authentication not required. Args: query_params: Set any values if you want to add query params headers: Additional request headers Returns: API response """ return self.api_call( path="actions", query_params=query_params, headers=headers, ) ``` Returns information about the kind of actions that the Audit Logs API returns as a list of all actions and a short description of each. Authentication not required. ## Args **`query_params`** Set any values if you want to add query params **`headers`** Additional request headers ## Returns API response `def api_call(self, *, http_verb: str = 'GET', path: str, query_params: Dict[str, Any] | None = None, body_params: Dict[str, Any] | None = None, headers: Dict[str, str] | None = None) ‑> [AuditLogsResponse](v1/response.html#slack_sdk.audit_logs.v1.response.AuditLogsResponse "slack_sdk.audit_logs.v1.response.AuditLogsResponse")` Expand source code ``` def api_call( self, *, http_verb: str = "GET", path: str, query_params: Optional[Dict[str, Any]] = None, body_params: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None, ) -> AuditLogsResponse: """Performs a Slack API request and returns the result.""" url = f"{self.base_url}{path}" query = _build_query(query_params) if len(query) > 0: url += f"?{query}" return self._perform_http_request( http_verb=http_verb, url=url, body=body_params, headers=_build_request_headers( token=self.token, default_headers=self.default_headers, additional_headers=headers, ), ) ``` Performs a Slack API request and returns the result. `def logs(self, *, latest: int | None = None, oldest: int | None = None, limit: int | None = None, action: str | None = None, actor: str | None = None, entity: str | None = None, cursor: str | None = None, additional_query_params: Dict[str, Any] | None = None, headers: Dict[str, str] | None = None) ‑> [AuditLogsResponse](v1/response.html#slack_sdk.audit_logs.v1.response.AuditLogsResponse "slack_sdk.audit_logs.v1.response.AuditLogsResponse")` Expand source code ``` def logs( self, *, latest: Optional[int] = None, oldest: Optional[int] = None, limit: Optional[int] = None, action: Optional[str] = None, actor: Optional[str] = None, entity: Optional[str] = None, cursor: Optional[str] = None, additional_query_params: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None, ) -> AuditLogsResponse: """This is the primary endpoint for retrieving actual audit events from your organization. It will return a list of actions that have occurred on the installed workspace or grid organization. Authentication required. The following filters can be applied in order to narrow the range of actions returned. Filters are added as query string parameters and can be combined together. Multiple filter parameters are additive (a boolean AND) and are separated with an ampersand (&) in the query string. Filtering is entirely optional. Args: latest: Unix timestamp of the most recent audit event to include (inclusive). oldest: Unix timestamp of the least recent audit event to include (inclusive). Data is not available prior to March 2018. limit: Number of results to optimistically return, maximum 9999. action: Name of the action. actor: User ID who initiated the action. entity: ID of the target entity of the action (such as a channel, workspace, organization, file). cursor: The next page cursor of pagination additional_query_params: Add anything else if you need to use the ones this library does not support headers: Additional request headers Returns: API response """ query_params = { "latest": latest, "oldest": oldest, "limit": limit, "action": action, "actor": actor, "entity": entity, "cursor": cursor, } if additional_query_params is not None: query_params.update(additional_query_params) query_params = {k: v for k, v in query_params.items() if v is not None} return self.api_call( path="logs", query_params=query_params, headers=headers, ) ``` This is the primary endpoint for retrieving actual audit events from your organization. It will return a list of actions that have occurred on the installed workspace or grid organization. Authentication required. The following filters can be applied in order to narrow the range of actions returned. Filters are added as query string parameters and can be combined together. Multiple filter parameters are additive (a boolean AND) and are separated with an ampersand (&) in the query string. Filtering is entirely optional. ## Args **`latest`** Unix timestamp of the most recent audit event to include (inclusive). **`oldest`** Unix timestamp of the least recent audit event to include (inclusive). Data is not available prior to March 2018. **`limit`** Number of results to optimistically return, maximum 9999. **`action`** Name of the action. **`actor`** User ID who initiated the action. **`entity`** ID of the target entity of the action (such as a channel, workspace, organization, file). **`cursor`** The next page cursor of pagination **`additional_query_params`** Add anything else if you need to use the ones this library does not support **`headers`** Additional request headers ## Returns API response `def schemas(self, *, query_params: Dict[str, Any] | None = None, headers: Dict[str, str] | None = None) ‑> [AuditLogsResponse](v1/response.html#slack_sdk.audit_logs.v1.response.AuditLogsResponse "slack_sdk.audit_logs.v1.response.AuditLogsResponse")` Expand source code ``` def schemas( self, *, query_params: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None, ) -> AuditLogsResponse: """Returns information about the kind of objects which the Audit Logs API returns as a list of all objects and a short description. Authentication not required. Args: query_params: Set any values if you want to add query params headers: Additional request headers Returns: API response """ return self.api_call( path="schemas", query_params=query_params, headers=headers, ) ``` Returns information about the kind of objects which the Audit Logs API returns as a list of all objects and a short description. Authentication not required. ## Args **`query_params`** Set any values if you want to add query params **`headers`** Additional request headers ## Returns API response `class AuditLogsResponse (*, url: str, status_code: int, raw_body: str | None, headers: dict)` Expand source code ``` class AuditLogsResponse: url: str status_code: int headers: Dict[str, Any] raw_body: Optional[str] body: Optional[Dict[str, Any]] typed_body: Optional[LogsResponse] @property # type: ignore[no-redef] def typed_body(self) -> Optional[LogsResponse]: if self.body is None: return None return LogsResponse(**self.body) def __init__( self, *, url: str, status_code: int, raw_body: Optional[str], headers: dict, ): self.url = url self.status_code = status_code self.headers = headers self.raw_body = raw_body self.body = json.loads(raw_body) if raw_body is not None and raw_body.startswith("{") else None ``` ### Class variables `var body : Dict[str, Any] | None` The type of the None singleton. `var headers : Dict[str, Any]` The type of the None singleton. `var raw_body : str | None` The type of the None singleton. `var status_code : int` The type of the None singleton. `var url : str` The type of the None singleton. ### Instance variables `prop typed_body : [LogsResponse](v1/logs.html#slack_sdk.audit_logs.v1.logs.LogsResponse "slack_sdk.audit_logs.v1.logs.LogsResponse") | None` Expand source code ``` @property # type: ignore[no-redef] def typed_body(self) -> Optional[LogsResponse]: if self.body is None: return None return LogsResponse(**self.body) ``` --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/audit_logs/v1 # Module slack_sdk.audit_logs.v1 Audit Logs API is a set of APIs for monitoring what’s happening in your Enterprise Grid organization. Refer to [https://docs.slack.dev/tools/python-slack-sdk/audit-logs](https://docs.slack.dev/tools/python-slack-sdk/audit-logs) for details. ## Sub-modules `[slack_sdk.audit_logs.v1.async_client](async_client.html "slack_sdk.audit_logs.v1.async_client")` Audit Logs API is a set of APIs for monitoring what’s happening in your Enterprise Grid organization … `[slack_sdk.audit_logs.v1.client](client.html "slack_sdk.audit_logs.v1.client")` Audit Logs API is a set of APIs for monitoring what’s happening in your Enterprise Grid organization … `[slack_sdk.audit_logs.v1.internal_utils](internal_utils.html "slack_sdk.audit_logs.v1.internal_utils")` `[slack_sdk.audit_logs.v1.logs](logs.html "slack_sdk.audit_logs.v1.logs")` `[slack_sdk.audit_logs.v1.response](response.html "slack_sdk.audit_logs.v1.response")` --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/errors # Module slack_sdk.errors Errors that can be raised by this SDK ## Classes `class BotUserAccessError (*args, **kwargs)` Expand source code ``` class BotUserAccessError(SlackClientError): """Error raised when an 'xoxb-*' token is being used for a Slack API method that only accepts 'xoxp-*' tokens. """ ``` Error raised when an 'xoxb-_' token is being used for a Slack API method that only accepts 'xoxp-_' tokens. ### Ancestors * [SlackClientError](#slack_sdk.errors.SlackClientError "slack_sdk.errors.SlackClientError") * builtins.Exception * builtins.BaseException `class SlackApiError (message, response)` Expand source code ``` class SlackApiError(SlackClientError): """Error raised when Slack does not send the expected response. Attributes: response (SlackResponse): The SlackResponse object containing all of the data sent back from the API. Note: The message (str) passed into the exception is used when a user converts the exception to a str. i.e. str(SlackApiError("This text will be sent as a string.")) """ def __init__(self, message, response): msg = f"{message}\nThe server responded with: {response}" self.response = response super(SlackApiError, self).__init__(msg) ``` Error raised when Slack does not send the expected response. ## Attributes **`response`** : `SlackResponse` The SlackResponse object containing all of the data sent back from the API. ## Note The message (str) passed into the exception is used when a user converts the exception to a str. i.e. str(SlackApiError("This text will be sent as a string.")) ### Ancestors * [SlackClientError](#slack_sdk.errors.SlackClientError "slack_sdk.errors.SlackClientError") * builtins.Exception * builtins.BaseException `class SlackClientConfigurationError (*args, **kwargs)` Expand source code ``` class SlackClientConfigurationError(SlackClientError): """Error raised because of invalid configuration on the client side: * when attempting to send messages over the websocket when the connection is closed. * when external system (e.g., Amazon S3) configuration / credentials are not correct """ ``` Error raised because of invalid configuration on the client side: \* when attempting to send messages over the websocket when the connection is closed. \* when external system (e.g., Amazon S3) configuration / credentials are not correct ### Ancestors * [SlackClientError](#slack_sdk.errors.SlackClientError "slack_sdk.errors.SlackClientError") * builtins.Exception * builtins.BaseException `class SlackClientError (*args, **kwargs)` Expand source code ``` class SlackClientError(Exception): """Base class for Client errors""" ``` Base class for Client errors ### Ancestors * builtins.Exception * builtins.BaseException ### Subclasses * [BotUserAccessError](#slack_sdk.errors.BotUserAccessError "slack_sdk.errors.BotUserAccessError") * [SlackApiError](#slack_sdk.errors.SlackApiError "slack_sdk.errors.SlackApiError") * [SlackClientConfigurationError](#slack_sdk.errors.SlackClientConfigurationError "slack_sdk.errors.SlackClientConfigurationError") * [SlackClientNotConnectedError](#slack_sdk.errors.SlackClientNotConnectedError "slack_sdk.errors.SlackClientNotConnectedError") * [SlackObjectFormationError](#slack_sdk.errors.SlackObjectFormationError "slack_sdk.errors.SlackObjectFormationError") * [SlackRequestError](#slack_sdk.errors.SlackRequestError "slack_sdk.errors.SlackRequestError") * [SlackTokenRotationError](#slack_sdk.errors.SlackTokenRotationError "slack_sdk.errors.SlackTokenRotationError") `class SlackClientNotConnectedError (*args, **kwargs)` Expand source code ``` class SlackClientNotConnectedError(SlackClientError): """Error raised when attempting to send messages over the websocket when the connection is closed.""" ``` Error raised when attempting to send messages over the websocket when the connection is closed. ### Ancestors * [SlackClientError](#slack_sdk.errors.SlackClientError "slack_sdk.errors.SlackClientError") * builtins.Exception * builtins.BaseException `class SlackObjectFormationError (*args, **kwargs)` Expand source code ``` class SlackObjectFormationError(SlackClientError): """Error raised when a constructed object is not valid/malformed""" ``` Error raised when a constructed object is not valid/malformed ### Ancestors * [SlackClientError](#slack_sdk.errors.SlackClientError "slack_sdk.errors.SlackClientError") * builtins.Exception * builtins.BaseException `class SlackRequestError (*args, **kwargs)` Expand source code ``` class SlackRequestError(SlackClientError): """Error raised when there's a problem with the request that's being submitted.""" ``` Error raised when there's a problem with the request that's being submitted. ### Ancestors * [SlackClientError](#slack_sdk.errors.SlackClientError "slack_sdk.errors.SlackClientError") * builtins.Exception * builtins.BaseException `class SlackTokenRotationError (api_error: [SlackApiError](#slack_sdk.errors.SlackApiError "slack_sdk.errors.SlackApiError"))` Expand source code ``` class SlackTokenRotationError(SlackClientError): """Error raised when the oauth.v2.access call for token rotation fails""" api_error: SlackApiError def __init__(self, api_error: SlackApiError): self.api_error = api_error ``` Error raised when the oauth.v2.access call for token rotation fails ### Ancestors * [SlackClientError](#slack_sdk.errors.SlackClientError "slack_sdk.errors.SlackClientError") * builtins.Exception * builtins.BaseException ### Class variables `var api_error : [SlackApiError](#slack_sdk.errors.SlackApiError "slack_sdk.errors.SlackApiError")` The type of the None singleton. --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/http_retry # Module slack_sdk.http_retry ## Sub-modules `[slack_sdk.http_retry.async_handler](async_handler.html "slack_sdk.http_retry.async_handler")` asyncio compatible RetryHandler interface. You can pass an array of handlers to customize retry logics in supported API clients. `[slack_sdk.http_retry.builtin_async_handlers](builtin_async_handlers.html "slack_sdk.http_retry.builtin_async_handlers")` `[slack_sdk.http_retry.builtin_handlers](builtin_handlers.html "slack_sdk.http_retry.builtin_handlers")` `[slack_sdk.http_retry.builtin_interval_calculators](builtin_interval_calculators.html "slack_sdk.http_retry.builtin_interval_calculators")` `[slack_sdk.http_retry.handler](handler.html "slack_sdk.http_retry.handler")` RetryHandler interface. You can pass an array of handlers to customize retry logics in supported API clients. `[slack_sdk.http_retry.interval_calculator](interval_calculator.html "slack_sdk.http_retry.interval_calculator")` `[slack_sdk.http_retry.jitter](jitter.html "slack_sdk.http_retry.jitter")` `[slack_sdk.http_retry.request](request.html "slack_sdk.http_retry.request")` `[slack_sdk.http_retry.response](response.html "slack_sdk.http_retry.response")` `[slack_sdk.http_retry.state](state.html "slack_sdk.http_retry.state")` ## Functions `def all_builtin_retry_handlers() ‑> List[[RetryHandler](handler.html#slack_sdk.http_retry.handler.RetryHandler "slack_sdk.http_retry.handler.RetryHandler")]` Expand source code ``` def all_builtin_retry_handlers() -> List[RetryHandler]: return [ connect_error_retry_handler, rate_limit_error_retry_handler, ] ``` `def default_retry_handlers() ‑> List[[RetryHandler](handler.html#slack_sdk.http_retry.handler.RetryHandler "slack_sdk.http_retry.handler.RetryHandler")]` Expand source code ``` def default_retry_handlers() -> List[RetryHandler]: return [connect_error_retry_handler] ``` ## Classes `class BackoffRetryIntervalCalculator (backoff_factor: float = 0.5, jitter: [Jitter](jitter.html#slack_sdk.http_retry.jitter.Jitter "slack_sdk.http_retry.jitter.Jitter") | None = None)` Expand source code ``` class BackoffRetryIntervalCalculator(RetryIntervalCalculator): """Retry interval calculator that calculates in the manner of Exponential Backoff And Jitter see also: https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/ """ backoff_factor: float jitter: Jitter def __init__(self, backoff_factor: float = 0.5, jitter: Optional[Jitter] = None): """Retry interval calculator that calculates in the manner of Exponential Backoff And Jitter Args: backoff_factor: The factor for the backoff interval calculation jitter: The jitter logic implementation """ self.backoff_factor = backoff_factor self.jitter = jitter if jitter is not None else RandomJitter() def calculate_sleep_duration(self, current_attempt: int) -> float: interval = self.backoff_factor * (2 ** (current_attempt)) sleep_duration = self.jitter.recalculate(interval) return sleep_duration ``` Retry interval calculator that calculates in the manner of Exponential Backoff And Jitter see also: [https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/) Retry interval calculator that calculates in the manner of Exponential Backoff And Jitter ## Args **`backoff_factor`** The factor for the backoff interval calculation **`jitter`** The jitter logic implementation ### Ancestors * [RetryIntervalCalculator](interval_calculator.html#slack_sdk.http_retry.interval_calculator.RetryIntervalCalculator "slack_sdk.http_retry.interval_calculator.RetryIntervalCalculator") ### Class variables `var backoff_factor : float` The type of the None singleton. `var jitter : [Jitter](jitter.html#slack_sdk.http_retry.jitter.Jitter "slack_sdk.http_retry.jitter.Jitter")` The type of the None singleton. ### Inherited members * `**[RetryIntervalCalculator](interval_calculator.html#slack_sdk.http_retry.interval_calculator.RetryIntervalCalculator "slack_sdk.http_retry.interval_calculator.RetryIntervalCalculator")**`: * `[calculate_sleep_duration](interval_calculator.html#slack_sdk.http_retry.interval_calculator.RetryIntervalCalculator.calculate_sleep_duration "slack_sdk.http_retry.interval_calculator.RetryIntervalCalculator.calculate_sleep_duration")` `class ConnectionErrorRetryHandler (max_retry_count: int = 1, interval_calculator: [RetryIntervalCalculator](interval_calculator.html#slack_sdk.http_retry.interval_calculator.RetryIntervalCalculator "slack_sdk.http_retry.interval_calculator.RetryIntervalCalculator") = , error_types: List[Type[Exception]] = [, , ])` Expand source code ``` class ConnectionErrorRetryHandler(RetryHandler): """RetryHandler that does retries for connectivity issues.""" def __init__( self, max_retry_count: int = 1, interval_calculator: RetryIntervalCalculator = default_interval_calculator, error_types: List[Type[Exception]] = [ # To cover URLError: URLError, ConnectionResetError, RemoteDisconnected, ], ): super().__init__(max_retry_count, interval_calculator) self.error_types_to_do_retries = error_types def _can_retry( self, *, state: RetryState, request: HttpRequest, response: Optional[HttpResponse] = None, error: Optional[Exception] = None, ) -> bool: if error is None: return False if isinstance(error, URLError): if response is not None: return False # status 40x for error_type in self.error_types_to_do_retries: if isinstance(error, error_type): return True return False ``` RetryHandler that does retries for connectivity issues. RetryHandler interface. ## Args **`max_retry_count`** The maximum times to do retries **`interval_calculator`** Pass an interval calculator for customizing the logic ### Ancestors * [RetryHandler](handler.html#slack_sdk.http_retry.handler.RetryHandler "slack_sdk.http_retry.handler.RetryHandler") ### Inherited members * `**[RetryHandler](handler.html#slack_sdk.http_retry.handler.RetryHandler "slack_sdk.http_retry.handler.RetryHandler")**`: * `[interval_calculator](handler.html#slack_sdk.http_retry.handler.RetryHandler.interval_calculator "slack_sdk.http_retry.handler.RetryHandler.interval_calculator")` * `[max_retry_count](handler.html#slack_sdk.http_retry.handler.RetryHandler.max_retry_count "slack_sdk.http_retry.handler.RetryHandler.max_retry_count")` `class FixedValueRetryIntervalCalculator (fixed_internal: float = 0.5)` Expand source code ``` class FixedValueRetryIntervalCalculator(RetryIntervalCalculator): """Retry interval calculator that uses a fixed value.""" fixed_interval: float def __init__(self, fixed_internal: float = 0.5): """Retry interval calculator that uses a fixed value. Args: fixed_internal: The fixed interval seconds """ self.fixed_interval = fixed_internal def calculate_sleep_duration(self, current_attempt: int) -> float: return self.fixed_interval ``` Retry interval calculator that uses a fixed value. Retry interval calculator that uses a fixed value. ## Args **`fixed_internal`** The fixed interval seconds ### Ancestors * [RetryIntervalCalculator](interval_calculator.html#slack_sdk.http_retry.interval_calculator.RetryIntervalCalculator "slack_sdk.http_retry.interval_calculator.RetryIntervalCalculator") ### Class variables `var fixed_interval : float` The type of the None singleton. ### Inherited members * `**[RetryIntervalCalculator](interval_calculator.html#slack_sdk.http_retry.interval_calculator.RetryIntervalCalculator "slack_sdk.http_retry.interval_calculator.RetryIntervalCalculator")**`: * `[calculate_sleep_duration](interval_calculator.html#slack_sdk.http_retry.interval_calculator.RetryIntervalCalculator.calculate_sleep_duration "slack_sdk.http_retry.interval_calculator.RetryIntervalCalculator.calculate_sleep_duration")` `class HttpRequest (*, method: str, url: str, headers: Dict[str, str | List[str]], body_params: Dict[str, Any] | None = None, data: bytes | None = None)` Expand source code ``` class HttpRequest: """HTTP request representation""" method: str url: str headers: Dict[str, Union[str, List[str]]] body_params: Optional[Dict[str, Any]] data: Optional[bytes] def __init__( self, *, method: str, url: str, headers: Dict[str, Union[str, List[str]]], body_params: Optional[Dict[str, Any]] = None, data: Optional[bytes] = None, ): self.method = method self.url = url self.headers = {k: v if isinstance(v, list) else [v] for k, v in headers.items()} self.body_params = body_params self.data = data @classmethod def from_urllib_http_request(cls, req: Request) -> "HttpRequest": return HttpRequest( method=req.method, # type: ignore[arg-type] url=req.full_url, headers={k: v if isinstance(v, list) else [v] for k, v in req.headers.items()}, data=req.data, # type: ignore[arg-type] ) ``` HTTP request representation ### Class variables `var body_params : Dict[str, Any] | None` The type of the None singleton. `var data : bytes | None` The type of the None singleton. `var headers : Dict[str, str | List[str]]` The type of the None singleton. `var method : str` The type of the None singleton. `var url : str` The type of the None singleton. ### Static methods `def from_urllib_http_request(req: urllib.request.Request) ‑> [HttpRequest](request.html#slack_sdk.http_retry.request.HttpRequest "slack_sdk.http_retry.request.HttpRequest")` `class HttpResponse (*, status_code: int | str, headers: Dict[str, str | List[str]], body: Dict[str, Any] | None = None, data: bytes | None = None)` Expand source code ``` class HttpResponse: """HTTP response representation""" status_code: int headers: Dict[str, Union[List[str], str]] body: Optional[Dict[str, Any]] data: Optional[bytes] def __init__( self, *, status_code: Union[int, str], headers: Dict[str, Union[str, List[str]]], body: Optional[Dict[str, Any]] = None, data: Optional[bytes] = None, ): self.status_code = int(status_code) self.headers = {k: v if isinstance(v, list) else [v] for k, v in headers.items()} self.body = body self.data = data ``` HTTP response representation ### Class variables `var body : Dict[str, Any] | None` The type of the None singleton. `var data : bytes | None` The type of the None singleton. `var headers : Dict[str, str | List[str]]` The type of the None singleton. `var status_code : int` The type of the None singleton. `class Jitter` Expand source code ``` class Jitter: """Jitter interface""" def recalculate(self, duration: float) -> float: """Recalculate the given duration. see also: https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/ Args: duration: the duration in seconds Returns: A new duration that the jitter amount is added """ raise NotImplementedError() ``` Jitter interface ### Subclasses * [RandomJitter](jitter.html#slack_sdk.http_retry.jitter.RandomJitter "slack_sdk.http_retry.jitter.RandomJitter") ### Methods `def recalculate(self, duration: float) ‑> float` Expand source code ``` def recalculate(self, duration: float) -> float: """Recalculate the given duration. see also: https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/ Args: duration: the duration in seconds Returns: A new duration that the jitter amount is added """ raise NotImplementedError() ``` Recalculate the given duration. see also: [https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/) ## Args **`duration`** the duration in seconds ## Returns A new duration that the jitter amount is added `class RateLimitErrorRetryHandler (max_retry_count: int = 1, interval_calculator: [RetryIntervalCalculator](interval_calculator.html#slack_sdk.http_retry.interval_calculator.RetryIntervalCalculator "slack_sdk.http_retry.interval_calculator.RetryIntervalCalculator") = )` Expand source code ``` class RateLimitErrorRetryHandler(RetryHandler): """RetryHandler that does retries for rate limited errors.""" def _can_retry( self, *, state: RetryState, request: HttpRequest, response: Optional[HttpResponse] = None, error: Optional[Exception] = None, ) -> bool: return response is not None and response.status_code == 429 def prepare_for_next_attempt( self, *, state: RetryState, request: HttpRequest, response: Optional[HttpResponse] = None, error: Optional[Exception] = None, ) -> None: if response is None: raise error # type: ignore[misc] state.next_attempt_requested = True retry_after_header_name: Optional[str] = None for k in response.headers.keys(): if k.lower() == "retry-after": retry_after_header_name = k break duration = 1 if retry_after_header_name is None: # This situation usually does not arise. Just in case. duration += random.random() # type: ignore[assignment] else: duration = int(response.headers.get(retry_after_header_name)[0]) + random.random() # type: ignore[index, assignment] # noqa: E501 time.sleep(duration) state.increment_current_attempt() ``` RetryHandler that does retries for rate limited errors. RetryHandler interface. ## Args **`max_retry_count`** The maximum times to do retries **`interval_calculator`** Pass an interval calculator for customizing the logic ### Ancestors * [RetryHandler](handler.html#slack_sdk.http_retry.handler.RetryHandler "slack_sdk.http_retry.handler.RetryHandler") ### Methods `def prepare_for_next_attempt(self, *, state: [RetryState](state.html#slack_sdk.http_retry.state.RetryState "slack_sdk.http_retry.state.RetryState"), request: [HttpRequest](request.html#slack_sdk.http_retry.request.HttpRequest "slack_sdk.http_retry.request.HttpRequest"), response: [HttpResponse](response.html#slack_sdk.http_retry.response.HttpResponse "slack_sdk.http_retry.response.HttpResponse") | None = None, error: Exception | None = None) ‑> None` Expand source code ``` def prepare_for_next_attempt( self, *, state: RetryState, request: HttpRequest, response: Optional[HttpResponse] = None, error: Optional[Exception] = None, ) -> None: if response is None: raise error # type: ignore[misc] state.next_attempt_requested = True retry_after_header_name: Optional[str] = None for k in response.headers.keys(): if k.lower() == "retry-after": retry_after_header_name = k break duration = 1 if retry_after_header_name is None: # This situation usually does not arise. Just in case. duration += random.random() # type: ignore[assignment] else: duration = int(response.headers.get(retry_after_header_name)[0]) + random.random() # type: ignore[index, assignment] # noqa: E501 time.sleep(duration) state.increment_current_attempt() ``` ### Inherited members * `**[RetryHandler](handler.html#slack_sdk.http_retry.handler.RetryHandler "slack_sdk.http_retry.handler.RetryHandler")**`: * `[interval_calculator](handler.html#slack_sdk.http_retry.handler.RetryHandler.interval_calculator "slack_sdk.http_retry.handler.RetryHandler.interval_calculator")` * `[max_retry_count](handler.html#slack_sdk.http_retry.handler.RetryHandler.max_retry_count "slack_sdk.http_retry.handler.RetryHandler.max_retry_count")` `class RetryHandler (max_retry_count: int = 1, interval_calculator: [RetryIntervalCalculator](interval_calculator.html#slack_sdk.http_retry.interval_calculator.RetryIntervalCalculator "slack_sdk.http_retry.interval_calculator.RetryIntervalCalculator") = )` Expand source code ``` class RetryHandler: """RetryHandler interface. You can pass an array of handlers to customize retry logics in supported API clients. """ max_retry_count: int interval_calculator: RetryIntervalCalculator def __init__( self, max_retry_count: int = 1, interval_calculator: RetryIntervalCalculator = default_interval_calculator, ): """RetryHandler interface. Args: max_retry_count: The maximum times to do retries interval_calculator: Pass an interval calculator for customizing the logic """ self.max_retry_count = max_retry_count self.interval_calculator = interval_calculator def can_retry( self, *, state: RetryState, request: HttpRequest, response: Optional[HttpResponse] = None, error: Optional[Exception] = None, ) -> bool: if state.current_attempt >= self.max_retry_count: return False return self._can_retry( state=state, request=request, response=response, error=error, ) def _can_retry( self, *, state: RetryState, request: HttpRequest, response: Optional[HttpResponse] = None, error: Optional[Exception] = None, ) -> bool: raise NotImplementedError() def prepare_for_next_attempt( self, *, state: RetryState, request: HttpRequest, response: Optional[HttpResponse] = None, error: Optional[Exception] = None, ) -> None: state.next_attempt_requested = True duration = self.interval_calculator.calculate_sleep_duration(state.current_attempt) time.sleep(duration) state.increment_current_attempt() ``` RetryHandler interface. You can pass an array of handlers to customize retry logics in supported API clients. RetryHandler interface. ## Args **`max_retry_count`** The maximum times to do retries **`interval_calculator`** Pass an interval calculator for customizing the logic ### Subclasses * [ConnectionErrorRetryHandler](builtin_handlers.html#slack_sdk.http_retry.builtin_handlers.ConnectionErrorRetryHandler "slack_sdk.http_retry.builtin_handlers.ConnectionErrorRetryHandler") * [RateLimitErrorRetryHandler](builtin_handlers.html#slack_sdk.http_retry.builtin_handlers.RateLimitErrorRetryHandler "slack_sdk.http_retry.builtin_handlers.RateLimitErrorRetryHandler") * [ServerErrorRetryHandler](builtin_handlers.html#slack_sdk.http_retry.builtin_handlers.ServerErrorRetryHandler "slack_sdk.http_retry.builtin_handlers.ServerErrorRetryHandler") ### Class variables `var interval_calculator : [RetryIntervalCalculator](interval_calculator.html#slack_sdk.http_retry.interval_calculator.RetryIntervalCalculator "slack_sdk.http_retry.interval_calculator.RetryIntervalCalculator")` The type of the None singleton. `var max_retry_count : int` The type of the None singleton. ### Methods `def can_retry(self, *, state: [RetryState](state.html#slack_sdk.http_retry.state.RetryState "slack_sdk.http_retry.state.RetryState"), request: [HttpRequest](request.html#slack_sdk.http_retry.request.HttpRequest "slack_sdk.http_retry.request.HttpRequest"), response: [HttpResponse](response.html#slack_sdk.http_retry.response.HttpResponse "slack_sdk.http_retry.response.HttpResponse") | None = None, error: Exception | None = None) ‑> bool` Expand source code ``` def can_retry( self, *, state: RetryState, request: HttpRequest, response: Optional[HttpResponse] = None, error: Optional[Exception] = None, ) -> bool: if state.current_attempt >= self.max_retry_count: return False return self._can_retry( state=state, request=request, response=response, error=error, ) ``` `def prepare_for_next_attempt(self, *, state: [RetryState](state.html#slack_sdk.http_retry.state.RetryState "slack_sdk.http_retry.state.RetryState"), request: [HttpRequest](request.html#slack_sdk.http_retry.request.HttpRequest "slack_sdk.http_retry.request.HttpRequest"), response: [HttpResponse](response.html#slack_sdk.http_retry.response.HttpResponse "slack_sdk.http_retry.response.HttpResponse") | None = None, error: Exception | None = None) ‑> None` Expand source code ``` def prepare_for_next_attempt( self, *, state: RetryState, request: HttpRequest, response: Optional[HttpResponse] = None, error: Optional[Exception] = None, ) -> None: state.next_attempt_requested = True duration = self.interval_calculator.calculate_sleep_duration(state.current_attempt) time.sleep(duration) state.increment_current_attempt() ``` `class RetryIntervalCalculator` Expand source code ``` class RetryIntervalCalculator: """Retry interval calculator interface.""" def calculate_sleep_duration(self, current_attempt: int) -> float: """Calculates an interval duration in seconds. Args: current_attempt: the number of the current attempt (zero-origin; 0 means no retries are done so far) Returns: calculated interval duration in seconds """ raise NotImplementedError() ``` Retry interval calculator interface. ### Subclasses * [BackoffRetryIntervalCalculator](builtin_interval_calculators.html#slack_sdk.http_retry.builtin_interval_calculators.BackoffRetryIntervalCalculator "slack_sdk.http_retry.builtin_interval_calculators.BackoffRetryIntervalCalculator") * [FixedValueRetryIntervalCalculator](builtin_interval_calculators.html#slack_sdk.http_retry.builtin_interval_calculators.FixedValueRetryIntervalCalculator "slack_sdk.http_retry.builtin_interval_calculators.FixedValueRetryIntervalCalculator") ### Methods `def calculate_sleep_duration(self, current_attempt: int) ‑> float` Expand source code ``` def calculate_sleep_duration(self, current_attempt: int) -> float: """Calculates an interval duration in seconds. Args: current_attempt: the number of the current attempt (zero-origin; 0 means no retries are done so far) Returns: calculated interval duration in seconds """ raise NotImplementedError() ``` Calculates an interval duration in seconds. ## Args **`current_attempt`** the number of the current attempt (zero-origin; 0 means no retries are done so far) ## Returns calculated interval duration in seconds `class RetryState (*, current_attempt: int = 0, custom_values: Dict[str, Any] | None = None)` Expand source code ``` class RetryState: next_attempt_requested: bool current_attempt: int # zero-origin custom_values: Optional[Dict[str, Any]] def __init__( self, *, current_attempt: int = 0, custom_values: Optional[Dict[str, Any]] = None, ): self.next_attempt_requested = False self.current_attempt = current_attempt self.custom_values = custom_values def increment_current_attempt(self) -> int: self.current_attempt += 1 return self.current_attempt ``` ### Class variables `var current_attempt : int` The type of the None singleton. `var custom_values : Dict[str, Any] | None` The type of the None singleton. `var next_attempt_requested : bool` The type of the None singleton. ### Methods `def increment_current_attempt(self) ‑> int` Expand source code ``` def increment_current_attempt(self) -> int: self.current_attempt += 1 return self.current_attempt ``` --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/models # Module slack_sdk.models Classes for constructing Slack-specific data structure ## Sub-modules `[slack_sdk.models.attachments](attachments/index.html "slack_sdk.models.attachments")` `[slack_sdk.models.basic_objects](basic_objects.html "slack_sdk.models.basic_objects")` `[slack_sdk.models.blocks](blocks/index.html "slack_sdk.models.blocks")` Block Kit data model objects … `[slack_sdk.models.dialoags](dialoags.html "slack_sdk.models.dialoags")` `[slack_sdk.models.dialogs](dialogs/index.html "slack_sdk.models.dialogs")` `[slack_sdk.models.messages](messages/index.html "slack_sdk.models.messages")` `[slack_sdk.models.metadata](metadata/index.html "slack_sdk.models.metadata")` `[slack_sdk.models.views](views/index.html "slack_sdk.models.views")` ## Functions `def extract_json(item_or_items: [JsonObject](basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") | Sequence[[JsonObject](basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")], *format_args) ‑> Dict[Any, Any] | List[Dict[Any, Any]] | Sequence[[JsonObject](basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")]` Expand source code ``` def extract_json( item_or_items: Union[JsonObject, Sequence[JsonObject]], *format_args ) -> Union[Dict[Any, Any], List[Dict[Any, Any]], Sequence[JsonObject]]: """ Given a sequence (or single item), attempt to call the to_dict() method on each item and return a plain list. If item is not the expected type, return it unmodified, in case it's already a plain dict or some other user created class. Args: item_or_items: item(s) to go through format_args: Any formatting specifiers to pass into the object's to_dict method """ try: return [ elem.to_dict(*format_args) if isinstance(elem, JsonObject) else elem for elem in item_or_items # type: ignore[union-attr] ] except TypeError: # not iterable, so try returning it as a single item return item_or_items.to_dict(*format_args) if isinstance(item_or_items, JsonObject) else item_or_items ``` Given a sequence (or single item), attempt to call the to\_dict() method on each item and return a plain list. If item is not the expected type, return it unmodified, in case it's already a plain dict or some other user created class. ## Args **`item_or_items`** item(s) to go through **`format_args`** Any formatting specifiers to pass into the object's to\_dict method `def show_unknown_key_warning(name: str | object, others: dict)` Expand source code ``` def show_unknown_key_warning(name: Union[str, object], others: dict): if "type" in others: others.pop("type") if len(others) > 0: keys = ", ".join(others.keys()) logger = logging.getLogger(__name__) if isinstance(name, object): name = name.__class__.__name__ logger.debug( f"!!! {name}'s constructor args ({keys}) were ignored." f"If they should be supported by this library, report this issue to the project :bow: " f"https://github.com/slackapi/python-slack-sdk/issues" ) ``` ## Classes `class BaseObject` Expand source code ``` class BaseObject: """The base class for all model objects in this module""" def __str__(self): return f"" ``` The base class for all model objects in this module ### Subclasses * [JsonObject](basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [Link](messages/index.html#slack_sdk.models.messages.Link "slack_sdk.models.messages.Link") `class EnumValidator (attribute: str, enum: Iterable[str])` Expand source code ``` class EnumValidator(JsonValidator): def __init__(self, attribute: str, enum: Iterable[str]): super().__init__(f"{attribute} attribute must be one of the following values: " f"{', '.join(enum)}") ``` Decorate a method on a class to mark it as a JSON validator. Validation functions should return true if valid, false if not. ## Args **`message`** Message to be attached to the thrown SlackObjectFormationError ### Ancestors * [JsonValidator](basic_objects.html#slack_sdk.models.basic_objects.JsonValidator "slack_sdk.models.basic_objects.JsonValidator") `class JsonObject` Expand source code ``` class JsonObject(BaseObject, metaclass=ABCMeta): """The base class for JSON serializable class objects""" @property @abstractmethod def attributes(self) -> Set[str]: """Provide a set of attributes of this object that will make up its JSON structure""" return set() def validate_json(self) -> None: """ Raises: SlackObjectFormationError if the object was not valid """ for attribute in (func for func in dir(self) if not func.startswith("__")): method = getattr(self, attribute, None) if callable(method) and hasattr(method, "validator"): method() def get_object_attribute(self, key: str): return getattr(self, key, None) def get_non_null_attributes(self) -> dict: """ Construct a dictionary out of non-null keys (from attributes property) present on this object """ def to_dict_compatible(value: Union[dict, list, object, tuple]) -> Union[dict, list, Any]: if isinstance(value, (list, tuple)): return [to_dict_compatible(v) for v in value] else: to_dict = getattr(value, "to_dict", None) if to_dict and callable(to_dict): return {k: to_dict_compatible(v) for k, v in value.to_dict().items()} # type: ignore[attr-defined] else: return value def is_not_empty(self, key: str) -> bool: value = self.get_object_attribute(key) if value is None: return False # Usually, Block Kit components do not allow an empty array for a property value, but there are some exceptions. # The following code deals with these exceptions: type_value = getattr(self, "type", None) for empty_allowed in EMPTY_ALLOWED_TYPE_AND_PROPERTY_LIST: if type_value == empty_allowed["type"] and key == empty_allowed["property"]: return True has_len = getattr(value, "__len__", None) is not None if has_len: return len(value) > 0 else: return value is not None return { key: to_dict_compatible(value=self.get_object_attribute(key)) for key in sorted(self.attributes) if is_not_empty(self, key) } def to_dict(self, *args) -> dict: """ Extract this object as a JSON-compatible, Slack-API-valid dictionary Args: *args: Any specific formatting args (rare; generally not required) Raises: SlackObjectFormationError if the object was not valid """ self.validate_json() return self.get_non_null_attributes() def __repr__(self): dict_value = self.get_non_null_attributes() if dict_value: return f"" else: return self.__str__() def __eq__(self, other: Any) -> bool: if not isinstance(other, JsonObject): return False return self.to_dict() == other.to_dict() ``` The base class for JSON serializable class objects ### Ancestors * [BaseObject](basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Subclasses * [Action](attachments/index.html#slack_sdk.models.attachments.Action "slack_sdk.models.attachments.Action") * [Attachment](attachments/index.html#slack_sdk.models.attachments.Attachment "slack_sdk.models.attachments.Attachment") * [AttachmentField](attachments/index.html#slack_sdk.models.attachments.AttachmentField "slack_sdk.models.attachments.AttachmentField") * [ConfirmObject](blocks/basic_components.html#slack_sdk.models.blocks.basic_components.ConfirmObject "slack_sdk.models.blocks.basic_components.ConfirmObject") * [DispatchActionConfig](blocks/basic_components.html#slack_sdk.models.blocks.basic_components.DispatchActionConfig "slack_sdk.models.blocks.basic_components.DispatchActionConfig") * [FeedbackButtonObject](blocks/basic_components.html#slack_sdk.models.blocks.basic_components.FeedbackButtonObject "slack_sdk.models.blocks.basic_components.FeedbackButtonObject") * [Option](blocks/basic_components.html#slack_sdk.models.blocks.basic_components.Option "slack_sdk.models.blocks.basic_components.Option") * [OptionGroup](blocks/basic_components.html#slack_sdk.models.blocks.basic_components.OptionGroup "slack_sdk.models.blocks.basic_components.OptionGroup") * [SlackFile](blocks/basic_components.html#slack_sdk.models.blocks.basic_components.SlackFile "slack_sdk.models.blocks.basic_components.SlackFile") * [TextObject](blocks/basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject") * [Workflow](blocks/basic_components.html#slack_sdk.models.blocks.basic_components.Workflow "slack_sdk.models.blocks.basic_components.Workflow") * [WorkflowTrigger](blocks/basic_components.html#slack_sdk.models.blocks.basic_components.WorkflowTrigger "slack_sdk.models.blocks.basic_components.WorkflowTrigger") * [BlockElement](blocks/block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [ConversationFilter](blocks/block_elements.html#slack_sdk.models.blocks.block_elements.ConversationFilter "slack_sdk.models.blocks.block_elements.ConversationFilter") * [Block](blocks/blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block") * [AbstractDialogSelector](dialogs/index.html#slack_sdk.models.dialogs.AbstractDialogSelector "slack_sdk.models.dialogs.AbstractDialogSelector") * [DialogBuilder](dialogs/index.html#slack_sdk.models.dialogs.DialogBuilder "slack_sdk.models.dialogs.DialogBuilder") * [DialogTextComponent](dialogs/index.html#slack_sdk.models.dialogs.DialogTextComponent "slack_sdk.models.dialogs.DialogTextComponent") * [Chunk](messages/chunk.html#slack_sdk.models.messages.chunk.Chunk "slack_sdk.models.messages.chunk.Chunk") * [Message](messages/message.html#slack_sdk.models.messages.message.Message "slack_sdk.models.messages.message.Message") * [ContentItemEntityFields](metadata/index.html#slack_sdk.models.metadata.ContentItemEntityFields "slack_sdk.models.metadata.ContentItemEntityFields") * [EntityActionButton](metadata/index.html#slack_sdk.models.metadata.EntityActionButton "slack_sdk.models.metadata.EntityActionButton") * [EntityActionProcessingState](metadata/index.html#slack_sdk.models.metadata.EntityActionProcessingState "slack_sdk.models.metadata.EntityActionProcessingState") * [EntityActions](metadata/index.html#slack_sdk.models.metadata.EntityActions "slack_sdk.models.metadata.EntityActions") * [EntityArrayItemField](metadata/index.html#slack_sdk.models.metadata.EntityArrayItemField "slack_sdk.models.metadata.EntityArrayItemField") * [EntityAttributes](metadata/index.html#slack_sdk.models.metadata.EntityAttributes "slack_sdk.models.metadata.EntityAttributes") * [EntityBooleanCheckboxField](metadata/index.html#slack_sdk.models.metadata.EntityBooleanCheckboxField "slack_sdk.models.metadata.EntityBooleanCheckboxField") * [EntityBooleanTextField](metadata/index.html#slack_sdk.models.metadata.EntityBooleanTextField "slack_sdk.models.metadata.EntityBooleanTextField") * [EntityCustomField](metadata/index.html#slack_sdk.models.metadata.EntityCustomField "slack_sdk.models.metadata.EntityCustomField") * [EntityEditNumberConfig](metadata/index.html#slack_sdk.models.metadata.EntityEditNumberConfig "slack_sdk.models.metadata.EntityEditNumberConfig") * [EntityEditSelectConfig](metadata/index.html#slack_sdk.models.metadata.EntityEditSelectConfig "slack_sdk.models.metadata.EntityEditSelectConfig") * [EntityEditSupport](metadata/index.html#slack_sdk.models.metadata.EntityEditSupport "slack_sdk.models.metadata.EntityEditSupport") * [EntityEditTextConfig](metadata/index.html#slack_sdk.models.metadata.EntityEditTextConfig "slack_sdk.models.metadata.EntityEditTextConfig") * [EntityFullSizePreview](metadata/index.html#slack_sdk.models.metadata.EntityFullSizePreview "slack_sdk.models.metadata.EntityFullSizePreview") * [EntityFullSizePreviewError](metadata/index.html#slack_sdk.models.metadata.EntityFullSizePreviewError "slack_sdk.models.metadata.EntityFullSizePreviewError") * [EntityIconField](metadata/index.html#slack_sdk.models.metadata.EntityIconField "slack_sdk.models.metadata.EntityIconField") * [EntityIconSlackFile](metadata/index.html#slack_sdk.models.metadata.EntityIconSlackFile "slack_sdk.models.metadata.EntityIconSlackFile") * [EntityImageField](metadata/index.html#slack_sdk.models.metadata.EntityImageField "slack_sdk.models.metadata.EntityImageField") * [EntityMetadata](metadata/index.html#slack_sdk.models.metadata.EntityMetadata "slack_sdk.models.metadata.EntityMetadata") * [EntityPayload](metadata/index.html#slack_sdk.models.metadata.EntityPayload "slack_sdk.models.metadata.EntityPayload") * [EntityRefField](metadata/index.html#slack_sdk.models.metadata.EntityRefField "slack_sdk.models.metadata.EntityRefField") * [EntityStringField](metadata/index.html#slack_sdk.models.metadata.EntityStringField "slack_sdk.models.metadata.EntityStringField") * [EntityTimestampField](metadata/index.html#slack_sdk.models.metadata.EntityTimestampField "slack_sdk.models.metadata.EntityTimestampField") * [EntityTitle](metadata/index.html#slack_sdk.models.metadata.EntityTitle "slack_sdk.models.metadata.EntityTitle") * [EntityTypedField](metadata/index.html#slack_sdk.models.metadata.EntityTypedField "slack_sdk.models.metadata.EntityTypedField") * [EntityUserField](metadata/index.html#slack_sdk.models.metadata.EntityUserField "slack_sdk.models.metadata.EntityUserField") * [EntityUserIDField](metadata/index.html#slack_sdk.models.metadata.EntityUserIDField "slack_sdk.models.metadata.EntityUserIDField") * [EventAndEntityMetadata](metadata/index.html#slack_sdk.models.metadata.EventAndEntityMetadata "slack_sdk.models.metadata.EventAndEntityMetadata") * [ExternalRef](metadata/index.html#slack_sdk.models.metadata.ExternalRef "slack_sdk.models.metadata.ExternalRef") * [FileEntityFields](metadata/index.html#slack_sdk.models.metadata.FileEntityFields "slack_sdk.models.metadata.FileEntityFields") * [FileEntitySlackFile](metadata/index.html#slack_sdk.models.metadata.FileEntitySlackFile "slack_sdk.models.metadata.FileEntitySlackFile") * [IncidentEntityFields](metadata/index.html#slack_sdk.models.metadata.IncidentEntityFields "slack_sdk.models.metadata.IncidentEntityFields") * [Metadata](metadata/index.html#slack_sdk.models.metadata.Metadata "slack_sdk.models.metadata.Metadata") * [TaskEntityFields](metadata/index.html#slack_sdk.models.metadata.TaskEntityFields "slack_sdk.models.metadata.TaskEntityFields") * [View](views/index.html#slack_sdk.models.views.View "slack_sdk.models.views.View") * [ViewState](views/index.html#slack_sdk.models.views.ViewState "slack_sdk.models.views.ViewState") * [ViewStateValue](views/index.html#slack_sdk.models.views.ViewStateValue "slack_sdk.models.views.ViewStateValue") ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property @abstractmethod def attributes(self) -> Set[str]: """Provide a set of attributes of this object that will make up its JSON structure""" return set() ``` Provide a set of attributes of this object that will make up its JSON structure ### Methods `def get_non_null_attributes(self) ‑> dict` Expand source code ``` def get_non_null_attributes(self) -> dict: """ Construct a dictionary out of non-null keys (from attributes property) present on this object """ def to_dict_compatible(value: Union[dict, list, object, tuple]) -> Union[dict, list, Any]: if isinstance(value, (list, tuple)): return [to_dict_compatible(v) for v in value] else: to_dict = getattr(value, "to_dict", None) if to_dict and callable(to_dict): return {k: to_dict_compatible(v) for k, v in value.to_dict().items()} # type: ignore[attr-defined] else: return value def is_not_empty(self, key: str) -> bool: value = self.get_object_attribute(key) if value is None: return False # Usually, Block Kit components do not allow an empty array for a property value, but there are some exceptions. # The following code deals with these exceptions: type_value = getattr(self, "type", None) for empty_allowed in EMPTY_ALLOWED_TYPE_AND_PROPERTY_LIST: if type_value == empty_allowed["type"] and key == empty_allowed["property"]: return True has_len = getattr(value, "__len__", None) is not None if has_len: return len(value) > 0 else: return value is not None return { key: to_dict_compatible(value=self.get_object_attribute(key)) for key in sorted(self.attributes) if is_not_empty(self, key) } ``` Construct a dictionary out of non-null keys (from attributes property) present on this object `def get_object_attribute(self, key: str)` Expand source code ``` def get_object_attribute(self, key: str): return getattr(self, key, None) ``` `def to_dict(self, *args) ‑> dict` Expand source code ``` def to_dict(self, *args) -> dict: """ Extract this object as a JSON-compatible, Slack-API-valid dictionary Args: *args: Any specific formatting args (rare; generally not required) Raises: SlackObjectFormationError if the object was not valid """ self.validate_json() return self.get_non_null_attributes() ``` Extract this object as a JSON-compatible, Slack-API-valid dictionary ## Args **`*args`** Any specific formatting args (rare; generally not required) ## Raises SlackObjectFormationError if the object was not valid `def validate_json(self) ‑> None` Expand source code ``` def validate_json(self) -> None: """ Raises: SlackObjectFormationError if the object was not valid """ for attribute in (func for func in dir(self) if not func.startswith("__")): method = getattr(self, attribute, None) if callable(method) and hasattr(method, "validator"): method() ``` ## Raises SlackObjectFormationError if the object was not valid `class JsonValidator (message: str)` Expand source code ``` class JsonValidator: def __init__(self, message: str): """ Decorate a method on a class to mark it as a JSON validator. Validation functions should return true if valid, false if not. Args: message: Message to be attached to the thrown SlackObjectFormationError """ self.message = message def __call__(self, func: Callable) -> Callable[..., None]: @wraps(func) def wrapped_f(*args, **kwargs): if not func(*args, **kwargs): raise SlackObjectFormationError(self.message) wrapped_f.validator = True # type: ignore[attr-defined] return wrapped_f ``` Decorate a method on a class to mark it as a JSON validator. Validation functions should return true if valid, false if not. ## Args **`message`** Message to be attached to the thrown SlackObjectFormationError ### Subclasses * [EnumValidator](basic_objects.html#slack_sdk.models.basic_objects.EnumValidator "slack_sdk.models.basic_objects.EnumValidator") --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/models/attachments # Module slack_sdk.models.attachments ## Classes `class AbstractActionSelector (*, name: str, text: str, selected_option: [Option](../blocks/basic_components.html#slack_sdk.models.blocks.basic_components.Option "slack_sdk.models.blocks.basic_components.Option") | None = None)` Expand source code ``` class AbstractActionSelector(Action, metaclass=ABCMeta): DataSourceTypes = DynamicSelectElementTypes.union({"external", "static"}) attributes = {"data_source", "name", "text", "type"} @property @abstractmethod def data_source(self) -> str: pass def __init__(self, *, name: str, text: str, selected_option: Optional[Option] = None): super().__init__(text=text, name=name, subtype="select") self.selected_option = selected_option @EnumValidator("data_source", DataSourceTypes) def data_source_valid(self): return self.data_source in self.DataSourceTypes def to_dict(self) -> dict: json = super().to_dict() if self.selected_option is not None: # this is a special case for ExternalActionSelectElement - in that case, # you pass the initial value of the selector as a selected_options array json["selected_options"] = extract_json([self.selected_option], "action") return json ``` Action in attachments [https://docs.slack.dev/messaging/formatting-message-text/#rich-layouts](https://docs.slack.dev/messaging/formatting-message-text/#rich-layouts) [https://docs.slack.dev/legacy/legacy-messaging/legacy-interactive-message-field-guide/#message\_action\_fields](https://docs.slack.dev/legacy/legacy-messaging/legacy-interactive-message-field-guide/#message_action_fields) ### Ancestors * [Action](#slack_sdk.models.attachments.Action "slack_sdk.models.attachments.Action") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Subclasses * [ActionChannelSelector](#slack_sdk.models.attachments.ActionChannelSelector "slack_sdk.models.attachments.ActionChannelSelector") * [ActionConversationSelector](#slack_sdk.models.attachments.ActionConversationSelector "slack_sdk.models.attachments.ActionConversationSelector") * [ActionExternalSelector](#slack_sdk.models.attachments.ActionExternalSelector "slack_sdk.models.attachments.ActionExternalSelector") * [ActionUserSelector](#slack_sdk.models.attachments.ActionUserSelector "slack_sdk.models.attachments.ActionUserSelector") * slack\_sdk.models.dialogs.ActionStaticSelector ### Class variables `var DataSourceTypes` The type of the None singleton. ### Instance variables `prop data_source : str` Expand source code ``` @property @abstractmethod def data_source(self) -> str: pass ``` ### Methods `def data_source_valid(self)` Expand source code ``` @EnumValidator("data_source", DataSourceTypes) def data_source_valid(self): return self.data_source in self.DataSourceTypes ``` ### Inherited members * `**[Action](#slack_sdk.models.attachments.Action "slack_sdk.models.attachments.Action")**`: * `[attributes](#slack_sdk.models.attachments.Action.attributes "slack_sdk.models.attachments.Action.attributes")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.attachments.Action.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.attachments.Action.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.attachments.Action.validate_json")` `class Action (*, text: str, subtype: str, name: str | None = None, url: str | None = None)` Expand source code ``` class Action(JsonObject): """Action in attachments https://docs.slack.dev/messaging/formatting-message-text/#rich-layouts https://docs.slack.dev/legacy/legacy-messaging/legacy-interactive-message-field-guide/#message_action_fields """ attributes = {"name", "text", "url"} def __init__( self, *, text: str, subtype: str, name: Optional[str] = None, url: Optional[str] = None, ): self.name = name self.url = url self.text = text self.subtype = subtype @JsonValidator("name or url attribute is required") def name_or_url_present(self): return self.name is not None or self.url is not None def to_dict(self) -> dict: json = super().to_dict() json["type"] = self.subtype return json ``` Action in attachments [https://docs.slack.dev/messaging/formatting-message-text/#rich-layouts](https://docs.slack.dev/messaging/formatting-message-text/#rich-layouts) [https://docs.slack.dev/legacy/legacy-messaging/legacy-interactive-message-field-guide/#message\_action\_fields](https://docs.slack.dev/legacy/legacy-messaging/legacy-interactive-message-field-guide/#message_action_fields) ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Subclasses * [AbstractActionSelector](#slack_sdk.models.attachments.AbstractActionSelector "slack_sdk.models.attachments.AbstractActionSelector") * [ActionButton](#slack_sdk.models.attachments.ActionButton "slack_sdk.models.attachments.ActionButton") * [ActionLinkButton](#slack_sdk.models.attachments.ActionLinkButton "slack_sdk.models.attachments.ActionLinkButton") ### Class variables `var attributes` The type of the None singleton. ### Methods `def name_or_url_present(self)` Expand source code ``` @JsonValidator("name or url attribute is required") def name_or_url_present(self): return self.name is not None or self.url is not None ``` ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class ActionButton (*, name: str, text: str, value: str, confirm: [ConfirmObject](../blocks/basic_components.html#slack_sdk.models.blocks.basic_components.ConfirmObject "slack_sdk.models.blocks.basic_components.ConfirmObject") | None = None, style: str | None = None)` Expand source code ``` class ActionButton(Action): @property def attributes(self): return super().attributes.union({"style", "value"}) value_max_length = 2000 def __init__( self, *, name: str, text: str, value: str, confirm: Optional[ConfirmObject] = None, style: Optional[str] = None, ): """Simple button for use inside attachments https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/ Args: name: Name this specific action. The name will be returned to your Action URL along with the message's callback_id when this action is invoked. Use it to identify this particular response path. text: The user-facing label for the message button or menu representing this action. Cannot contain markup. value: Provide a string identifying this specific action. It will be sent to your Action URL along with the name and attachment's callback_id . If providing multiple actions with the same name, value can be strategically used to differentiate intent. Cannot exceed 2000 characters. confirm: a ConfirmObject that will appear in a dialog to confirm user's choice. style: Leave blank to indicate that this is an ordinary button. Use "primary" or "danger" to mark important buttons. """ super().__init__(name=name, text=text, subtype="button") self.value = value self.confirm = confirm self.style = style @JsonValidator(f"value attribute cannot exceed {value_max_length} characters") def value_length(self): return len(self.value) <= self.value_max_length @EnumValidator("style", ButtonStyles) def style_valid(self): return self.style is None or self.style in ButtonStyles def to_dict(self) -> dict: json = super().to_dict() if self.confirm is not None: json["confirm"] = extract_json(self.confirm, "action") return json ``` Action in attachments [https://docs.slack.dev/messaging/formatting-message-text/#rich-layouts](https://docs.slack.dev/messaging/formatting-message-text/#rich-layouts) [https://docs.slack.dev/legacy/legacy-messaging/legacy-interactive-message-field-guide/#message\_action\_fields](https://docs.slack.dev/legacy/legacy-messaging/legacy-interactive-message-field-guide/#message_action_fields) Simple button for use inside attachments [https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/](https://docs.slack.dev/legacy/legacy-messaging/legacy-message-buttons/) ## Args **`name`** Name this specific action. The name will be returned to your Action URL along with the message's callback\_id when this action is invoked. Use it to identify this particular response path. **`text`** The user-facing label for the message button or menu representing this action. Cannot contain markup. **`value`** Provide a string identifying this specific action. It will be sent to your Action URL along with the name and attachment's callback\_id . If providing multiple actions with the same name, value can be strategically used to differentiate intent. Cannot exceed 2000 characters. **`confirm`** a ConfirmObject that will appear in a dialog to confirm user's choice. **`style`** Leave blank to indicate that this is an ordinary button. Use "primary" or "danger" to mark important buttons. ### Ancestors * [Action](#slack_sdk.models.attachments.Action "slack_sdk.models.attachments.Action") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var value_max_length` The type of the None singleton. ### Instance variables `prop attributes` Expand source code ``` @property def attributes(self): return super().attributes.union({"style", "value"}) ``` Build an unordered collection of unique elements. ### Methods `def style_valid(self)` Expand source code ``` @EnumValidator("style", ButtonStyles) def style_valid(self): return self.style is None or self.style in ButtonStyles ``` `def value_length(self)` Expand source code ``` @JsonValidator(f"value attribute cannot exceed {value_max_length} characters") def value_length(self): return len(self.value) <= self.value_max_length ``` ### Inherited members * `**[Action](#slack_sdk.models.attachments.Action "slack_sdk.models.attachments.Action")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.attachments.Action.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.attachments.Action.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.attachments.Action.validate_json")` `class ActionChannelSelector (name: str, text: str, selected_channel: [Option](../blocks/basic_components.html#slack_sdk.models.blocks.basic_components.Option "slack_sdk.models.blocks.basic_components.Option") | None = None)` Expand source code ``` class ActionChannelSelector(AbstractActionSelector): data_source = "channels" def __init__(self, name: str, text: str, selected_channel: Optional[Option] = None): """ Automatically populate the selector with a list of public channels in the workspace. https://docs.slack.dev/legacy/legacy-messaging/legacy-adding-menus-to-messages/#menu_channels Args: name: Name this specific action. The name will be returned to your Action URL along with the message's callback_id when this action is invoked. Use it to identify this particular response path. text: The user-facing label for the message button or menu representing this action. Cannot contain markup. selected_channel: An Option object to pre-select as the default value. """ super().__init__(name=name, text=text, selected_option=selected_channel) ``` Action in attachments [https://docs.slack.dev/messaging/formatting-message-text/#rich-layouts](https://docs.slack.dev/messaging/formatting-message-text/#rich-layouts) [https://docs.slack.dev/legacy/legacy-messaging/legacy-interactive-message-field-guide/#message\_action\_fields](https://docs.slack.dev/legacy/legacy-messaging/legacy-interactive-message-field-guide/#message_action_fields) Automatically populate the selector with a list of public channels in the workspace. [https://docs.slack.dev/legacy/legacy-messaging/legacy-adding-menus-to-messages/#menu\_channels](https://docs.slack.dev/legacy/legacy-messaging/legacy-adding-menus-to-messages/#menu_channels) ## Args **`name`** Name this specific action. The name will be returned to your Action URL along with the message's callback\_id when this action is invoked. Use it to identify this particular response path. **`text`** The user-facing label for the message button or menu representing this action. Cannot contain markup. **`selected_channel`** An Option object to pre-select as the default value. ### Ancestors * [AbstractActionSelector](#slack_sdk.models.attachments.AbstractActionSelector "slack_sdk.models.attachments.AbstractActionSelector") * [Action](#slack_sdk.models.attachments.Action "slack_sdk.models.attachments.Action") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var data_source` The type of the None singleton. ### Inherited members * `**[AbstractActionSelector](#slack_sdk.models.attachments.AbstractActionSelector "slack_sdk.models.attachments.AbstractActionSelector")**`: * `[DataSourceTypes](#slack_sdk.models.attachments.AbstractActionSelector.DataSourceTypes "slack_sdk.models.attachments.AbstractActionSelector.DataSourceTypes")` * `[attributes](#slack_sdk.models.attachments.Action.attributes "slack_sdk.models.attachments.AbstractActionSelector.attributes")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.attachments.AbstractActionSelector.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.attachments.AbstractActionSelector.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.attachments.AbstractActionSelector.validate_json")` `class ActionConversationSelector (name: str, text: str, selected_conversation: [Option](../blocks/basic_components.html#slack_sdk.models.blocks.basic_components.Option "slack_sdk.models.blocks.basic_components.Option") | None = None)` Expand source code ``` class ActionConversationSelector(AbstractActionSelector): data_source = "conversations" def __init__(self, name: str, text: str, selected_conversation: Optional[Option] = None): """ Automatically populate the selector with a list of conversations they have in the workspace. https://docs.slack.dev/legacy/legacy-messaging/legacy-adding-menus-to-messages/#menu_conversations Args: name: Name this specific action. The name will be returned to your Action URL along with the message's callback_id when this action is invoked. Use it to identify this particular response path. text: The user-facing label for the message button or menu representing this action. Cannot contain markup. selected_conversation: An Option object to pre-select as the default value. """ super().__init__(name=name, text=text, selected_option=selected_conversation) ``` Action in attachments [https://docs.slack.dev/messaging/formatting-message-text/#rich-layouts](https://docs.slack.dev/messaging/formatting-message-text/#rich-layouts) [https://docs.slack.dev/legacy/legacy-messaging/legacy-interactive-message-field-guide/#message\_action\_fields](https://docs.slack.dev/legacy/legacy-messaging/legacy-interactive-message-field-guide/#message_action_fields) Automatically populate the selector with a list of conversations they have in the workspace. [https://docs.slack.dev/legacy/legacy-messaging/legacy-adding-menus-to-messages/#menu\_conversations](https://docs.slack.dev/legacy/legacy-messaging/legacy-adding-menus-to-messages/#menu_conversations) ## Args **`name`** Name this specific action. The name will be returned to your Action URL along with the message's callback\_id when this action is invoked. Use it to identify this particular response path. **`text`** The user-facing label for the message button or menu representing this action. Cannot contain markup. **`selected_conversation`** An Option object to pre-select as the default value. ### Ancestors * [AbstractActionSelector](#slack_sdk.models.attachments.AbstractActionSelector "slack_sdk.models.attachments.AbstractActionSelector") * [Action](#slack_sdk.models.attachments.Action "slack_sdk.models.attachments.Action") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var data_source` The type of the None singleton. ### Inherited members * `**[AbstractActionSelector](#slack_sdk.models.attachments.AbstractActionSelector "slack_sdk.models.attachments.AbstractActionSelector")**`: * `[DataSourceTypes](#slack_sdk.models.attachments.AbstractActionSelector.DataSourceTypes "slack_sdk.models.attachments.AbstractActionSelector.DataSourceTypes")` * `[attributes](#slack_sdk.models.attachments.Action.attributes "slack_sdk.models.attachments.AbstractActionSelector.attributes")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.attachments.AbstractActionSelector.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.attachments.AbstractActionSelector.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.attachments.AbstractActionSelector.validate_json")` `class ActionExternalSelector (*, name: str, text: str, selected_option: [Option](../blocks/basic_components.html#slack_sdk.models.blocks.basic_components.Option "slack_sdk.models.blocks.basic_components.Option") | None = None, min_query_length: int | None = None)` Expand source code ``` class ActionExternalSelector(AbstractActionSelector): data_source = "external" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"min_query_length"}) def __init__( self, *, name: str, text: str, selected_option: Optional[Option] = None, min_query_length: Optional[int] = None, ): """ Populate a message select menu from your own application dynamically. https://docs.slack.dev/legacy/legacy-messaging/legacy-adding-menus-to-messages/#menu_dynamic Args: name: Name this specific action. The name will be returned to your Action URL along with the message's callback_id when this action is invoked. Use it to identify this particular response path. text: The user-facing label for the message button or menu representing this action. Cannot contain markup. selected_option: An Option object to pre-select as the default value. min_query_length: Specify the number of characters that must be typed by a user into a dynamic select menu before dispatching to the app. """ super().__init__(name=name, text=text, selected_option=selected_option) self.min_query_length = min_query_length ``` Action in attachments [https://docs.slack.dev/messaging/formatting-message-text/#rich-layouts](https://docs.slack.dev/messaging/formatting-message-text/#rich-layouts) [https://docs.slack.dev/legacy/legacy-messaging/legacy-interactive-message-field-guide/#message\_action\_fields](https://docs.slack.dev/legacy/legacy-messaging/legacy-interactive-message-field-guide/#message_action_fields) Populate a message select menu from your own application dynamically. [https://docs.slack.dev/legacy/legacy-messaging/legacy-adding-menus-to-messages/#menu\_dynamic](https://docs.slack.dev/legacy/legacy-messaging/legacy-adding-menus-to-messages/#menu_dynamic) ## Args **`name`** Name this specific action. The name will be returned to your Action URL along with the message's callback\_id when this action is invoked. Use it to identify this particular response path. **`text`** The user-facing label for the message button or menu representing this action. Cannot contain markup. **`selected_option`** An Option object to pre-select as the default value. **`min_query_length`** Specify the number of characters that must be typed by a user into a dynamic select menu before dispatching to the app. ### Ancestors * [AbstractActionSelector](#slack_sdk.models.attachments.AbstractActionSelector "slack_sdk.models.attachments.AbstractActionSelector") * [Action](#slack_sdk.models.attachments.Action "slack_sdk.models.attachments.Action") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var data_source` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"min_query_length"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[AbstractActionSelector](#slack_sdk.models.attachments.AbstractActionSelector "slack_sdk.models.attachments.AbstractActionSelector")**`: * `[DataSourceTypes](#slack_sdk.models.attachments.AbstractActionSelector.DataSourceTypes "slack_sdk.models.attachments.AbstractActionSelector.DataSourceTypes")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.attachments.AbstractActionSelector.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.attachments.AbstractActionSelector.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.attachments.AbstractActionSelector.validate_json")` `class ActionLinkButton (*, text: str, url: str)` Expand source code ``` class ActionLinkButton(Action): def __init__(self, *, text: str, url: str): """A simple interactive button that just opens a URL https://docs.slack.dev/messaging/formatting-message-text/#rich-layouts Args: text: text to display on the button, eg 'Click Me!" url: the URL to open """ super().__init__(text=text, url=url, subtype="button") ``` Action in attachments [https://docs.slack.dev/messaging/formatting-message-text/#rich-layouts](https://docs.slack.dev/messaging/formatting-message-text/#rich-layouts) [https://docs.slack.dev/legacy/legacy-messaging/legacy-interactive-message-field-guide/#message\_action\_fields](https://docs.slack.dev/legacy/legacy-messaging/legacy-interactive-message-field-guide/#message_action_fields) A simple interactive button that just opens a URL [https://docs.slack.dev/messaging/formatting-message-text/#rich-layouts](https://docs.slack.dev/messaging/formatting-message-text/#rich-layouts) ## Args **`text`** text to display on the button, eg 'Click Me!" **`url`** the URL to open ### Ancestors * [Action](#slack_sdk.models.attachments.Action "slack_sdk.models.attachments.Action") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Inherited members * `**[Action](#slack_sdk.models.attachments.Action "slack_sdk.models.attachments.Action")**`: * `[attributes](#slack_sdk.models.attachments.Action.attributes "slack_sdk.models.attachments.Action.attributes")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.attachments.Action.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.attachments.Action.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.attachments.Action.validate_json")` `class ActionUserSelector (name: str, text: str, selected_user: [Option](../blocks/basic_components.html#slack_sdk.models.blocks.basic_components.Option "slack_sdk.models.blocks.basic_components.Option") | None = None)` Expand source code ``` class ActionUserSelector(AbstractActionSelector): data_source = "users" def __init__(self, name: str, text: str, selected_user: Optional[Option] = None): """Automatically populate the selector with a list of users in the workspace. https://docs.slack.dev/legacy/legacy-messaging/legacy-adding-menus-to-messages/#menu_team_members Args: name: Name this specific action. The name will be returned to your Action URL along with the message's callback_id when this action is invoked. Use it to identify this particular response path. text: The user-facing label for the message button or menu representing this action. Cannot contain markup. selected_user: An Option object to pre-select as the default value. """ super().__init__(name=name, text=text, selected_option=selected_user) ``` Action in attachments [https://docs.slack.dev/messaging/formatting-message-text/#rich-layouts](https://docs.slack.dev/messaging/formatting-message-text/#rich-layouts) [https://docs.slack.dev/legacy/legacy-messaging/legacy-interactive-message-field-guide/#message\_action\_fields](https://docs.slack.dev/legacy/legacy-messaging/legacy-interactive-message-field-guide/#message_action_fields) Automatically populate the selector with a list of users in the workspace. [https://docs.slack.dev/legacy/legacy-messaging/legacy-adding-menus-to-messages/#menu\_team\_members](https://docs.slack.dev/legacy/legacy-messaging/legacy-adding-menus-to-messages/#menu_team_members) ## Args **`name`** Name this specific action. The name will be returned to your Action URL along with the message's callback\_id when this action is invoked. Use it to identify this particular response path. **`text`** The user-facing label for the message button or menu representing this action. Cannot contain markup. **`selected_user`** An Option object to pre-select as the default value. ### Ancestors * [AbstractActionSelector](#slack_sdk.models.attachments.AbstractActionSelector "slack_sdk.models.attachments.AbstractActionSelector") * [Action](#slack_sdk.models.attachments.Action "slack_sdk.models.attachments.Action") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var data_source` The type of the None singleton. ### Inherited members * `**[AbstractActionSelector](#slack_sdk.models.attachments.AbstractActionSelector "slack_sdk.models.attachments.AbstractActionSelector")**`: * `[DataSourceTypes](#slack_sdk.models.attachments.AbstractActionSelector.DataSourceTypes "slack_sdk.models.attachments.AbstractActionSelector.DataSourceTypes")` * `[attributes](#slack_sdk.models.attachments.Action.attributes "slack_sdk.models.attachments.AbstractActionSelector.attributes")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.attachments.AbstractActionSelector.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.attachments.AbstractActionSelector.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.attachments.AbstractActionSelector.validate_json")` `class Attachment (*, text: str, fallback: str | None = None, fields: Sequence[[AttachmentField](#slack_sdk.models.attachments.AttachmentField "slack_sdk.models.attachments.AttachmentField")] | None = None, color: str | None = None, markdown_in: Sequence[str] | None = None, title: str | None = None, title_link: str | None = None, pretext: str | None = None, author_name: str | None = None, author_subname: str | None = None, author_link: str | None = None, author_icon: str | None = None, image_url: str | None = None, thumb_url: str | None = None, footer: str | None = None, footer_icon: str | None = None, ts: int | None = None)` Expand source code ``` class Attachment(JsonObject): attributes = { "author_icon", "author_link", "author_name", "author_subname", "color", "fallback", "fields", "footer", "footer_icon", "image_url", "pretext", "text", "thumb_url", "title", "title_link", "ts", } fields: Sequence[AttachmentField] MarkdownFields = {"fields", "pretext", "text"} footer_max_length = 300 def __init__( self, *, text: str, fallback: Optional[str] = None, fields: Optional[Sequence[AttachmentField]] = None, color: Optional[str] = None, markdown_in: Optional[Sequence[str]] = None, title: Optional[str] = None, title_link: Optional[str] = None, pretext: Optional[str] = None, author_name: Optional[str] = None, author_subname: Optional[str] = None, author_link: Optional[str] = None, author_icon: Optional[str] = None, image_url: Optional[str] = None, thumb_url: Optional[str] = None, footer: Optional[str] = None, footer_icon: Optional[str] = None, ts: Optional[int] = None, ): """ A supplemental object that will display after the rest of the message. Considered legacy - recommended replacement is to use message blocks instead. https://docs.slack.dev/legacy/legacy-messaging/legacy-secondary-message-attachments#fields Args: text: The main body text of the attachment. It can be formatted as plain text, or with markdown by including it in the markdown_in parameter. The content will automatically collapse if it contains 700+ characters or 5+ linebreaks, and will display a "Show more..." link to expand the content. fallback: A plain text summary of the attachment used in clients that don't show formatted text (eg. IRC, mobile notifications). fields: An array of AttachmentField objects that get displayed in a table-like way. For best results, include no more than 2-3 field objects. color: Changes the color of the border on the left side of this attachment from the default gray. Can be any hex color code (eg. #439FE0) markdown_in: An array of field names that should be formatted by markdown syntax - allowed values: "pretext", "text", "fields" title: Large title text near the top of the attachment. title_link: A valid URL that turns the title text into a hyperlink. pretext: Text that appears above the message attachment block. It can be formatted as plain text, or with markdown by including it in the markdown_in parameter. author_name: Small text used to display the author's name. author_subname: Small text used to display the author's sub name. author_link: A valid URL that will hyperlink the author_name text. Will only work if author_name is present. author_icon: A valid URL that displays a small 16px by 16px image to the left of the author_name text. Will only work if author_name is present. image_url: A valid URL to an image file that will be displayed at the bottom of the attachment. We support GIF, JPEG, PNG, and BMP formats. Large images will be resized to a maximum width of 360px or a maximum height of 500px, while still maintaining the original aspect ratio. Cannot be used with thumb_url. thumb_url: A valid URL to an image file that will be displayed as a thumbnail on the right side of a message attachment. We currently support the following formats: GIF, JPEG, PNG, and BMP. The thumbnail's longest dimension will be scaled down to 75px while maintaining the aspect ratio of the image. The filesize of the image must also be less than 500 KB. For best results, please use images that are already 75px by 75px. footer: Some brief text to help contextualize and identify an attachment. Limited to 300 characters, and may be truncated further when displayed to users in environments with limited screen real estate. footer_icon: A valid URL to an image file that will be displayed beside the footer text. Will only work if footer is present. We'll render what you provide at 16px by 16px. It's best to use an image that is similarly sized. ts: An integer Unix timestamp that is used to related your attachment to a specific time. The attachment will display the additional timestamp value as part of the attachment's footer. Your message's timestamp will be displayed in varying ways, depending on how far in the past or future it is, relative to the present. Form factors, like mobile versus desktop may also transform its rendered appearance. """ self.text = text self.title = title self.fallback = fallback self.pretext = pretext self.title_link = title_link self.color = color self.author_name = author_name self.author_subname = author_subname self.author_link = author_link self.author_icon = author_icon self.image_url = image_url self.thumb_url = thumb_url self.footer = footer self.footer_icon = footer_icon self.ts = ts self.fields = fields or [] self.markdown_in = markdown_in or [] @JsonValidator(f"footer attribute cannot exceed {footer_max_length} characters") def footer_length(self) -> bool: return self.footer is None or len(self.footer) <= self.footer_max_length @JsonValidator("ts attribute cannot be present if footer attribute is absent") def ts_without_footer(self) -> bool: return self.ts is None or self.footer is not None @EnumValidator("markdown_in", MarkdownFields) def markdown_in_valid(self): return not self.markdown_in or all(e in self.MarkdownFields for e in self.markdown_in) @JsonValidator("color attribute must be 'good', 'warning', 'danger', or a hex color code") def color_valid(self) -> bool: return ( self.color is None or self.color in SeededColors or re.match("^#(?:[0-9A-F]{2}){3}$", self.color, re.IGNORECASE) is not None ) @JsonValidator("image_url attribute cannot be present if thumb_url is populated") def image_url_and_thumb_url_populated(self) -> bool: return self.image_url is None or self.thumb_url is None @JsonValidator("name must be present if link is present") def author_link_without_author_name(self) -> bool: return self.author_link is None or self.author_name is not None @JsonValidator("icon must be present if link is present") def author_link_without_author_icon(self) -> bool: return self.author_link is None or self.author_icon is not None def to_dict(self) -> dict: json = super().to_dict() if self.fields is not None: json["fields"] = extract_json(self.fields) if self.markdown_in: json["mrkdwn_in"] = self.markdown_in return json ``` The base class for JSON serializable class objects A supplemental object that will display after the rest of the message. Considered legacy - recommended replacement is to use message blocks instead. [https://docs.slack.dev/legacy/legacy-messaging/legacy-secondary-message-attachments#fields](https://docs.slack.dev/legacy/legacy-messaging/legacy-secondary-message-attachments#fields) ## Args **`text`** The main body text of the attachment. It can be formatted as plain text, or with markdown by including it in the markdown\_in parameter. The content will automatically collapse if it contains 700+ characters or 5+ linebreaks, and will display a "Show more…" link to expand the content. **`fallback`** A plain text summary of the attachment used in clients that don't show formatted text (eg. IRC, mobile notifications). **`fields`** An array of AttachmentField objects that get displayed in a table-like way. For best results, include no more than 2-3 field objects. **`color`** Changes the color of the border on the left side of this attachment from the default gray. Can be any hex color code (eg. #439FE0) **`markdown_in`** An array of field names that should be formatted by markdown syntax - allowed values: "pretext", "text", "fields" **`title`** Large title text near the top of the attachment. **`title_link`** A valid URL that turns the title text into a hyperlink. **`pretext`** Text that appears above the message attachment block. It can be formatted as plain text, or with markdown by including it in the markdown\_in parameter. **`author_name`** Small text used to display the author's name. **`author_subname`** Small text used to display the author's sub name. **`author_link`** A valid URL that will hyperlink the author\_name text. Will only work if author\_name is present. **`author_icon`** A valid URL that displays a small 16px by 16px image to the left of the author\_name text. Will only work if author\_name is present. **`image_url`** A valid URL to an image file that will be displayed at the bottom of the attachment. We support GIF, JPEG, PNG, and BMP formats. Large images will be resized to a maximum width of 360px or a maximum height of 500px, while still maintaining the original aspect ratio. Cannot be used with thumb\_url. **`thumb_url`** A valid URL to an image file that will be displayed as a thumbnail on the right side of a message attachment. We currently support the following formats: GIF, JPEG, PNG, and BMP. The thumbnail's longest dimension will be scaled down to 75px while maintaining the aspect ratio of the image. The filesize of the image must also be less than 500 KB. For best results, please use images that are already 75px by 75px. **`footer`** Some brief text to help contextualize and identify an attachment. Limited to 300 characters, and may be truncated further when displayed to users in environments with limited screen real estate. **`footer_icon`** A valid URL to an image file that will be displayed beside the footer text. Will only work if footer is present. We'll render what you provide at 16px by 16px. It's best to use an image that is similarly sized. **`ts`** An integer Unix timestamp that is used to related your attachment to a specific time. The attachment will display the additional timestamp value as part of the attachment's footer. Your message's timestamp will be displayed in varying ways, depending on how far in the past or future it is, relative to the present. Form factors, like mobile versus desktop may also transform its rendered appearance. ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Subclasses * [BlockAttachment](#slack_sdk.models.attachments.BlockAttachment "slack_sdk.models.attachments.BlockAttachment") * [InteractiveAttachment](#slack_sdk.models.attachments.InteractiveAttachment "slack_sdk.models.attachments.InteractiveAttachment") ### Class variables `var MarkdownFields` The type of the None singleton. `var attributes` The type of the None singleton. `var fields : Sequence[[AttachmentField](#slack_sdk.models.attachments.AttachmentField "slack_sdk.models.attachments.AttachmentField")]` The type of the None singleton. `var footer_max_length` The type of the None singleton. ### Methods `def author_link_without_author_icon(self) ‑> bool` Expand source code ``` @JsonValidator("icon must be present if link is present") def author_link_without_author_icon(self) -> bool: return self.author_link is None or self.author_icon is not None ``` `def author_link_without_author_name(self) ‑> bool` Expand source code ``` @JsonValidator("name must be present if link is present") def author_link_without_author_name(self) -> bool: return self.author_link is None or self.author_name is not None ``` `def color_valid(self) ‑> bool` Expand source code ``` @JsonValidator("color attribute must be 'good', 'warning', 'danger', or a hex color code") def color_valid(self) -> bool: return ( self.color is None or self.color in SeededColors or re.match("^#(?:[0-9A-F]{2}){3}$", self.color, re.IGNORECASE) is not None ) ``` `def footer_length(self) ‑> bool` Expand source code ``` @JsonValidator(f"footer attribute cannot exceed {footer_max_length} characters") def footer_length(self) -> bool: return self.footer is None or len(self.footer) <= self.footer_max_length ``` `def image_url_and_thumb_url_populated(self) ‑> bool` Expand source code ``` @JsonValidator("image_url attribute cannot be present if thumb_url is populated") def image_url_and_thumb_url_populated(self) -> bool: return self.image_url is None or self.thumb_url is None ``` `def markdown_in_valid(self)` Expand source code ``` @EnumValidator("markdown_in", MarkdownFields) def markdown_in_valid(self): return not self.markdown_in or all(e in self.MarkdownFields for e in self.markdown_in) ``` `def ts_without_footer(self) ‑> bool` Expand source code ``` @JsonValidator("ts attribute cannot be present if footer attribute is absent") def ts_without_footer(self) -> bool: return self.ts is None or self.footer is not None ``` ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class AttachmentField (*, title: str | None = None, value: str | None = None, short: bool = True)` Expand source code ``` class AttachmentField(JsonObject): attributes = {"short", "title", "value"} def __init__( self, *, title: Optional[str] = None, value: Optional[str] = None, short: bool = True, ): self.title = title self.value = value self.short = short ``` The base class for JSON serializable class objects ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class BlockAttachment (*, blocks: Sequence[[Block](../blocks/blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")], color: str | None = None, fallback: str | None = None)` Expand source code ``` class BlockAttachment(Attachment): blocks: List[Block] @property def attributes(self): return super().attributes.union({"blocks", "color"}) def __init__( self, *, blocks: Sequence[Block], color: Optional[str] = None, fallback: Optional[str] = None, ): """ A bridge between legacy attachments and Block Kit formatting - pass a list of Block objects directly to this attachment. https://docs.slack.dev/legacy/legacy-messaging/legacy-secondary-message-attachments#fields Args: blocks: a sequence of Block objects color: Changes the color of the border on the left side of this attachment from the default gray. Can either be one of "good" (green), "warning" (yellow), "danger" (red), or any hex color code (eg. #439FE0) fallback: fallback text """ super().__init__(text="", fallback=fallback, color=color) self.blocks = list(blocks) @JsonValidator("fields attribute cannot be populated on BlockAttachment") def fields_attribute_absent(self) -> bool: return not self.fields def to_dict(self) -> dict: json = super().to_dict() json.update({"blocks": extract_json(self.blocks)}) del json["fields"] # cannot supply fields and blocks at the same time return json ``` The base class for JSON serializable class objects A bridge between legacy attachments and Block Kit formatting - pass a list of Block objects directly to this attachment. [https://docs.slack.dev/legacy/legacy-messaging/legacy-secondary-message-attachments#fields](https://docs.slack.dev/legacy/legacy-messaging/legacy-secondary-message-attachments#fields) ## Args **`blocks`** a sequence of Block objects **`color`** Changes the color of the border on the left side of this attachment from the default gray. Can either be one of "good" (green), "warning" (yellow), "danger" (red), or any hex color code (eg. #439FE0) **`fallback`** fallback text ### Ancestors * [Attachment](#slack_sdk.models.attachments.Attachment "slack_sdk.models.attachments.Attachment") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var blocks : List[[Block](../blocks/blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")]` The type of the None singleton. ### Instance variables `prop attributes` Expand source code ``` @property def attributes(self): return super().attributes.union({"blocks", "color"}) ``` Build an unordered collection of unique elements. ### Methods `def fields_attribute_absent(self) ‑> bool` Expand source code ``` @JsonValidator("fields attribute cannot be populated on BlockAttachment") def fields_attribute_absent(self) -> bool: return not self.fields ``` ### Inherited members * `**[Attachment](#slack_sdk.models.attachments.Attachment "slack_sdk.models.attachments.Attachment")**`: * `[MarkdownFields](#slack_sdk.models.attachments.Attachment.MarkdownFields "slack_sdk.models.attachments.Attachment.MarkdownFields")` * `[fields](#slack_sdk.models.attachments.Attachment.fields "slack_sdk.models.attachments.Attachment.fields")` * `[footer_max_length](#slack_sdk.models.attachments.Attachment.footer_max_length "slack_sdk.models.attachments.Attachment.footer_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.attachments.Attachment.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.attachments.Attachment.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.attachments.Attachment.validate_json")` `class InteractiveAttachment (*, actions: Sequence[[Action](#slack_sdk.models.attachments.Action "slack_sdk.models.attachments.Action")], callback_id: str, text: str, fallback: str | None = None, fields: Sequence[[AttachmentField](#slack_sdk.models.attachments.AttachmentField "slack_sdk.models.attachments.AttachmentField")] | None = None, color: str | None = None, markdown_in: Sequence[str] | None = None, title: str | None = None, title_link: str | None = None, pretext: str | None = None, author_name: str | None = None, author_subname: str | None = None, author_link: str | None = None, author_icon: str | None = None, image_url: str | None = None, thumb_url: str | None = None, footer: str | None = None, footer_icon: str | None = None, ts: int | None = None)` Expand source code ``` class InteractiveAttachment(Attachment): @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"callback_id"}) actions_max_length = 5 def __init__( self, *, actions: Sequence[Action], callback_id: str, text: str, fallback: Optional[str] = None, fields: Optional[Sequence[AttachmentField]] = None, color: Optional[str] = None, markdown_in: Optional[Sequence[str]] = None, title: Optional[str] = None, title_link: Optional[str] = None, pretext: Optional[str] = None, author_name: Optional[str] = None, author_subname: Optional[str] = None, author_link: Optional[str] = None, author_icon: Optional[str] = None, image_url: Optional[str] = None, thumb_url: Optional[str] = None, footer: Optional[str] = None, footer_icon: Optional[str] = None, ts: Optional[int] = None, ): """ An Attachment, but designed to contain interactive Actions Considered legacy - recommended replacement is to use message blocks instead. https://docs.slack.dev/legacy/legacy-messaging/legacy-interactive-message-field-guide/#attachment_fields https://docs.slack.dev/legacy/legacy-messaging/legacy-secondary-message-attachments#fields Args: actions: A collection of Action objects to include in the attachment. Cannot exceed 5 elements. callback_id: The ID used to identify this attachment. Will be part of the payload sent back to your application. text: The main body text of the attachment. It can be formatted as plain text, or with markdown by including it in the markdown_in parameter. The content will automatically collapse if it contains 700+ characters or 5+ linebreaks, and will display a "Show more..." link to expand the content. fallback: A plain text summary of the attachment used in clients that don't show formatted text (eg. IRC, mobile notifications). fields: An array of AttachmentField objects that get displayed in a table-like way. For best results, include no more than 2-3 field objects. color: Changes the color of the border on the left side of this attachment from the default gray. Can either be one of "good" (green), "warning" (yellow), "danger" (red), or any hex color code (eg. #439FE0) markdown_in: An array of field names that should be formatted by markdown syntax - allowed values: "pretext", "text", "fields" title: Large title text near the top of the attachment. title_link: A valid URL that turns the title text into a hyperlink. pretext: Text that appears above the message attachment block. It can be formatted as plain text, or with markdown by including it in the markdown_in parameter. author_name: Small text used to display the author's name. author_subname: Small text used to display the author's sub name. author_link: A valid URL that will hyperlink the author_name text. Will only work if author_name is present. author_icon: A valid URL that displays a small 16px by 16px image to the left of the author_name text. Will only work if author_name is present. image_url: A valid URL to an image file that will be displayed at the bottom of the attachment. We support GIF, JPEG, PNG, and BMP formats. Large images will be resized to a maximum width of 360px or a maximum height of 500px, while still maintaining the original aspect ratio. Cannot be used with thumb_url. thumb_url: A valid URL to an image file that will be displayed as a thumbnail on the right side of a message attachment. We currently support the following formats: GIF, JPEG, PNG, and BMP. The thumbnail's longest dimension will be scaled down to 75px while maintaining the aspect ratio of the image. The filesize of the image must also be less than 500 KB. For best results, please use images that are already 75px by 75px. footer: Some brief text to help contextualize and identify an attachment. Limited to 300 characters, and may be truncated further when displayed to users in environments with limited screen real estate. footer_icon: A valid URL to an image file that will be displayed beside the footer text. Will only work if footer is present. We'll render what you provide at 16px by 16px. It's best to use an image that is similarly sized. ts: An integer Unix timestamp that is used to related your attachment to a specific time. The attachment will display the additional timestamp value as part of the attachment's footer. Your message's timestamp will be displayed in varying ways, depending on how far in the past or future it is, relative to the present. Form factors, like mobile versus desktop may also transform its rendered appearance. """ super().__init__( text=text, title=title, fallback=fallback, fields=fields, pretext=pretext, title_link=title_link, color=color, author_name=author_name, author_subname=author_subname, author_link=author_link, author_icon=author_icon, image_url=image_url, thumb_url=thumb_url, footer=footer, footer_icon=footer_icon, ts=ts, markdown_in=markdown_in, ) self.callback_id = callback_id self.actions = actions or [] @JsonValidator(f"actions attribute cannot exceed {actions_max_length} elements") def actions_length(self) -> bool: return len(self.actions) <= self.actions_max_length def to_dict(self) -> dict: json = super().to_dict() json["actions"] = extract_json(self.actions) return json ``` The base class for JSON serializable class objects An Attachment, but designed to contain interactive Actions Considered legacy - recommended replacement is to use message blocks instead. [https://docs.slack.dev/legacy/legacy-messaging/legacy-interactive-message-field-guide/#attachment\_fields](https://docs.slack.dev/legacy/legacy-messaging/legacy-interactive-message-field-guide/#attachment_fields) [https://docs.slack.dev/legacy/legacy-messaging/legacy-secondary-message-attachments#fields](https://docs.slack.dev/legacy/legacy-messaging/legacy-secondary-message-attachments#fields) ## Args **`actions`** A collection of Action objects to include in the attachment. Cannot exceed 5 elements. **`callback_id`** The ID used to identify this attachment. Will be part of the payload sent back to your application. **`text`** The main body text of the attachment. It can be formatted as plain text, or with markdown by including it in the markdown\_in parameter. The content will automatically collapse if it contains 700+ characters or 5+ linebreaks, and will display a "Show more…" link to expand the content. **`fallback`** A plain text summary of the attachment used in clients that don't show formatted text (eg. IRC, mobile notifications). **`fields`** An array of AttachmentField objects that get displayed in a table-like way. For best results, include no more than 2-3 field objects. **`color`** Changes the color of the border on the left side of this attachment from the default gray. Can either be one of "good" (green), "warning" (yellow), "danger" (red), or any hex color code (eg. #439FE0) **`markdown_in`** An array of field names that should be formatted by markdown syntax - allowed values: "pretext", "text", "fields" **`title`** Large title text near the top of the attachment. **`title_link`** A valid URL that turns the title text into a hyperlink. **`pretext`** Text that appears above the message attachment block. It can be formatted as plain text, or with markdown by including it in the markdown\_in parameter. **`author_name`** Small text used to display the author's name. **`author_subname`** Small text used to display the author's sub name. **`author_link`** A valid URL that will hyperlink the author\_name text. Will only work if author\_name is present. **`author_icon`** A valid URL that displays a small 16px by 16px image to the left of the author\_name text. Will only work if author\_name is present. **`image_url`** A valid URL to an image file that will be displayed at the bottom of the attachment. We support GIF, JPEG, PNG, and BMP formats. Large images will be resized to a maximum width of 360px or a maximum height of 500px, while still maintaining the original aspect ratio. Cannot be used with thumb\_url. **`thumb_url`** A valid URL to an image file that will be displayed as a thumbnail on the right side of a message attachment. We currently support the following formats: GIF, JPEG, PNG, and BMP. The thumbnail's longest dimension will be scaled down to 75px while maintaining the aspect ratio of the image. The filesize of the image must also be less than 500 KB. For best results, please use images that are already 75px by 75px. **`footer`** Some brief text to help contextualize and identify an attachment. Limited to 300 characters, and may be truncated further when displayed to users in environments with limited screen real estate. **`footer_icon`** A valid URL to an image file that will be displayed beside the footer text. Will only work if footer is present. We'll render what you provide at 16px by 16px. It's best to use an image that is similarly sized. **`ts`** An integer Unix timestamp that is used to related your attachment to a specific time. The attachment will display the additional timestamp value as part of the attachment's footer. Your message's timestamp will be displayed in varying ways, depending on how far in the past or future it is, relative to the present. Form factors, like mobile versus desktop may also transform its rendered appearance. ### Ancestors * [Attachment](#slack_sdk.models.attachments.Attachment "slack_sdk.models.attachments.Attachment") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var actions_max_length` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"callback_id"}) ``` Build an unordered collection of unique elements. ### Methods `def actions_length(self) ‑> bool` Expand source code ``` @JsonValidator(f"actions attribute cannot exceed {actions_max_length} elements") def actions_length(self) -> bool: return len(self.actions) <= self.actions_max_length ``` ### Inherited members * `**[Attachment](#slack_sdk.models.attachments.Attachment "slack_sdk.models.attachments.Attachment")**`: * `[MarkdownFields](#slack_sdk.models.attachments.Attachment.MarkdownFields "slack_sdk.models.attachments.Attachment.MarkdownFields")` * `[fields](#slack_sdk.models.attachments.Attachment.fields "slack_sdk.models.attachments.Attachment.fields")` * `[footer_max_length](#slack_sdk.models.attachments.Attachment.footer_max_length "slack_sdk.models.attachments.Attachment.footer_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.attachments.Attachment.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.attachments.Attachment.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.attachments.Attachment.validate_json")` --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/models/blocks # Module slack_sdk.models.blocks Block Kit data model objects To learn more about Block Kit, please check the following resources and tools: * [https://docs.slack.dev/block-kit/](https://docs.slack.dev/block-kit/) * [https://docs.slack.dev/reference/block-kit/blocks](https://docs.slack.dev/reference/block-kit/blocks) * [https://app.slack.com/block-kit-builder](https://app.slack.com/block-kit-builder) ## Sub-modules `[slack_sdk.models.blocks.basic_components](basic_components.html "slack_sdk.models.blocks.basic_components")` `[slack_sdk.models.blocks.block_elements](block_elements.html "slack_sdk.models.blocks.block_elements")` `[slack_sdk.models.blocks.blocks](blocks.html "slack_sdk.models.blocks.blocks")` ## Classes `class ActionsBlock (*, elements: Sequence[dict | [InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement")], block_id: str | None = None, **others: dict)` Expand source code ``` class ActionsBlock(Block): type = "actions" elements_max_length = 25 @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"elements"}) def __init__( self, *, elements: Sequence[Union[dict, InteractiveElement]], block_id: Optional[str] = None, **others: dict, ): """A block that is used to hold interactive elements. https://docs.slack.dev/reference/block-kit/blocks/actions-block Args: elements (required): An array of interactive element objects - buttons, select menus, overflow menus, or date pickers. There is a maximum of 25 elements in each action block. block_id: A string acting as a unique identifier for a block. If not specified, a block_id will be generated. You can use this block_id when you receive an interaction payload to identify the source of the action. Maximum length for this field is 255 characters. block_id should be unique for each message and each iteration of a message. If a message is updated, use a new block_id. """ super().__init__(type=self.type, block_id=block_id) show_unknown_key_warning(self, others) self.elements = BlockElement.parse_all(elements) @JsonValidator(f"elements attribute cannot exceed {elements_max_length} elements") def _validate_elements_length(self): return self.elements is None or len(self.elements) <= self.elements_max_length ``` Blocks are a series of components that can be combined to create visually rich and compellingly interactive messages. [https://docs.slack.dev/reference/block-kit/blocks](https://docs.slack.dev/reference/block-kit/blocks) A block that is used to hold interactive elements. [https://docs.slack.dev/reference/block-kit/blocks/actions-block](https://docs.slack.dev/reference/block-kit/blocks/actions-block) ## Args **`elements`** : `required` An array of interactive element objects - buttons, select menus, overflow menus, or date pickers. There is a maximum of 25 elements in each action block. **`block_id`** A string acting as a unique identifier for a block. If not specified, a block\_id will be generated. You can use this block\_id when you receive an interaction payload to identify the source of the action. Maximum length for this field is 255 characters. block\_id should be unique for each message and each iteration of a message. If a message is updated, use a new block\_id. ### Ancestors * [Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var elements_max_length` The type of the None singleton. `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"elements"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")**`: * `[block_id_max_length](blocks.html#slack_sdk.models.blocks.blocks.Block.block_id_max_length "slack_sdk.models.blocks.blocks.Block.block_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.blocks.Block.get_non_null_attributes")` * `[logger](blocks.html#slack_sdk.models.blocks.blocks.Block.logger "slack_sdk.models.blocks.blocks.Block.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.blocks.Block.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.blocks.Block.validate_json")` `class AlertBlock (*, text: str | dict | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject"), level: str | None = None, block_id: str | None = None, **others: dict)` Expand source code ``` class AlertBlock(Block): type = "alert" valid_levels = {"default", "info", "warning", "error", "success"} @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"text", "level"}) def __init__( self, *, text: Union[str, dict, TextObject], level: Optional[str] = None, block_id: Optional[str] = None, **others: dict, ): """Displays alerts, warnings, and informational messages. https://docs.slack.dev/reference/block-kit/blocks/alert-block Args: text (required): The alert message, using plain_text or mrkdwn formatting. level: One of "default", "info", "warning", "error", or "success". Will be "default" if omitted. block_id: A unique identifier for a block. If not specified, a block_id will be generated. """ super().__init__(type=self.type, block_id=block_id) show_unknown_key_warning(self, others) self.text = TextObject.parse(text) self.level = level @JsonValidator("text attribute must be specified") def _validate_text(self): return self.text is not None @JsonValidator("level must be a valid value (default, info, warning, error, success)") def _validate_level(self): return self.level is None or self.level in self.valid_levels ``` Blocks are a series of components that can be combined to create visually rich and compellingly interactive messages. [https://docs.slack.dev/reference/block-kit/blocks](https://docs.slack.dev/reference/block-kit/blocks) Displays alerts, warnings, and informational messages. [https://docs.slack.dev/reference/block-kit/blocks/alert-block](https://docs.slack.dev/reference/block-kit/blocks/alert-block) ## Args **`text`** : `required` The alert message, using plain\_text or mrkdwn formatting. **`level`** One of "default", "info", "warning", "error", or "success". Will be "default" if omitted. **`block_id`** A unique identifier for a block. If not specified, a block\_id will be generated. ### Ancestors * [Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. `var valid_levels` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"text", "level"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")**`: * `[block_id_max_length](blocks.html#slack_sdk.models.blocks.blocks.Block.block_id_max_length "slack_sdk.models.blocks.blocks.Block.block_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.blocks.Block.get_non_null_attributes")` * `[logger](blocks.html#slack_sdk.models.blocks.blocks.Block.logger "slack_sdk.models.blocks.blocks.Block.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.blocks.Block.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.blocks.Block.validate_json")` `class Block (*, type: str | None = None, subtype: str | None = None, block_id: str | None = None)` Expand source code ``` class Block(JsonObject): """Blocks are a series of components that can be combined to create visually rich and compellingly interactive messages. https://docs.slack.dev/reference/block-kit/blocks """ attributes = {"block_id", "type"} block_id_max_length = 255 logger = logging.getLogger(__name__) def _subtype_warning(self): warnings.warn( "subtype is deprecated since slackclient 2.6.0, use type instead", DeprecationWarning, ) @property def subtype(self) -> Optional[str]: return self.type def __init__( self, *, type: Optional[str] = None, subtype: Optional[str] = None, # deprecated block_id: Optional[str] = None, ): if subtype: self._subtype_warning() self.type = type if type else subtype self.block_id = block_id self.color = None @JsonValidator(f"block_id cannot exceed {block_id_max_length} characters") def _validate_block_id_length(self): return self.block_id is None or len(self.block_id) <= self.block_id_max_length @classmethod def parse(cls, block: Union[dict, "Block"]) -> Optional["Block"]: if block is None: return None elif isinstance(block, Block): return block else: if "type" in block: type = block["type"] if type == SectionBlock.type: return SectionBlock(**block) elif type == DividerBlock.type: return DividerBlock(**block) elif type == ImageBlock.type: return ImageBlock(**block) elif type == ActionsBlock.type: return ActionsBlock(**block) elif type == ContextBlock.type: return ContextBlock(**block) elif type == ContextActionsBlock.type: return ContextActionsBlock(**block) elif type == InputBlock.type: return InputBlock(**block) elif type == FileBlock.type: return FileBlock(**block) elif type == CallBlock.type: return CallBlock(**block) elif type == HeaderBlock.type: return HeaderBlock(**block) elif type == MarkdownBlock.type: return MarkdownBlock(**block) elif type == VideoBlock.type: return VideoBlock(**block) elif type == RichTextBlock.type: return RichTextBlock(**block) elif type == TableBlock.type: return TableBlock(**block) elif type == TaskCardBlock.type: return TaskCardBlock(**block) elif type == PlanBlock.type: return PlanBlock(**block) elif type == CardBlock.type: return CardBlock(**block) elif type == AlertBlock.type: return AlertBlock(**block) elif type == CarouselBlock.type: return CarouselBlock(**block) else: cls.logger.warning(f"Unknown block detected and skipped ({block})") return None else: cls.logger.warning(f"Unknown block detected and skipped ({block})") return None @classmethod def parse_all(cls, blocks: Optional[Sequence[Union[dict, "Block"]]]) -> List["Block"]: return [cls.parse(b) for b in blocks or []] # type: ignore[misc] ``` Blocks are a series of components that can be combined to create visually rich and compellingly interactive messages. [https://docs.slack.dev/reference/block-kit/blocks](https://docs.slack.dev/reference/block-kit/blocks) ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Subclasses * [ActionsBlock](blocks.html#slack_sdk.models.blocks.blocks.ActionsBlock "slack_sdk.models.blocks.blocks.ActionsBlock") * [AlertBlock](blocks.html#slack_sdk.models.blocks.blocks.AlertBlock "slack_sdk.models.blocks.blocks.AlertBlock") * [CallBlock](blocks.html#slack_sdk.models.blocks.blocks.CallBlock "slack_sdk.models.blocks.blocks.CallBlock") * [CardBlock](blocks.html#slack_sdk.models.blocks.blocks.CardBlock "slack_sdk.models.blocks.blocks.CardBlock") * [CarouselBlock](blocks.html#slack_sdk.models.blocks.blocks.CarouselBlock "slack_sdk.models.blocks.blocks.CarouselBlock") * [ContextActionsBlock](blocks.html#slack_sdk.models.blocks.blocks.ContextActionsBlock "slack_sdk.models.blocks.blocks.ContextActionsBlock") * [ContextBlock](blocks.html#slack_sdk.models.blocks.blocks.ContextBlock "slack_sdk.models.blocks.blocks.ContextBlock") * [DividerBlock](blocks.html#slack_sdk.models.blocks.blocks.DividerBlock "slack_sdk.models.blocks.blocks.DividerBlock") * [FileBlock](blocks.html#slack_sdk.models.blocks.blocks.FileBlock "slack_sdk.models.blocks.blocks.FileBlock") * [HeaderBlock](blocks.html#slack_sdk.models.blocks.blocks.HeaderBlock "slack_sdk.models.blocks.blocks.HeaderBlock") * [ImageBlock](blocks.html#slack_sdk.models.blocks.blocks.ImageBlock "slack_sdk.models.blocks.blocks.ImageBlock") * [InputBlock](blocks.html#slack_sdk.models.blocks.blocks.InputBlock "slack_sdk.models.blocks.blocks.InputBlock") * [MarkdownBlock](blocks.html#slack_sdk.models.blocks.blocks.MarkdownBlock "slack_sdk.models.blocks.blocks.MarkdownBlock") * [PlanBlock](blocks.html#slack_sdk.models.blocks.blocks.PlanBlock "slack_sdk.models.blocks.blocks.PlanBlock") * [RichTextBlock](blocks.html#slack_sdk.models.blocks.blocks.RichTextBlock "slack_sdk.models.blocks.blocks.RichTextBlock") * [SectionBlock](blocks.html#slack_sdk.models.blocks.blocks.SectionBlock "slack_sdk.models.blocks.blocks.SectionBlock") * [TableBlock](blocks.html#slack_sdk.models.blocks.blocks.TableBlock "slack_sdk.models.blocks.blocks.TableBlock") * [TaskCardBlock](blocks.html#slack_sdk.models.blocks.blocks.TaskCardBlock "slack_sdk.models.blocks.blocks.TaskCardBlock") * [VideoBlock](blocks.html#slack_sdk.models.blocks.blocks.VideoBlock "slack_sdk.models.blocks.blocks.VideoBlock") ### Class variables `var attributes` The type of the None singleton. `var block_id_max_length` The type of the None singleton. `var logger` The type of the None singleton. ### Static methods `def parse(block: dict | ForwardRef('[Block](#slack_sdk.models.blocks.Block "slack_sdk.models.blocks.Block")')) ‑> [Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block") | None` `def parse_all(blocks: Sequence[dict | ForwardRef('[Block](#slack_sdk.models.blocks.Block "slack_sdk.models.blocks.Block")')] | None) ‑> List[[Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")]` ### Instance variables `prop subtype : str | None` Expand source code ``` @property def subtype(self) -> Optional[str]: return self.type ``` ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class BlockElement (*, type: str | None = None, subtype: str | None = None, **others: dict)` Expand source code ``` class BlockElement(JsonObject, metaclass=ABCMeta): """Block Elements are things that exists inside of your Blocks. https://docs.slack.dev/reference/block-kit/block-elements/ """ attributes = {"type"} logger = logging.getLogger(__name__) def _subtype_warning(self): warnings.warn( "subtype is deprecated since slackclient 2.6.0, use type instead", DeprecationWarning, ) @property def subtype(self) -> Optional[str]: return self.type def __init__( self, *, type: Optional[str] = None, subtype: Optional[str] = None, **others: dict, ): if subtype: self._subtype_warning() self.type = type if type else subtype show_unknown_key_warning(self, others) @classmethod def parse(cls, block_element: Union[dict, "BlockElement"]) -> Optional[Union["BlockElement", TextObject]]: if block_element is None: return None elif isinstance(block_element, dict): if "type" in block_element: d = copy.copy(block_element) t = d.pop("type") for subclass in cls._get_sub_block_elements(): if t == subclass.type: return subclass(**d) if t == PlainTextObject.type: return PlainTextObject(**d) elif t == MarkdownTextObject.type: return MarkdownTextObject(**d) elif isinstance(block_element, (TextObject, BlockElement)): return block_element cls.logger.warning(f"Unknown element detected and skipped ({block_element})") return None @classmethod def parse_all( cls, block_elements: Sequence[Union[dict, "BlockElement", TextObject]] ) -> List[Union["BlockElement", TextObject]]: return [cls.parse(e) for e in block_elements or []] # type: ignore[arg-type, misc] @classmethod def _get_sub_block_elements(cls: Type["BlockElement"]) -> Iterator[Type["BlockElement"]]: for subclass in cls.__subclasses__(): if hasattr(subclass, "type"): yield subclass yield from subclass._get_sub_block_elements() ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Subclasses * [ImageElement](block_elements.html#slack_sdk.models.blocks.block_elements.ImageElement "slack_sdk.models.blocks.block_elements.ImageElement") * [InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement") * [RichTextElement](block_elements.html#slack_sdk.models.blocks.block_elements.RichTextElement "slack_sdk.models.blocks.block_elements.RichTextElement") * [UrlSourceElement](block_elements.html#slack_sdk.models.blocks.block_elements.UrlSourceElement "slack_sdk.models.blocks.block_elements.UrlSourceElement") ### Class variables `var attributes` The type of the None singleton. `var logger` The type of the None singleton. ### Static methods `def parse(block_element: dict | ForwardRef('[BlockElement](#slack_sdk.models.blocks.BlockElement "slack_sdk.models.blocks.BlockElement")')) ‑> [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject") | None` `def parse_all(block_elements: Sequence[dict | ForwardRef('[BlockElement](#slack_sdk.models.blocks.BlockElement "slack_sdk.models.blocks.BlockElement")') | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject")]) ‑> List[[BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject")]` ### Instance variables `prop subtype : str | None` Expand source code ``` @property def subtype(self) -> Optional[str]: return self.type ``` ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class ButtonElement (*, text: str | dict | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject"), action_id: str | None = None, url: str | None = None, value: str | None = None, style: str | None = None, confirm: dict | [ConfirmObject](basic_components.html#slack_sdk.models.blocks.basic_components.ConfirmObject "slack_sdk.models.blocks.basic_components.ConfirmObject") | None = None, accessibility_label: str | None = None, **others: dict)` Expand source code ``` class ButtonElement(InteractiveElement): type = "button" text_max_length = 75 url_max_length = 3000 value_max_length = 2000 @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"text", "url", "value", "style", "confirm", "accessibility_label"}) def __init__( self, *, text: Union[str, dict, TextObject], action_id: Optional[str] = None, url: Optional[str] = None, value: Optional[str] = None, style: Optional[str] = None, # primary, danger confirm: Optional[Union[dict, ConfirmObject]] = None, accessibility_label: Optional[str] = None, **others: dict, ): """An interactive element that inserts a button. The button can be a trigger for anything from opening a simple link to starting a complex workflow. https://docs.slack.dev/reference/block-kit/block-elements/button-element/ Args: text (required): A text object that defines the button's text. Can only be of type: plain_text. Maximum length for the text in this field is 75 characters. action_id (required): An identifier for this action. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action_ids in the containing block. Maximum length for this field is 255 characters. url: A URL to load in the user's browser when the button is clicked. Maximum length for this field is 3000 characters. If you're using url, you'll still receive an interaction payload and will need to send an acknowledgement response. value: The value to send along with the interaction payload. Maximum length for this field is 2000 characters. style: Decorates buttons with alternative visual color schemes. Use this option with restraint. "primary" gives buttons a green outline and text, ideal for affirmation or confirmation actions. "primary" should only be used for one button within a set. "danger" gives buttons a red outline and text, and should be used when the action is destructive. Use "danger" even more sparingly than "primary". If you don't include this field, the default button style will be used. confirm: A confirm object that defines an optional confirmation dialog after the button is clicked. accessibility_label: A label for longer descriptive text about a button element. This label will be read out by screen readers instead of the button text object. Maximum length for this field is 75 characters. """ super().__init__(action_id=action_id, type=self.type) show_unknown_key_warning(self, others) # NOTE: default_type=PlainTextObject.type here is only for backward-compatibility with version 2.5.0 self.text = TextObject.parse(text, default_type=PlainTextObject.type) self.url = url self.value = value self.style = style self.confirm = ConfirmObject.parse(confirm) # type: ignore[arg-type] self.accessibility_label = accessibility_label @JsonValidator(f"text attribute cannot exceed {text_max_length} characters") def _validate_text_length(self) -> bool: return self.text is None or self.text.text is None or len(self.text.text) <= self.text_max_length @JsonValidator(f"url attribute cannot exceed {url_max_length} characters") def _validate_url_length(self) -> bool: return self.url is None or len(self.url) <= self.url_max_length @JsonValidator(f"value attribute cannot exceed {value_max_length} characters") def _validate_value_length(self) -> bool: return self.value is None or len(self.value) <= self.value_max_length @EnumValidator("style", ButtonStyles) def _validate_style_valid(self): return self.style is None or self.style in ButtonStyles @JsonValidator(f"accessibility_label attribute cannot exceed {text_max_length} characters") def _validate_accessibility_label_length(self) -> bool: return self.accessibility_label is None or len(self.accessibility_label) <= self.text_max_length ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) An interactive element that inserts a button. The button can be a trigger for anything from opening a simple link to starting a complex workflow. [https://docs.slack.dev/reference/block-kit/block-elements/button-element/](https://docs.slack.dev/reference/block-kit/block-elements/button-element/) ## Args **`text`** : `required` A text object that defines the button's text. Can only be of type: plain\_text. Maximum length for the text in this field is 75 characters. **`action_id`** : `required` An identifier for this action. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action\_ids in the containing block. Maximum length for this field is 255 characters. **`url`** A URL to load in the user's browser when the button is clicked. Maximum length for this field is 3000 characters. If you're using url, you'll still receive an interaction payload and will need to send an acknowledgement response. **`value`** The value to send along with the interaction payload. Maximum length for this field is 2000 characters. **`style`** Decorates buttons with alternative visual color schemes. Use this option with restraint. "primary" gives buttons a green outline and text, ideal for affirmation or confirmation actions. "primary" should only be used for one button within a set. "danger" gives buttons a red outline and text, and should be used when the action is destructive. Use "danger" even more sparingly than "primary". If you don't include this field, the default button style will be used. **`confirm`** A confirm object that defines an optional confirmation dialog after the button is clicked. **`accessibility_label`** A label for longer descriptive text about a button element. This label will be read out by screen readers instead of the button text object. Maximum length for this field is 75 characters. ### Ancestors * [InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement") * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Subclasses * [LinkButtonElement](block_elements.html#slack_sdk.models.blocks.block_elements.LinkButtonElement "slack_sdk.models.blocks.block_elements.LinkButtonElement") ### Class variables `var text_max_length` The type of the None singleton. `var type` The type of the None singleton. `var url_max_length` The type of the None singleton. `var value_max_length` The type of the None singleton. ### Inherited members * `**[InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement")**`: * `[action_id_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement.action_id_max_length "slack_sdk.models.blocks.block_elements.InteractiveElement.action_id_max_length")` * `[attributes](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement.attributes "slack_sdk.models.blocks.block_elements.InteractiveElement.attributes")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.InteractiveElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.InteractiveElement.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.InteractiveElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.InteractiveElement.validate_json")` `class CallBlock (*, call_id: str, api_decoration_available: bool | None = None, call: Dict[str, Dict[str, Any]] | None = None, block_id: str | None = None, **others: dict)` Expand source code ``` class CallBlock(Block): type = "call" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"call_id", "api_decoration_available", "call"}) def __init__( self, *, call_id: str, api_decoration_available: Optional[bool] = None, call: Optional[Dict[str, Dict[str, Any]]] = None, block_id: Optional[str] = None, **others: dict, ): """Displays a call information https://docs.slack.dev/reference/block-kit/blocks#call """ super().__init__(type=self.type, block_id=block_id) show_unknown_key_warning(self, others) self.call_id = call_id self.api_decoration_available = api_decoration_available self.call = call ``` Blocks are a series of components that can be combined to create visually rich and compellingly interactive messages. [https://docs.slack.dev/reference/block-kit/blocks](https://docs.slack.dev/reference/block-kit/blocks) Displays a call information [https://docs.slack.dev/reference/block-kit/blocks#call](https://docs.slack.dev/reference/block-kit/blocks#call) ### Ancestors * [Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"call_id", "api_decoration_available", "call"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")**`: * `[block_id_max_length](blocks.html#slack_sdk.models.blocks.blocks.Block.block_id_max_length "slack_sdk.models.blocks.blocks.Block.block_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.blocks.Block.get_non_null_attributes")` * `[logger](blocks.html#slack_sdk.models.blocks.blocks.Block.logger "slack_sdk.models.blocks.blocks.Block.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.blocks.Block.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.blocks.Block.validate_json")` `class CardBlock (*, block_id: str | None = None, hero_image: str | None = None, icon: str | None = None, title: str | dict | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject") | None = None, subtitle: str | dict | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject") | None = None, body: str | dict | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject") | None = None, actions: Sequence[dict | [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement")] | None = None, **others: dict)` Expand source code ``` class CardBlock(Block): type = "card" title_max_length = 150 subtitle_max_length = 150 body_max_length = 200 @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union( { "hero_image", "icon", "title", "subtitle", "body", "actions", } ) def __init__( self, *, block_id: Optional[str] = None, hero_image: Optional[str] = None, icon: Optional[str] = None, title: Optional[Union[str, dict, TextObject]] = None, subtitle: Optional[Union[str, dict, TextObject]] = None, body: Optional[Union[str, dict, TextObject]] = None, actions: Optional[Sequence[Union[dict, BlockElement]]] = None, **others: dict, ): """Displays content in a card. https://docs.slack.dev/reference/block-kit/blocks/card-block Args: block_id: A unique identifier for a block. If not specified, a block_id will be generated. hero_image: Link to the top image used on the card. icon: Link to the small image used next to the card's title and subtitle. title: Title of the card. 150 characters max. subtitle: Subtitle of the card. 150 characters max. body: Content of the card. 200 characters max. actions: Action buttons shown at the bottom of the card. """ super().__init__(type=self.type, block_id=block_id) show_unknown_key_warning(self, others) self.hero_image = hero_image self.icon = icon self.title = TextObject.parse(title, default_type=MarkdownTextObject.type) # type: ignore[arg-type] self.subtitle = TextObject.parse(subtitle, default_type=MarkdownTextObject.type) # type: ignore[arg-type] self.body = TextObject.parse(body, default_type=MarkdownTextObject.type) # type: ignore[arg-type] self.actions = BlockElement.parse_all(actions) if actions else None @JsonValidator("At least one of hero_image, title, actions, or body is required") def _validate_content(self): return self.hero_image is not None or self.title is not None or self.actions is not None or self.body is not None @JsonValidator(f"title attribute cannot exceed {title_max_length} characters") def _validate_title_length(self): return self.title is None or self.title.text is None or len(self.title.text) <= self.title_max_length @JsonValidator(f"subtitle attribute cannot exceed {subtitle_max_length} characters") def _validate_subtitle_length(self): return self.subtitle is None or self.subtitle.text is None or len(self.subtitle.text) <= self.subtitle_max_length @JsonValidator(f"body attribute cannot exceed {body_max_length} characters") def _validate_body_length(self): return self.body is None or self.body.text is None or len(self.body.text) <= self.body_max_length ``` Blocks are a series of components that can be combined to create visually rich and compellingly interactive messages. [https://docs.slack.dev/reference/block-kit/blocks](https://docs.slack.dev/reference/block-kit/blocks) Displays content in a card. [https://docs.slack.dev/reference/block-kit/blocks/card-block](https://docs.slack.dev/reference/block-kit/blocks/card-block) ## Args **`block_id`** A unique identifier for a block. If not specified, a block\_id will be generated. **`hero_image`** Link to the top image used on the card. **`icon`** Link to the small image used next to the card's title and subtitle. **`title`** Title of the card. 150 characters max. **`subtitle`** Subtitle of the card. 150 characters max. **`body`** Content of the card. 200 characters max. **`actions`** Action buttons shown at the bottom of the card. ### Ancestors * [Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var body_max_length` The type of the None singleton. `var subtitle_max_length` The type of the None singleton. `var title_max_length` The type of the None singleton. `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union( { "hero_image", "icon", "title", "subtitle", "body", "actions", } ) ``` Build an unordered collection of unique elements. ### Inherited members * `**[Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")**`: * `[block_id_max_length](blocks.html#slack_sdk.models.blocks.blocks.Block.block_id_max_length "slack_sdk.models.blocks.blocks.Block.block_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.blocks.Block.get_non_null_attributes")` * `[logger](blocks.html#slack_sdk.models.blocks.blocks.Block.logger "slack_sdk.models.blocks.blocks.Block.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.blocks.Block.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.blocks.Block.validate_json")` `class CarouselBlock (*, elements: Sequence[dict | [CardBlock](blocks.html#slack_sdk.models.blocks.blocks.CardBlock "slack_sdk.models.blocks.blocks.CardBlock")], block_id: str | None = None, **others: dict)` Expand source code ``` class CarouselBlock(Block): type = "carousel" elements_max_length = 10 @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"elements"}) def __init__( self, *, elements: Sequence[Union[dict, CardBlock]], block_id: Optional[str] = None, **others: dict, ): """Displays related card blocks in a horizontally-scrolling container. https://docs.slack.dev/reference/block-kit/blocks/carousel-block Args: elements (required): A list of cards. Minimum 1, maximum 10 cards. block_id: A unique identifier for a block. If not specified, a block_id will be generated. """ super().__init__(type=self.type, block_id=block_id) show_unknown_key_warning(self, others) self.elements = Block.parse_all(elements) @JsonValidator("elements attribute must contain at least 1 card") def _validate_elements_present(self): return self.elements is not None and len(self.elements) >= 1 @JsonValidator(f"elements attribute cannot exceed {elements_max_length} cards") def _validate_elements_length(self): return self.elements is None or len(self.elements) <= self.elements_max_length ``` Blocks are a series of components that can be combined to create visually rich and compellingly interactive messages. [https://docs.slack.dev/reference/block-kit/blocks](https://docs.slack.dev/reference/block-kit/blocks) Displays related card blocks in a horizontally-scrolling container. [https://docs.slack.dev/reference/block-kit/blocks/carousel-block](https://docs.slack.dev/reference/block-kit/blocks/carousel-block) ## Args **`elements`** : `required` A list of cards. Minimum 1, maximum 10 cards. **`block_id`** A unique identifier for a block. If not specified, a block\_id will be generated. ### Ancestors * [Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var elements_max_length` The type of the None singleton. `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"elements"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")**`: * `[block_id_max_length](blocks.html#slack_sdk.models.blocks.blocks.Block.block_id_max_length "slack_sdk.models.blocks.blocks.Block.block_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.blocks.Block.get_non_null_attributes")` * `[logger](blocks.html#slack_sdk.models.blocks.blocks.Block.logger "slack_sdk.models.blocks.blocks.Block.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.blocks.Block.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.blocks.Block.validate_json")` `class ChannelMultiSelectElement (*, placeholder: str | dict | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject") | None = None, action_id: str | None = None, initial_channels: Sequence[str] | None = None, confirm: dict | [ConfirmObject](basic_components.html#slack_sdk.models.blocks.basic_components.ConfirmObject "slack_sdk.models.blocks.basic_components.ConfirmObject") | None = None, max_selected_items: int | None = None, focus_on_load: bool | None = None, **others: dict)` Expand source code ``` class ChannelMultiSelectElement(InputInteractiveElement): type = "multi_channels_select" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"initial_channels", "max_selected_items"}) def __init__( self, *, placeholder: Optional[Union[str, dict, TextObject]] = None, action_id: Optional[str] = None, initial_channels: Optional[Sequence[str]] = None, confirm: Optional[Union[dict, ConfirmObject]] = None, max_selected_items: Optional[int] = None, focus_on_load: Optional[bool] = None, **others: dict, ): """ This multi-select menu will populate its options with a list of public channels visible to the current user in the active workspace. https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#channel_multi_select Args: placeholder (required): A plain_text only text object that defines the placeholder text shown on the menu. Maximum length for the text in this field is 150 characters. action_id (required): An identifier for the action triggered when a menu option is selected. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action_ids in the containing block. Maximum length for this field is 255 characters. initial_channels: An array of one or more IDs of any valid public channel to be pre-selected when the menu loads. confirm: A confirm object that defines an optional confirmation dialog that appears before the multi-select choices are submitted. max_selected_items: Specifies the maximum number of items that can be selected in the menu. Minimum number is 1. focus_on_load: Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. """ super().__init__( type=self.type, action_id=action_id, placeholder=TextObject.parse(placeholder, PlainTextObject.type), # type: ignore[arg-type] confirm=ConfirmObject.parse(confirm), # type: ignore[arg-type] focus_on_load=focus_on_load, ) show_unknown_key_warning(self, others) self.initial_channels = initial_channels self.max_selected_items = max_selected_items ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) This multi-select menu will populate its options with a list of public channels visible to the current user in the active workspace. [https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#channel\_multi\_select](https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#channel_multi_select) ## Args **`placeholder`** : `required` A plain\_text only text object that defines the placeholder text shown on the menu. Maximum length for the text in this field is 150 characters. **`action_id`** : `required` An identifier for the action triggered when a menu option is selected. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action\_ids in the containing block. Maximum length for this field is 255 characters. **`initial_channels`** An array of one or more IDs of any valid public channel to be pre-selected when the menu loads. **`confirm`** A confirm object that defines an optional confirmation dialog that appears before the multi-select choices are submitted. **`max_selected_items`** Specifies the maximum number of items that can be selected in the menu. Minimum number is 1. **`focus_on_load`** Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. ### Ancestors * [InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement") * [InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement") * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"initial_channels", "max_selected_items"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement")**`: * `[action_id_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement.action_id_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.action_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.InputInteractiveElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.InputInteractiveElement.logger")` * `[placeholder_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.InputInteractiveElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.InputInteractiveElement.validate_json")` `class ChannelSelectElement (*, placeholder: str | dict | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject") | None = None, action_id: str | None = None, initial_channel: str | None = None, confirm: dict | [ConfirmObject](basic_components.html#slack_sdk.models.blocks.basic_components.ConfirmObject "slack_sdk.models.blocks.basic_components.ConfirmObject") | None = None, response_url_enabled: bool | None = None, focus_on_load: bool | None = None, **others: dict)` Expand source code ``` class ChannelSelectElement(InputInteractiveElement): type = "channels_select" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"initial_channel", "response_url_enabled"}) def __init__( self, *, placeholder: Optional[Union[str, dict, TextObject]] = None, action_id: Optional[str] = None, initial_channel: Optional[str] = None, confirm: Optional[Union[dict, ConfirmObject]] = None, response_url_enabled: Optional[bool] = None, focus_on_load: Optional[bool] = None, **others: dict, ): """ This select menu will populate its options with a list of public channels visible to the current user in the active workspace. https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element/#channels_select Args: placeholder (required): A plain_text only text object that defines the placeholder text shown on the menu. Maximum length for the text in this field is 150 characters. action_id (required): An identifier for the action triggered when a menu option is selected. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action_ids in the containing block. Maximum length for this field is 255 characters. initial_channel: The ID of any valid public channel to be pre-selected when the menu loads. confirm: A confirm object that defines an optional confirmation dialog that appears after a menu item is selected. response_url_enabled: This field only works with menus in input blocks in modals. When set to true, the view_submission payload from the menu's parent view will contain a response_url. This response_url can be used for message responses. The target channel for the message will be determined by the value of this select menu focus_on_load: Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. """ super().__init__( type=self.type, action_id=action_id, placeholder=TextObject.parse(placeholder, PlainTextObject.type), # type: ignore[arg-type] confirm=ConfirmObject.parse(confirm), # type: ignore[arg-type] focus_on_load=focus_on_load, ) show_unknown_key_warning(self, others) self.initial_channel = initial_channel self.response_url_enabled = response_url_enabled ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) This select menu will populate its options with a list of public channels visible to the current user in the active workspace. [https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element/#channels\_select](https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element/#channels_select) ## Args **`placeholder`** : `required` A plain\_text only text object that defines the placeholder text shown on the menu. Maximum length for the text in this field is 150 characters. **`action_id`** : `required` An identifier for the action triggered when a menu option is selected. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action\_ids in the containing block. Maximum length for this field is 255 characters. **`initial_channel`** The ID of any valid public channel to be pre-selected when the menu loads. **`confirm`** A confirm object that defines an optional confirmation dialog that appears after a menu item is selected. **`response_url_enabled`** This field only works with menus in input blocks in modals. When set to true, the view\_submission payload from the menu's parent view will contain a response\_url. This response\_url can be used for message responses. The target channel for the message will be determined by the value of this select menu **`focus_on_load`** Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. ### Ancestors * [InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement") * [InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement") * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"initial_channel", "response_url_enabled"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement")**`: * `[action_id_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement.action_id_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.action_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.InputInteractiveElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.InputInteractiveElement.logger")` * `[placeholder_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.InputInteractiveElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.InputInteractiveElement.validate_json")` `class CheckboxesElement (*, action_id: str | None = None, options: Sequence[dict | [Option](basic_components.html#slack_sdk.models.blocks.basic_components.Option "slack_sdk.models.blocks.basic_components.Option")] | None = None, initial_options: Sequence[dict | [Option](basic_components.html#slack_sdk.models.blocks.basic_components.Option "slack_sdk.models.blocks.basic_components.Option")] | None = None, confirm: dict | [ConfirmObject](basic_components.html#slack_sdk.models.blocks.basic_components.ConfirmObject "slack_sdk.models.blocks.basic_components.ConfirmObject") | None = None, focus_on_load: bool | None = None, **others: dict)` Expand source code ``` class CheckboxesElement(InputInteractiveElement): type = "checkboxes" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"options", "initial_options"}) def __init__( self, *, action_id: Optional[str] = None, options: Optional[Sequence[Union[dict, Option]]] = None, initial_options: Optional[Sequence[Union[dict, Option]]] = None, confirm: Optional[Union[dict, ConfirmObject]] = None, focus_on_load: Optional[bool] = None, **others: dict, ): """A checkbox group that allows a user to choose multiple items from a list of possible options. https://docs.slack.dev/reference/block-kit/block-elements/checkboxes-element/ Args: action_id (required): An identifier for the action triggered when the checkbox group is changed. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action_ids in the containing block. Maximum length for this field is 255 characters. options (required): An array of option objects. A maximum of 10 options are allowed. initial_options: An array of option objects that exactly matches one or more of the options. These options will be selected when the checkbox group initially loads. confirm: A confirm object that defines an optional confirmation dialog that appears after clicking one of the checkboxes in this element. focus_on_load: Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. """ super().__init__( type=self.type, action_id=action_id, confirm=ConfirmObject.parse(confirm), # type: ignore[arg-type] focus_on_load=focus_on_load, ) show_unknown_key_warning(self, others) self.options = Option.parse_all(options) self.initial_options = Option.parse_all(initial_options) ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) A checkbox group that allows a user to choose multiple items from a list of possible options. [https://docs.slack.dev/reference/block-kit/block-elements/checkboxes-element/](https://docs.slack.dev/reference/block-kit/block-elements/checkboxes-element/) ## Args **`action_id`** : `required` An identifier for the action triggered when the checkbox group is changed. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action\_ids in the containing block. Maximum length for this field is 255 characters. **`options`** : `required` An array of option objects. A maximum of 10 options are allowed. **`initial_options`** An array of option objects that exactly matches one or more of the options. These options will be selected when the checkbox group initially loads. **`confirm`** A confirm object that defines an optional confirmation dialog that appears after clicking one of the checkboxes in this element. **`focus_on_load`** Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. ### Ancestors * [InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement") * [InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement") * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"options", "initial_options"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement")**`: * `[action_id_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement.action_id_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.action_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.InputInteractiveElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.InputInteractiveElement.logger")` * `[placeholder_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.InputInteractiveElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.InputInteractiveElement.validate_json")` `class ConfirmObject (*, title: str | Dict[str, Any] | [PlainTextObject](basic_components.html#slack_sdk.models.blocks.basic_components.PlainTextObject "slack_sdk.models.blocks.basic_components.PlainTextObject"), text: str | Dict[str, Any] | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject"), confirm: str | Dict[str, Any] | [PlainTextObject](basic_components.html#slack_sdk.models.blocks.basic_components.PlainTextObject "slack_sdk.models.blocks.basic_components.PlainTextObject") = 'Yes', deny: str | Dict[str, Any] | [PlainTextObject](basic_components.html#slack_sdk.models.blocks.basic_components.PlainTextObject "slack_sdk.models.blocks.basic_components.PlainTextObject") = 'No', style: str | None = None)` Expand source code ``` class ConfirmObject(JsonObject): attributes: Set[str] = set() title_max_length = 100 text_max_length = 300 confirm_max_length = 30 deny_max_length = 30 @classmethod def parse(cls, confirm: Union["ConfirmObject", Dict[str, Any]]): if confirm: if isinstance(confirm, ConfirmObject): return confirm elif isinstance(confirm, dict): return ConfirmObject(**confirm) else: # Not yet implemented: show some warning here return None return None def __init__( self, *, title: Union[str, Dict[str, Any], PlainTextObject], text: Union[str, Dict[str, Any], TextObject], confirm: Union[str, Dict[str, Any], PlainTextObject] = "Yes", deny: Union[str, Dict[str, Any], PlainTextObject] = "No", style: Optional[str] = None, ): """ An object that defines a dialog that provides a confirmation step to any interactive element. This dialog will ask the user to confirm their action by offering a confirm and deny button. https://docs.slack.dev/reference/block-kit/composition-objects/confirmation-dialog-object/ """ self._title = TextObject.parse(title, default_type=PlainTextObject.type) self._text = TextObject.parse(text, default_type=MarkdownTextObject.type) self._confirm = TextObject.parse(confirm, default_type=PlainTextObject.type) self._deny = TextObject.parse(deny, default_type=PlainTextObject.type) self._style = style # for backward-compatibility with version 2.0-2.5, the following fields return str values self.title = self._title.text if self._title else None self.text = self._text.text if self._text else None self.confirm = self._confirm.text if self._confirm else None self.deny = self._deny.text if self._deny else None self.style = self._style @JsonValidator(f"title attribute cannot exceed {title_max_length} characters") def title_length(self) -> bool: return self._title is None or len(self._title.text) <= self.title_max_length @JsonValidator(f"text attribute cannot exceed {text_max_length} characters") def text_length(self) -> bool: return self._text is None or len(self._text.text) <= self.text_max_length @JsonValidator(f"confirm attribute cannot exceed {confirm_max_length} characters") def confirm_length(self) -> bool: return self._confirm is None or len(self._confirm.text) <= self.confirm_max_length @JsonValidator(f"deny attribute cannot exceed {deny_max_length} characters") def deny_length(self) -> bool: return self._deny is None or len(self._deny.text) <= self.deny_max_length @JsonValidator('style for confirm must be either "primary" or "danger"') def _validate_confirm_style(self) -> bool: return self._style is None or self._style in ["primary", "danger"] def to_dict(self, option_type: str = "block") -> Dict[str, Any]: if option_type == "action": # deliberately skipping JSON validators here - can't find documentation # on actual limits here json: Dict[str, Union[str, dict]] = { "ok_text": self._confirm.text if self._confirm and self._confirm.text != "Yes" else "Okay", "dismiss_text": self._deny.text if self._deny and self._deny.text != "No" else "Cancel", } if self._title: json["title"] = self._title.text if self._text: json["text"] = self._text.text return json else: self.validate_json() json = {} if self._title: json["title"] = self._title.to_dict() if self._text: json["text"] = self._text.to_dict() if self._confirm: json["confirm"] = self._confirm.to_dict() if self._deny: json["deny"] = self._deny.to_dict() if self._style: json["style"] = self._style return json ``` The base class for JSON serializable class objects An object that defines a dialog that provides a confirmation step to any interactive element. This dialog will ask the user to confirm their action by offering a confirm and deny button. [https://docs.slack.dev/reference/block-kit/composition-objects/confirmation-dialog-object/](https://docs.slack.dev/reference/block-kit/composition-objects/confirmation-dialog-object/) ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes : Set[str]` The type of the None singleton. `var confirm_max_length` The type of the None singleton. `var deny_max_length` The type of the None singleton. `var text_max_length` The type of the None singleton. `var title_max_length` The type of the None singleton. ### Static methods `def parse(confirm: ForwardRef('[ConfirmObject](#slack_sdk.models.blocks.ConfirmObject "slack_sdk.models.blocks.ConfirmObject")') | Dict[str, Any])` ### Methods `def confirm_length(self) ‑> bool` Expand source code ``` @JsonValidator(f"confirm attribute cannot exceed {confirm_max_length} characters") def confirm_length(self) -> bool: return self._confirm is None or len(self._confirm.text) <= self.confirm_max_length ``` `def deny_length(self) ‑> bool` Expand source code ``` @JsonValidator(f"deny attribute cannot exceed {deny_max_length} characters") def deny_length(self) -> bool: return self._deny is None or len(self._deny.text) <= self.deny_max_length ``` `def text_length(self) ‑> bool` Expand source code ``` @JsonValidator(f"text attribute cannot exceed {text_max_length} characters") def text_length(self) -> bool: return self._text is None or len(self._text.text) <= self.text_max_length ``` `def title_length(self) ‑> bool` Expand source code ``` @JsonValidator(f"title attribute cannot exceed {title_max_length} characters") def title_length(self) -> bool: return self._title is None or len(self._title.text) <= self.title_max_length ``` ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class ContextActionsBlock (*, elements: Sequence[dict | [FeedbackButtonsElement](block_elements.html#slack_sdk.models.blocks.block_elements.FeedbackButtonsElement "slack_sdk.models.blocks.block_elements.FeedbackButtonsElement") | [IconButtonElement](block_elements.html#slack_sdk.models.blocks.block_elements.IconButtonElement "slack_sdk.models.blocks.block_elements.IconButtonElement")], block_id: str | None = None, **others: dict)` Expand source code ``` class ContextActionsBlock(Block): type = "context_actions" elements_max_length = 5 @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"elements"}) def __init__( self, *, elements: Sequence[Union[dict, FeedbackButtonsElement, IconButtonElement]], block_id: Optional[str] = None, **others: dict, ): """Displays actions as contextual info, which can include both feedback buttons and icon buttons. https://docs.slack.dev/reference/block-kit/blocks/context-actions-block Args: elements (required): An array of feedback_buttons or icon_button block elements. Maximum number of items is 5. block_id: A string acting as a unique identifier for a block. If not specified, one will be generated. Maximum length for this field is 255 characters. block_id should be unique for each message and each iteration of a message. If a message is updated, use a new block_id. """ super().__init__(type=self.type, block_id=block_id) show_unknown_key_warning(self, others) self.elements = BlockElement.parse_all(elements) @JsonValidator("elements attribute must be specified") def _validate_elements(self): return self.elements is None or len(self.elements) > 0 @JsonValidator(f"elements attribute cannot exceed {elements_max_length} elements") def _validate_elements_length(self): return self.elements is None or len(self.elements) <= self.elements_max_length ``` Blocks are a series of components that can be combined to create visually rich and compellingly interactive messages. [https://docs.slack.dev/reference/block-kit/blocks](https://docs.slack.dev/reference/block-kit/blocks) Displays actions as contextual info, which can include both feedback buttons and icon buttons. [https://docs.slack.dev/reference/block-kit/blocks/context-actions-block](https://docs.slack.dev/reference/block-kit/blocks/context-actions-block) ## Args **`elements`** : `required` An array of feedback\_buttons or icon\_button block elements. Maximum number of items is 5. **`block_id`** A string acting as a unique identifier for a block. If not specified, one will be generated. Maximum length for this field is 255 characters. block\_id should be unique for each message and each iteration of a message. If a message is updated, use a new block\_id. ### Ancestors * [Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var elements_max_length` The type of the None singleton. `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"elements"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")**`: * `[block_id_max_length](blocks.html#slack_sdk.models.blocks.blocks.Block.block_id_max_length "slack_sdk.models.blocks.blocks.Block.block_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.blocks.Block.get_non_null_attributes")` * `[logger](blocks.html#slack_sdk.models.blocks.blocks.Block.logger "slack_sdk.models.blocks.blocks.Block.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.blocks.Block.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.blocks.Block.validate_json")` `class ContextBlock (*, elements: Sequence[dict | [ImageElement](block_elements.html#slack_sdk.models.blocks.block_elements.ImageElement "slack_sdk.models.blocks.block_elements.ImageElement") | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject")], block_id: str | None = None, **others: dict)` Expand source code ``` class ContextBlock(Block): type = "context" elements_max_length = 10 @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"elements"}) def __init__( self, *, elements: Sequence[Union[dict, ImageElement, TextObject]], block_id: Optional[str] = None, **others: dict, ): """Displays message context, which can include both images and text. https://docs.slack.dev/reference/block-kit/blocks/context-block Args: elements (required): An array of image elements and text objects. Maximum number of items is 10. block_id: A string acting as a unique identifier for a block. If not specified, one will be generated. Maximum length for this field is 255 characters. block_id should be unique for each message and each iteration of a message. If a message is updated, use a new block_id. """ super().__init__(type=self.type, block_id=block_id) show_unknown_key_warning(self, others) self.elements = BlockElement.parse_all(elements) @JsonValidator(f"elements attribute cannot exceed {elements_max_length} elements") def _validate_elements_length(self): return self.elements is None or len(self.elements) <= self.elements_max_length ``` Blocks are a series of components that can be combined to create visually rich and compellingly interactive messages. [https://docs.slack.dev/reference/block-kit/blocks](https://docs.slack.dev/reference/block-kit/blocks) Displays message context, which can include both images and text. [https://docs.slack.dev/reference/block-kit/blocks/context-block](https://docs.slack.dev/reference/block-kit/blocks/context-block) ## Args **`elements`** : `required` An array of image elements and text objects. Maximum number of items is 10. **`block_id`** A string acting as a unique identifier for a block. If not specified, one will be generated. Maximum length for this field is 255 characters. block\_id should be unique for each message and each iteration of a message. If a message is updated, use a new block\_id. ### Ancestors * [Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var elements_max_length` The type of the None singleton. `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"elements"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")**`: * `[block_id_max_length](blocks.html#slack_sdk.models.blocks.blocks.Block.block_id_max_length "slack_sdk.models.blocks.blocks.Block.block_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.blocks.Block.get_non_null_attributes")` * `[logger](blocks.html#slack_sdk.models.blocks.blocks.Block.logger "slack_sdk.models.blocks.blocks.Block.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.blocks.Block.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.blocks.Block.validate_json")` `class ConversationFilter (*, include: Sequence[str] | None = None, exclude_bot_users: bool | None = None, exclude_external_shared_channels: bool | None = None)` Expand source code ``` class ConversationFilter(JsonObject): attributes = {"include", "exclude_bot_users", "exclude_external_shared_channels"} logger = logging.getLogger(__name__) def __init__( self, *, include: Optional[Sequence[str]] = None, exclude_bot_users: Optional[bool] = None, exclude_external_shared_channels: Optional[bool] = None, ): """Provides a way to filter the list of options in a conversations select menu or conversations multi-select menu. https://docs.slack.dev/reference/block-kit/composition-objects/conversation-filter-object Args: include: Indicates which type of conversations should be included in the list. When this field is provided, any conversations that do not match will be excluded. You should provide an array of strings from the following options: "im", "mpim", "private", and "public". The array cannot be empty. exclude_bot_users: Indicates whether to exclude bot users from conversation lists. Defaults to false. exclude_external_shared_channels: Indicates whether to exclude external shared channels from conversation lists. Defaults to false. """ self.include = include self.exclude_bot_users = exclude_bot_users self.exclude_external_shared_channels = exclude_external_shared_channels @classmethod def parse(cls, filter: Union[dict, "ConversationFilter"]): if filter is None: return None elif isinstance(filter, ConversationFilter): return filter elif isinstance(filter, dict): d = copy.copy(filter) return ConversationFilter(**d) else: cls.logger.warning(f"Unknown conversation filter object detected and skipped ({filter})") return None ``` The base class for JSON serializable class objects Provides a way to filter the list of options in a conversations select menu or conversations multi-select menu. [https://docs.slack.dev/reference/block-kit/composition-objects/conversation-filter-object](https://docs.slack.dev/reference/block-kit/composition-objects/conversation-filter-object) ## Args **`include`** Indicates which type of conversations should be included in the list. When this field is provided, any conversations that do not match will be excluded. You should provide an array of strings from the following options: "im", "mpim", "private", and "public". The array cannot be empty. **`exclude_bot_users`** Indicates whether to exclude bot users from conversation lists. Defaults to false. **`exclude_external_shared_channels`** Indicates whether to exclude external shared channels from conversation lists. Defaults to false. ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. `var logger` The type of the None singleton. ### Static methods `def parse(filter: dict | ForwardRef('[ConversationFilter](#slack_sdk.models.blocks.ConversationFilter "slack_sdk.models.blocks.ConversationFilter")'))` ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class ConversationMultiSelectElement (*, placeholder: str | dict | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject") | None = None, action_id: str | None = None, initial_conversations: Sequence[str] | None = None, confirm: dict | [ConfirmObject](basic_components.html#slack_sdk.models.blocks.basic_components.ConfirmObject "slack_sdk.models.blocks.basic_components.ConfirmObject") | None = None, max_selected_items: int | None = None, default_to_current_conversation: bool | None = None, filter: dict | [ConversationFilter](block_elements.html#slack_sdk.models.blocks.block_elements.ConversationFilter "slack_sdk.models.blocks.block_elements.ConversationFilter") | None = None, focus_on_load: bool | None = None, **others: dict)` Expand source code ``` class ConversationMultiSelectElement(InputInteractiveElement): type = "multi_conversations_select" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union( { "initial_conversations", "max_selected_items", "default_to_current_conversation", "filter", } ) def __init__( self, *, placeholder: Optional[Union[str, dict, TextObject]] = None, action_id: Optional[str] = None, initial_conversations: Optional[Sequence[str]] = None, confirm: Optional[Union[dict, ConfirmObject]] = None, max_selected_items: Optional[int] = None, default_to_current_conversation: Optional[bool] = None, filter: Optional[Union[dict, ConversationFilter]] = None, focus_on_load: Optional[bool] = None, **others: dict, ): """ This multi-select menu will populate its options with a list of public and private channels, DMs, and MPIMs visible to the current user in the active workspace. https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element/#conversation_multi_select Args: placeholder (required): A plain_text only text object that defines the placeholder text shown on the menu. Maximum length for the text in this field is 150 characters. action_id (required): An identifier for the action triggered when a menu option is selected. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action_ids in the containing block. Maximum length for this field is 255 characters. initial_conversations: An array of one or more IDs of any valid conversations to be pre-selected when the menu loads. If default_to_current_conversation is also supplied, initial_conversations will be ignored. confirm: A confirm object that defines an optional confirmation dialog that appears before the multi-select choices are submitted. max_selected_items: Specifies the maximum number of items that can be selected in the menu. Minimum number is 1. default_to_current_conversation: Pre-populates the select menu with the conversation that the user was viewing when they opened the modal, if available. Default is false. filter: A filter object that reduces the list of available conversations using the specified criteria. focus_on_load: Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. """ super().__init__( type=self.type, action_id=action_id, placeholder=TextObject.parse(placeholder, PlainTextObject.type), # type: ignore[arg-type] confirm=ConfirmObject.parse(confirm), # type: ignore[arg-type] focus_on_load=focus_on_load, ) show_unknown_key_warning(self, others) self.initial_conversations = initial_conversations self.max_selected_items = max_selected_items self.default_to_current_conversation = default_to_current_conversation self.filter = ConversationFilter.parse(filter) # type: ignore[arg-type] ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) This multi-select menu will populate its options with a list of public and private channels, DMs, and MPIMs visible to the current user in the active workspace. [https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element/#conversation\_multi\_select](https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element/#conversation_multi_select) ## Args **`placeholder`** : `required` A plain\_text only text object that defines the placeholder text shown on the menu. Maximum length for the text in this field is 150 characters. **`action_id`** : `required` An identifier for the action triggered when a menu option is selected. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action\_ids in the containing block. Maximum length for this field is 255 characters. **`initial_conversations`** An array of one or more IDs of any valid conversations to be pre-selected when the menu loads. If default\_to\_current\_conversation is also supplied, initial\_conversations will be ignored. **`confirm`** A confirm object that defines an optional confirmation dialog that appears before the multi-select choices are submitted. **`max_selected_items`** Specifies the maximum number of items that can be selected in the menu. Minimum number is 1. **`default_to_current_conversation`** Pre-populates the select menu with the conversation that the user was viewing when they opened the modal, if available. Default is false. **`filter`** A filter object that reduces the list of available conversations using the specified criteria. **`focus_on_load`** Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. ### Ancestors * [InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement") * [InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement") * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union( { "initial_conversations", "max_selected_items", "default_to_current_conversation", "filter", } ) ``` Build an unordered collection of unique elements. ### Inherited members * `**[InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement")**`: * `[action_id_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement.action_id_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.action_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.InputInteractiveElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.InputInteractiveElement.logger")` * `[placeholder_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.InputInteractiveElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.InputInteractiveElement.validate_json")` `class ConversationSelectElement (*, placeholder: str | dict | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject") | None = None, action_id: str | None = None, initial_conversation: str | None = None, confirm: dict | [ConfirmObject](basic_components.html#slack_sdk.models.blocks.basic_components.ConfirmObject "slack_sdk.models.blocks.basic_components.ConfirmObject") | None = None, response_url_enabled: bool | None = None, default_to_current_conversation: bool | None = None, filter: [ConversationFilter](block_elements.html#slack_sdk.models.blocks.block_elements.ConversationFilter "slack_sdk.models.blocks.block_elements.ConversationFilter") | None = None, focus_on_load: bool | None = None, **others: dict)` Expand source code ``` class ConversationSelectElement(InputInteractiveElement): type = "conversations_select" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union( { "initial_conversation", "response_url_enabled", "filter", "default_to_current_conversation", } ) def __init__( self, *, placeholder: Optional[Union[str, dict, TextObject]] = None, action_id: Optional[str] = None, initial_conversation: Optional[str] = None, confirm: Optional[Union[dict, ConfirmObject]] = None, response_url_enabled: Optional[bool] = None, default_to_current_conversation: Optional[bool] = None, filter: Optional[ConversationFilter] = None, focus_on_load: Optional[bool] = None, **others: dict, ): """ This select menu will populate its options with a list of public and private channels, DMs, and MPIMs visible to the current user in the active workspace. https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element/#conversations_select Args: placeholder (required): A plain_text only text object that defines the placeholder text shown on the menu. Maximum length for the text in this field is 150 characters. action_id (required): An identifier for the action triggered when a menu option is selected. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action_ids in the containing block. Maximum length for this field is 255 characters. initial_conversation: The ID of any valid conversation to be pre-selected when the menu loads. If default_to_current_conversation is also supplied, initial_conversation will take precedence. confirm: A confirm object that defines an optional confirmation dialog that appears after a menu item is selected. response_url_enabled: This field only works with menus in input blocks in modals. When set to true, the view_submission payload from the menu's parent view will contain a response_url. This response_url can be used for message responses. The target conversation for the message will be determined by the value of this select menu. default_to_current_conversation: Pre-populates the select menu with the conversation that the user was viewing when they opened the modal, if available. Default is false. filter: A filter object that reduces the list of available conversations using the specified criteria. focus_on_load: Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. """ super().__init__( type=self.type, action_id=action_id, placeholder=TextObject.parse(placeholder, PlainTextObject.type), # type: ignore[arg-type] confirm=ConfirmObject.parse(confirm), # type: ignore[arg-type] focus_on_load=focus_on_load, ) show_unknown_key_warning(self, others) self.initial_conversation = initial_conversation self.response_url_enabled = response_url_enabled self.default_to_current_conversation = default_to_current_conversation self.filter = filter ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) This select menu will populate its options with a list of public and private channels, DMs, and MPIMs visible to the current user in the active workspace. [https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element/#conversations\_select](https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element/#conversations_select) ## Args **`placeholder`** : `required` A plain\_text only text object that defines the placeholder text shown on the menu. Maximum length for the text in this field is 150 characters. **`action_id`** : `required` An identifier for the action triggered when a menu option is selected. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action\_ids in the containing block. Maximum length for this field is 255 characters. **`initial_conversation`** The ID of any valid conversation to be pre-selected when the menu loads. If default\_to\_current\_conversation is also supplied, initial\_conversation will take precedence. **`confirm`** A confirm object that defines an optional confirmation dialog that appears after a menu item is selected. **`response_url_enabled`** This field only works with menus in input blocks in modals. When set to true, the view\_submission payload from the menu's parent view will contain a response\_url. This response\_url can be used for message responses. The target conversation for the message will be determined by the value of this select menu. **`default_to_current_conversation`** Pre-populates the select menu with the conversation that the user was viewing when they opened the modal, if available. Default is false. **`filter`** A filter object that reduces the list of available conversations using the specified criteria. **`focus_on_load`** Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. ### Ancestors * [InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement") * [InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement") * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union( { "initial_conversation", "response_url_enabled", "filter", "default_to_current_conversation", } ) ``` Build an unordered collection of unique elements. ### Inherited members * `**[InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement")**`: * `[action_id_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement.action_id_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.action_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.InputInteractiveElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.InputInteractiveElement.logger")` * `[placeholder_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.InputInteractiveElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.InputInteractiveElement.validate_json")` `class DatePickerElement (*, action_id: str | None = None, placeholder: str | dict | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject") | None = None, initial_date: str | None = None, confirm: dict | [ConfirmObject](basic_components.html#slack_sdk.models.blocks.basic_components.ConfirmObject "slack_sdk.models.blocks.basic_components.ConfirmObject") | None = None, focus_on_load: bool | None = None, **others: dict)` Expand source code ``` class DatePickerElement(InputInteractiveElement): type = "datepicker" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"initial_date"}) def __init__( self, *, action_id: Optional[str] = None, placeholder: Optional[Union[str, dict, TextObject]] = None, initial_date: Optional[str] = None, confirm: Optional[Union[dict, ConfirmObject]] = None, focus_on_load: Optional[bool] = None, **others: dict, ): """ An element which lets users easily select a date from a calendar style UI. Date picker elements can be used inside of SectionBlocks and ActionsBlocks. https://docs.slack.dev/reference/block-kit/block-elements/date-picker-element Args: action_id (required): An identifier for the action triggered when a menu option is selected. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action_ids in the containing block. Maximum length for this field is 255 characters. placeholder: A plain_text only text object that defines the placeholder text shown on the datepicker. Maximum length for the text in this field is 150 characters. initial_date: The initial date that is selected when the element is loaded. This should be in the format YYYY-MM-DD. confirm: A confirm object that defines an optional confirmation dialog that appears after a date is selected. focus_on_load: Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. """ super().__init__( type=self.type, action_id=action_id, placeholder=TextObject.parse(placeholder, PlainTextObject.type), # type: ignore[arg-type] confirm=ConfirmObject.parse(confirm), # type: ignore[arg-type] focus_on_load=focus_on_load, ) show_unknown_key_warning(self, others) self.initial_date = initial_date @JsonValidator("initial_date attribute must be in format 'YYYY-MM-DD'") def _validate_initial_date_valid(self) -> bool: return ( self.initial_date is None or re.match(r"\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])", self.initial_date) is not None ) ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) An element which lets users easily select a date from a calendar style UI. Date picker elements can be used inside of SectionBlocks and ActionsBlocks. [https://docs.slack.dev/reference/block-kit/block-elements/date-picker-element](https://docs.slack.dev/reference/block-kit/block-elements/date-picker-element) ## Args **`action_id`** : `required` An identifier for the action triggered when a menu option is selected. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action\_ids in the containing block. Maximum length for this field is 255 characters. **`placeholder`** A plain\_text only text object that defines the placeholder text shown on the datepicker. Maximum length for the text in this field is 150 characters. **`initial_date`** The initial date that is selected when the element is loaded. This should be in the format YYYY-MM-DD. **`confirm`** A confirm object that defines an optional confirmation dialog that appears after a date is selected. **`focus_on_load`** Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. ### Ancestors * [InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement") * [InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement") * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"initial_date"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement")**`: * `[action_id_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement.action_id_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.action_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.InputInteractiveElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.InputInteractiveElement.logger")` * `[placeholder_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.InputInteractiveElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.InputInteractiveElement.validate_json")` `class DateTimePickerElement (*, action_id: str | None = None, initial_date_time: int | None = None, confirm: dict | [ConfirmObject](basic_components.html#slack_sdk.models.blocks.basic_components.ConfirmObject "slack_sdk.models.blocks.basic_components.ConfirmObject") | None = None, focus_on_load: bool | None = None, **others: dict)` Expand source code ``` class DateTimePickerElement(InputInteractiveElement): type = "datetimepicker" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"initial_date_time"}) def __init__( self, *, action_id: Optional[str] = None, initial_date_time: Optional[int] = None, confirm: Optional[Union[dict, ConfirmObject]] = None, focus_on_load: Optional[bool] = None, **others: dict, ): """ An element that allows the selection of a time of day formatted as a UNIX timestamp. On desktop clients, this time picker will take the form of a dropdown list and the date picker will take the form of a dropdown calendar. Both options will have free-text entry for precise choices. On mobile clients, the time picker and date picker will use native UIs. https://docs.slack.dev/reference/block-kit/block-elements/date-picker-element/ Args: action_id (required): An identifier for the action triggered when a time is selected. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action_ids in the containing block. Maximum length for this field is 255 characters. initial_date_time: The initial date and time that is selected when the element is loaded, represented as a UNIX timestamp in seconds. This should be in the format of 10 digits, for example 1628633820 represents the date and time August 10th, 2021 at 03:17pm PST. and mm is minutes with leading zeros (00 to 59), for example 22:25 for 10:25pm. confirm: A confirm object that defines an optional confirmation dialog that appears after a time is selected. focus_on_load: Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. """ super().__init__( type=self.type, action_id=action_id, confirm=ConfirmObject.parse(confirm), # type: ignore[arg-type] focus_on_load=focus_on_load, ) show_unknown_key_warning(self, others) self.initial_date_time = initial_date_time @JsonValidator("initial_date_time attribute must be between 0 and 99999999 seconds") def _validate_initial_date_time_valid(self) -> bool: return self.initial_date_time is None or (0 <= self.initial_date_time <= 9999999999) ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) An element that allows the selection of a time of day formatted as a UNIX timestamp. On desktop clients, this time picker will take the form of a dropdown list and the date picker will take the form of a dropdown calendar. Both options will have free-text entry for precise choices. On mobile clients, the time picker and date picker will use native UIs. [https://docs.slack.dev/reference/block-kit/block-elements/date-picker-element/](https://docs.slack.dev/reference/block-kit/block-elements/date-picker-element/) ## Args **`action_id`** : `required` An identifier for the action triggered when a time is selected. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action\_ids in the containing block. Maximum length for this field is 255 characters. **`initial_date_time`** The initial date and time that is selected when the element is loaded, represented as a UNIX timestamp in seconds. This should be in the format of 10 digits, for example 1628633820 represents the date and time August 10th, 2021 at 03:17pm PST. and mm is minutes with leading zeros (00 to 59), for example 22:25 for 10:25pm. **`confirm`** A confirm object that defines an optional confirmation dialog that appears after a time is selected. **`focus_on_load`** Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. ### Ancestors * [InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement") * [InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement") * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"initial_date_time"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement")**`: * `[action_id_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement.action_id_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.action_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.InputInteractiveElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.InputInteractiveElement.logger")` * `[placeholder_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.InputInteractiveElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.InputInteractiveElement.validate_json")` `class DividerBlock (*, block_id: str | None = None, **others: dict)` Expand source code ``` class DividerBlock(Block): type = "divider" def __init__( self, *, block_id: Optional[str] = None, **others: dict, ): """A content divider, like an
, to split up different blocks inside of a message. https://docs.slack.dev/reference/block-kit/blocks/divider-block Args: block_id: A string acting as a unique identifier for a block. If not specified, one will be generated. You can use this block_id when you receive an interaction payload to identify the source of the action. Maximum length for this field is 255 characters. block_id should be unique for each message and each iteration of a message. If a message is updated, use a new block_id. """ super().__init__(type=self.type, block_id=block_id) show_unknown_key_warning(self, others) ``` Blocks are a series of components that can be combined to create visually rich and compellingly interactive messages. [https://docs.slack.dev/reference/block-kit/blocks](https://docs.slack.dev/reference/block-kit/blocks) A content divider, like an * * * , to split up different blocks inside of a message. [https://docs.slack.dev/reference/block-kit/blocks/divider-block](https://docs.slack.dev/reference/block-kit/blocks/divider-block) ## Args **`block_id`** A string acting as a unique identifier for a block. If not specified, one will be generated. You can use this block\_id when you receive an interaction payload to identify the source of the action. Maximum length for this field is 255 characters. block\_id should be unique for each message and each iteration of a message. If a message is updated, use a new block\_id. ### Ancestors * [Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Inherited members * `**[Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")**`: * `[attributes](blocks.html#slack_sdk.models.blocks.blocks.Block.attributes "slack_sdk.models.blocks.blocks.Block.attributes")` * `[block_id_max_length](blocks.html#slack_sdk.models.blocks.blocks.Block.block_id_max_length "slack_sdk.models.blocks.blocks.Block.block_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.blocks.Block.get_non_null_attributes")` * `[logger](blocks.html#slack_sdk.models.blocks.blocks.Block.logger "slack_sdk.models.blocks.blocks.Block.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.blocks.Block.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.blocks.Block.validate_json")` `class EmailInputElement (*, action_id: str | None = None, initial_value: str | None = None, dispatch_action_config: dict | [DispatchActionConfig](basic_components.html#slack_sdk.models.blocks.basic_components.DispatchActionConfig "slack_sdk.models.blocks.basic_components.DispatchActionConfig") | None = None, focus_on_load: bool | None = None, placeholder: str | dict | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject") | None = None, **others: dict)` Expand source code ``` class EmailInputElement(InputInteractiveElement): type = "email_text_input" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union( { "initial_value", "dispatch_action_config", } ) def __init__( self, *, action_id: Optional[str] = None, initial_value: Optional[str] = None, dispatch_action_config: Optional[Union[dict, DispatchActionConfig]] = None, focus_on_load: Optional[bool] = None, placeholder: Optional[Union[str, dict, TextObject]] = None, **others: dict, ): """ https://docs.slack.dev/reference/block-kit/block-elements/email-input-element Args: action_id (required): An identifier for the input value when the parent modal is submitted. You can use this when you receive a view_submission payload to identify the value of the input element. Should be unique among all other action_ids in the containing block. Maximum length for this field is 255 characters. initial_value: The initial value in the email input when it is loaded. dispatch_action_config: dispatch configuration object that determines when during text input the element returns a block_actions payload. focus_on_load: Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. placeholder: A plain_text only text object that defines the placeholder text shown in the email input. Maximum length for the text in this field is 150 characters. """ super().__init__( type=self.type, action_id=action_id, placeholder=TextObject.parse(placeholder, PlainTextObject.type), # type: ignore[arg-type] focus_on_load=focus_on_load, ) show_unknown_key_warning(self, others) self.initial_value = initial_value self.dispatch_action_config = dispatch_action_config ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) [https://docs.slack.dev/reference/block-kit/block-elements/email-input-element](https://docs.slack.dev/reference/block-kit/block-elements/email-input-element) ## Args **`action_id`** : `required` An identifier for the input value when the parent modal is submitted. You can use this when you receive a view\_submission payload to identify the value of the input element. Should be unique among all other action\_ids in the containing block. Maximum length for this field is 255 characters. **`initial_value`** The initial value in the email input when it is loaded. **`dispatch_action_config`** dispatch configuration object that determines when during text input the element returns a block\_actions payload. **`focus_on_load`** Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. **`placeholder`** A plain\_text only text object that defines the placeholder text shown in the email input. Maximum length for the text in this field is 150 characters. ### Ancestors * [InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement") * [InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement") * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union( { "initial_value", "dispatch_action_config", } ) ``` Build an unordered collection of unique elements. ### Inherited members * `**[InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement")**`: * `[action_id_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement.action_id_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.action_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.InputInteractiveElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.InputInteractiveElement.logger")` * `[placeholder_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.InputInteractiveElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.InputInteractiveElement.validate_json")` `class ExternalDataMultiSelectElement (*, placeholder: str | dict | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject") | None = None, action_id: str | None = None, min_query_length: int | None = None, initial_options: Sequence[dict | [Option](basic_components.html#slack_sdk.models.blocks.basic_components.Option "slack_sdk.models.blocks.basic_components.Option")] | None = None, confirm: dict | [ConfirmObject](basic_components.html#slack_sdk.models.blocks.basic_components.ConfirmObject "slack_sdk.models.blocks.basic_components.ConfirmObject") | None = None, max_selected_items: int | None = None, focus_on_load: bool | None = None, **others: dict)` Expand source code ``` class ExternalDataMultiSelectElement(InputInteractiveElement): type = "multi_external_select" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"min_query_length", "initial_options", "max_selected_items"}) def __init__( self, *, placeholder: Optional[Union[str, dict, TextObject]] = None, action_id: Optional[str] = None, min_query_length: Optional[int] = None, initial_options: Optional[Sequence[Union[dict, Option]]] = None, confirm: Optional[Union[dict, ConfirmObject]] = None, max_selected_items: Optional[int] = None, focus_on_load: Optional[bool] = None, **others: dict, ): """ This select menu will load its options from an external data source, allowing for a dynamic list of options. https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select Args: placeholder (required): A plain_text only text object that defines the placeholder text shown on the menu. Maximum length for the text in this field is 150 characters. action_id (required): An identifier for the action triggered when a menu option is selected. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action_ids in the containing block. Maximum length for this field is 255 characters. min_query_length: When the typeahead field is used, a request will be sent on every character change. If you prefer fewer requests or more fully ideated queries, use the min_query_length attribute to tell Slack the fewest number of typed characters required before dispatch. The default value is 3 initial_options: An array of option objects that exactly match one or more of the options within options or option_groups. These options will be selected when the menu initially loads. confirm: A confirm object that defines an optional confirmation dialog that appears before the multi-select choices are submitted. max_selected_items: Specifies the maximum number of items that can be selected in the menu. Minimum number is 1. focus_on_load: Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. """ super().__init__( type=self.type, action_id=action_id, placeholder=TextObject.parse(placeholder, PlainTextObject.type), # type: ignore[arg-type] confirm=ConfirmObject.parse(confirm), # type: ignore[arg-type] focus_on_load=focus_on_load, ) show_unknown_key_warning(self, others) self.min_query_length = min_query_length self.initial_options = Option.parse_all(initial_options) self.max_selected_items = max_selected_items ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) This select menu will load its options from an external data source, allowing for a dynamic list of options. [https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external\_multi\_select](https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select) ## Args **`placeholder`** : `required` A plain\_text only text object that defines the placeholder text shown on the menu. Maximum length for the text in this field is 150 characters. **`action_id`** : `required` An identifier for the action triggered when a menu option is selected. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action\_ids in the containing block. Maximum length for this field is 255 characters. **`min_query_length`** When the typeahead field is used, a request will be sent on every character change. If you prefer fewer requests or more fully ideated queries, use the min\_query\_length attribute to tell Slack the fewest number of typed characters required before dispatch. The default value is 3 **`initial_options`** An array of option objects that exactly match one or more of the options within options or option\_groups. These options will be selected when the menu initially loads. **`confirm`** A confirm object that defines an optional confirmation dialog that appears before the multi-select choices are submitted. **`max_selected_items`** Specifies the maximum number of items that can be selected in the menu. Minimum number is 1. **`focus_on_load`** Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. ### Ancestors * [InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement") * [InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement") * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"min_query_length", "initial_options", "max_selected_items"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement")**`: * `[action_id_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement.action_id_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.action_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.InputInteractiveElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.InputInteractiveElement.logger")` * `[placeholder_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.InputInteractiveElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.InputInteractiveElement.validate_json")` `class ExternalDataSelectElement (*, action_id: str | None = None, placeholder: str | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject") | None = None, initial_option: [Option](basic_components.html#slack_sdk.models.blocks.basic_components.Option "slack_sdk.models.blocks.basic_components.Option") | [OptionGroup](basic_components.html#slack_sdk.models.blocks.basic_components.OptionGroup "slack_sdk.models.blocks.basic_components.OptionGroup") | None = None, min_query_length: int | None = None, confirm: dict | [ConfirmObject](basic_components.html#slack_sdk.models.blocks.basic_components.ConfirmObject "slack_sdk.models.blocks.basic_components.ConfirmObject") | None = None, focus_on_load: bool | None = None, **others: dict)` Expand source code ``` class ExternalDataSelectElement(InputInteractiveElement): type = "external_select" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"min_query_length", "initial_option"}) def __init__( self, *, action_id: Optional[str] = None, placeholder: Optional[Union[str, TextObject]] = None, initial_option: Union[Optional[Option], Optional[OptionGroup]] = None, min_query_length: Optional[int] = None, confirm: Optional[Union[dict, ConfirmObject]] = None, focus_on_load: Optional[bool] = None, **others: dict, ): """ This select menu will load its options from an external data source, allowing for a dynamic list of options. https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element#external_select Args: action_id (required): An identifier for the action triggered when a menu option is selected. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action_ids in the containing block. Maximum length for this field is 255 characters. placeholder (required): A plain_text only text object that defines the placeholder text shown on the menu. Maximum length for the text in this field is 150 characters. initial_option: A single option that exactly matches one of the options within the options or option_groups loaded from the external data source. This option will be selected when the menu initially loads. min_query_length: When the typeahead field is used, a request will be sent on every character change. If you prefer fewer requests or more fully ideated queries, use the min_query_length attribute to tell Slack the fewest number of typed characters required before dispatch. The default value is 3. confirm: A confirm object that defines an optional confirmation dialog that appears after a menu item is selected. focus_on_load: Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. """ super().__init__( type=self.type, action_id=action_id, placeholder=TextObject.parse(placeholder, PlainTextObject.type), # type: ignore[arg-type] confirm=ConfirmObject.parse(confirm), # type: ignore[arg-type] focus_on_load=focus_on_load, ) show_unknown_key_warning(self, others) self.min_query_length = min_query_length self.initial_option = initial_option ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) This select menu will load its options from an external data source, allowing for a dynamic list of options. [https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element#external\_select](https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element#external_select) ## Args **`action_id`** : `required` An identifier for the action triggered when a menu option is selected. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action\_ids in the containing block. Maximum length for this field is 255 characters. **`placeholder`** : `required` A plain\_text only text object that defines the placeholder text shown on the menu. Maximum length for the text in this field is 150 characters. **`initial_option`** A single option that exactly matches one of the options within the options or option\_groups loaded from the external data source. This option will be selected when the menu initially loads. **`min_query_length`** When the typeahead field is used, a request will be sent on every character change. If you prefer fewer requests or more fully ideated queries, use the min\_query\_length attribute to tell Slack the fewest number of typed characters required before dispatch. The default value is 3. **`confirm`** A confirm object that defines an optional confirmation dialog that appears after a menu item is selected. **`focus_on_load`** Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. ### Ancestors * [InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement") * [InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement") * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"min_query_length", "initial_option"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement")**`: * `[action_id_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement.action_id_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.action_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.InputInteractiveElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.InputInteractiveElement.logger")` * `[placeholder_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.InputInteractiveElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.InputInteractiveElement.validate_json")` `class FeedbackButtonObject (*, text: str | Dict[str, Any] | [PlainTextObject](basic_components.html#slack_sdk.models.blocks.basic_components.PlainTextObject "slack_sdk.models.blocks.basic_components.PlainTextObject"), accessibility_label: str | None = None, value: str, **others: Dict[str, Any])` Expand source code ``` class FeedbackButtonObject(JsonObject): attributes: Set[str] = set() text_max_length = 75 value_max_length = 2000 @classmethod def parse(cls, feedback_button: Union["FeedbackButtonObject", Dict[str, Any]]): if feedback_button: if isinstance(feedback_button, FeedbackButtonObject): return feedback_button elif isinstance(feedback_button, dict): return FeedbackButtonObject(**feedback_button) else: # Not yet implemented: show some warning here return None return None def __init__( self, *, text: Union[str, Dict[str, Any], PlainTextObject], accessibility_label: Optional[str] = None, value: str, **others: Dict[str, Any], ): """ A feedback button element object for either positive or negative feedback. https://docs.slack.dev/reference/block-kit/block-elements/feedback-buttons-element#button-object-fields Args: text (required): An object containing some text. Maximum length for this field is 75 characters. accessibility_label: A label for longer descriptive text about a button element. This label will be read out by screen readers instead of the button `text` object. value (required): The button value. Maximum length for this field is 2000 characters. """ self._text: Optional[TextObject] = PlainTextObject.parse(text, default_type=PlainTextObject.type) self._accessibility_label: Optional[str] = accessibility_label self._value: Optional[str] = value show_unknown_key_warning(self, others) @JsonValidator(f"text attribute cannot exceed {text_max_length} characters") def text_length(self) -> bool: return self._text is None or len(self._text.text) <= self.text_max_length @JsonValidator(f"value attribute cannot exceed {value_max_length} characters") def value_length(self) -> bool: return self._value is None or len(self._value) <= self.value_max_length def to_dict(self) -> Dict[str, Any]: self.validate_json() json: Dict[str, Union[str, dict]] = {} if self._text: json["text"] = self._text.to_dict() if self._accessibility_label: json["accessibility_label"] = self._accessibility_label if self._value: json["value"] = self._value return json ``` The base class for JSON serializable class objects A feedback button element object for either positive or negative feedback. [https://docs.slack.dev/reference/block-kit/block-elements/feedback-buttons-element#button-object-fields](https://docs.slack.dev/reference/block-kit/block-elements/feedback-buttons-element#button-object-fields) ## Args **`text`** : `required` An object containing some text. Maximum length for this field is 75 characters. **`accessibility_label`** A label for longer descriptive text about a button element. This label will be read out by screen readers instead of the button `text` object. **`value`** : `required` The button value. Maximum length for this field is 2000 characters. ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes : Set[str]` The type of the None singleton. `var text_max_length` The type of the None singleton. `var value_max_length` The type of the None singleton. ### Static methods `def parse(feedback_button: ForwardRef('[FeedbackButtonObject](#slack_sdk.models.blocks.FeedbackButtonObject "slack_sdk.models.blocks.FeedbackButtonObject")') | Dict[str, Any])` ### Methods `def text_length(self) ‑> bool` Expand source code ``` @JsonValidator(f"text attribute cannot exceed {text_max_length} characters") def text_length(self) -> bool: return self._text is None or len(self._text.text) <= self.text_max_length ``` `def value_length(self) ‑> bool` Expand source code ``` @JsonValidator(f"value attribute cannot exceed {value_max_length} characters") def value_length(self) -> bool: return self._value is None or len(self._value) <= self.value_max_length ``` ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class FeedbackButtonsElement (*, action_id: str | None = None, positive_button: dict | [FeedbackButtonObject](basic_components.html#slack_sdk.models.blocks.basic_components.FeedbackButtonObject "slack_sdk.models.blocks.basic_components.FeedbackButtonObject"), negative_button: dict | [FeedbackButtonObject](basic_components.html#slack_sdk.models.blocks.basic_components.FeedbackButtonObject "slack_sdk.models.blocks.basic_components.FeedbackButtonObject"), **others: dict)` Expand source code ``` class FeedbackButtonsElement(InteractiveElement): type = "feedback_buttons" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"positive_button", "negative_button"}) def __init__( self, *, action_id: Optional[str] = None, positive_button: Union[dict, FeedbackButtonObject], negative_button: Union[dict, FeedbackButtonObject], **others: dict, ): """Buttons to indicate positive or negative feedback. https://docs.slack.dev/reference/block-kit/block-elements/feedback-buttons-element Args: action_id (required): An identifier for this action. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action_ids in the containing block. Maximum length for this field is 255 characters. positive_button (required): A button to indicate positive feedback. negative_button (required): A button to indicate negative feedback. """ super().__init__(action_id=action_id, type=self.type) show_unknown_key_warning(self, others) self.positive_button = FeedbackButtonObject.parse(positive_button) self.negative_button = FeedbackButtonObject.parse(negative_button) ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) Buttons to indicate positive or negative feedback. [https://docs.slack.dev/reference/block-kit/block-elements/feedback-buttons-element](https://docs.slack.dev/reference/block-kit/block-elements/feedback-buttons-element) ## Args **`action_id`** : `required` An identifier for this action. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action\_ids in the containing block. Maximum length for this field is 255 characters. **`positive_button`** : `required` A button to indicate positive feedback. **`negative_button`** : `required` A button to indicate negative feedback. ### Ancestors * [InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement") * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Inherited members * `**[InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement")**`: * `[action_id_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement.action_id_max_length "slack_sdk.models.blocks.block_elements.InteractiveElement.action_id_max_length")` * `[attributes](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement.attributes "slack_sdk.models.blocks.block_elements.InteractiveElement.attributes")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.InteractiveElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.InteractiveElement.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.InteractiveElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.InteractiveElement.validate_json")` `class FileBlock (*, external_id: str, source: str = 'remote', block_id: str | None = None, **others: dict)` Expand source code ``` class FileBlock(Block): type = "file" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"external_id", "source"}) def __init__( self, *, external_id: str, source: str = "remote", block_id: Optional[str] = None, **others: dict, ): """Displays a remote file. https://docs.slack.dev/reference/block-kit/blocks/file-block Args: external_id (required): The external unique ID for this file. source (required): At the moment, source will always be remote for a remote file. block_id: A string acting as a unique identifier for a block. If not specified, one will be generated. Maximum length for this field is 255 characters. block_id should be unique for each message and each iteration of a message. If a message is updated, use a new block_id. """ super().__init__(type=self.type, block_id=block_id) show_unknown_key_warning(self, others) self.external_id = external_id self.source = source ``` Blocks are a series of components that can be combined to create visually rich and compellingly interactive messages. [https://docs.slack.dev/reference/block-kit/blocks](https://docs.slack.dev/reference/block-kit/blocks) Displays a remote file. [https://docs.slack.dev/reference/block-kit/blocks/file-block](https://docs.slack.dev/reference/block-kit/blocks/file-block) ## Args **`external_id`** : `required` The external unique ID for this file. **`source`** : `required` At the moment, source will always be remote for a remote file. **`block_id`** A string acting as a unique identifier for a block. If not specified, one will be generated. Maximum length for this field is 255 characters. block\_id should be unique for each message and each iteration of a message. If a message is updated, use a new block\_id. ### Ancestors * [Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"external_id", "source"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")**`: * `[block_id_max_length](blocks.html#slack_sdk.models.blocks.blocks.Block.block_id_max_length "slack_sdk.models.blocks.blocks.Block.block_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.blocks.Block.get_non_null_attributes")` * `[logger](blocks.html#slack_sdk.models.blocks.blocks.Block.logger "slack_sdk.models.blocks.blocks.Block.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.blocks.Block.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.blocks.Block.validate_json")` `class HeaderBlock (*, block_id: str | None = None, text: str | dict | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject") | None = None, **others: dict)` Expand source code ``` class HeaderBlock(Block): type = "header" text_max_length = 150 @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"text"}) def __init__( self, *, block_id: Optional[str] = None, text: Optional[Union[str, dict, TextObject]] = None, **others: dict, ): """A header is a plain-text block that displays in a larger, bold font. https://docs.slack.dev/reference/block-kit/blocks/header-block Args: block_id: A string acting as a unique identifier for a block. If not specified, one will be generated. Maximum length for this field is 255 characters. block_id should be unique for each message and each iteration of a message. If a message is updated, use a new block_id. text (required): The text for the block, in the form of a plain_text text object. Maximum length for the text in this field is 150 characters. """ super().__init__(type=self.type, block_id=block_id) show_unknown_key_warning(self, others) self.text = TextObject.parse(text, default_type=PlainTextObject.type) # type: ignore[arg-type] @JsonValidator("text attribute must be specified") def _validate_text(self): return self.text is not None @JsonValidator(f"text attribute cannot exceed {text_max_length} characters") def _validate_alt_text_length(self): return self.text is None or len(self.text.text) <= self.text_max_length ``` Blocks are a series of components that can be combined to create visually rich and compellingly interactive messages. [https://docs.slack.dev/reference/block-kit/blocks](https://docs.slack.dev/reference/block-kit/blocks) A header is a plain-text block that displays in a larger, bold font. [https://docs.slack.dev/reference/block-kit/blocks/header-block](https://docs.slack.dev/reference/block-kit/blocks/header-block) ## Args **`block_id`** A string acting as a unique identifier for a block. If not specified, one will be generated. Maximum length for this field is 255 characters. block\_id should be unique for each message and each iteration of a message. If a message is updated, use a new block\_id. **`text`** : `required` The text for the block, in the form of a plain\_text text object. Maximum length for the text in this field is 150 characters. ### Ancestors * [Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var text_max_length` The type of the None singleton. `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"text"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")**`: * `[block_id_max_length](blocks.html#slack_sdk.models.blocks.blocks.Block.block_id_max_length "slack_sdk.models.blocks.blocks.Block.block_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.blocks.Block.get_non_null_attributes")` * `[logger](blocks.html#slack_sdk.models.blocks.blocks.Block.logger "slack_sdk.models.blocks.blocks.Block.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.blocks.Block.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.blocks.Block.validate_json")` `class IconButtonElement (*, action_id: str | None = None, icon: str, text: str | dict | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject"), accessibility_label: str | None = None, value: str | None = None, visible_to_user_ids: List[str] | None = None, confirm: dict | [ConfirmObject](basic_components.html#slack_sdk.models.blocks.basic_components.ConfirmObject "slack_sdk.models.blocks.basic_components.ConfirmObject") | None = None, **others: dict)` Expand source code ``` class IconButtonElement(InteractiveElement): type = "icon_button" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"icon", "text", "accessibility_label", "value", "visible_to_user_ids", "confirm"}) def __init__( self, *, action_id: Optional[str] = None, icon: str, text: Union[str, dict, TextObject], accessibility_label: Optional[str] = None, value: Optional[str] = None, visible_to_user_ids: Optional[List[str]] = None, confirm: Optional[Union[dict, ConfirmObject]] = None, **others: dict, ): """An icon button to perform actions. https://docs.slack.dev/reference/block-kit/block-elements/icon-button-element Args: action_id: An identifier for this action. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action_ids in the containing block. Maximum length for this field is 255 characters. icon (required): The icon to show (e.g., 'trash'). text (required): Defines an object containing some text. accessibility_label: A label for longer descriptive text about a button element. This label will be read out by screen readers instead of the button text object. Maximum length for this field is 75 characters. value: The button value. Maximum length for this field is 2000 characters. visible_to_user_ids: User IDs for which the icon appears. Maximum length for this field is 10 user IDs. confirm: A confirm object that defines an optional confirmation dialog after the button is clicked. """ super().__init__(action_id=action_id, type=self.type) show_unknown_key_warning(self, others) self.icon = icon self.text = TextObject.parse(text, PlainTextObject.type) self.accessibility_label = accessibility_label self.value = value self.visible_to_user_ids = visible_to_user_ids self.confirm = ConfirmObject.parse(confirm) if confirm else None ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) An icon button to perform actions. [https://docs.slack.dev/reference/block-kit/block-elements/icon-button-element](https://docs.slack.dev/reference/block-kit/block-elements/icon-button-element) ## Args **`action_id`** An identifier for this action. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action\_ids in the containing block. Maximum length for this field is 255 characters. **`icon`** : `required` The icon to show (e.g., 'trash'). **`text`** : `required` Defines an object containing some text. **`accessibility_label`** A label for longer descriptive text about a button element. This label will be read out by screen readers instead of the button text object. Maximum length for this field is 75 characters. **`value`** The button value. Maximum length for this field is 2000 characters. **`visible_to_user_ids`** User IDs for which the icon appears. Maximum length for this field is 10 user IDs. **`confirm`** A confirm object that defines an optional confirmation dialog after the button is clicked. ### Ancestors * [InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement") * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Inherited members * `**[InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement")**`: * `[action_id_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement.action_id_max_length "slack_sdk.models.blocks.block_elements.InteractiveElement.action_id_max_length")` * `[attributes](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement.attributes "slack_sdk.models.blocks.block_elements.InteractiveElement.attributes")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.InteractiveElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.InteractiveElement.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.InteractiveElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.InteractiveElement.validate_json")` `class ImageBlock (*, alt_text: str, image_url: str | None = None, slack_file: Dict[str, Any] | [SlackFile](basic_components.html#slack_sdk.models.blocks.basic_components.SlackFile "slack_sdk.models.blocks.basic_components.SlackFile") | None = None, title: str | dict | [PlainTextObject](basic_components.html#slack_sdk.models.blocks.basic_components.PlainTextObject "slack_sdk.models.blocks.basic_components.PlainTextObject") | None = None, block_id: str | None = None, **others: dict)` Expand source code ``` class ImageBlock(Block): type = "image" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"alt_text", "image_url", "title", "slack_file"}) image_url_max_length = 3000 alt_text_max_length = 2000 title_max_length = 2000 def __init__( self, *, alt_text: str, image_url: Optional[str] = None, slack_file: Optional[Union[Dict[str, Any], SlackFile]] = None, title: Optional[Union[str, dict, PlainTextObject]] = None, block_id: Optional[str] = None, **others: dict, ): """A simple image block, designed to make those cat photos really pop. https://docs.slack.dev/reference/block-kit/blocks/image-block Args: alt_text (required): A plain-text summary of the image. This should not contain any markup. Maximum length for this field is 2000 characters. image_url: The URL of the image to be displayed. Maximum length for this field is 3000 characters. slack_file: A Slack image file object that defines the source of the image. title: An optional title for the image in the form of a text object that can only be of type: plain_text. Maximum length for the text in this field is 2000 characters. block_id: A string acting as a unique identifier for a block. If not specified, one will be generated. Maximum length for this field is 255 characters. block_id should be unique for each message and each iteration of a message. If a message is updated, use a new block_id. """ super().__init__(type=self.type, block_id=block_id) show_unknown_key_warning(self, others) self.image_url = image_url self.alt_text = alt_text parsed_title = None if title is not None: if isinstance(title, str): parsed_title = PlainTextObject(text=title) elif isinstance(title, dict): if title.get("type") != PlainTextObject.type: raise SlackObjectFormationError(f"Unsupported type for title in an image block: {title.get('type')}") parsed_title = PlainTextObject(text=title.get("text"), emoji=title.get("emoji")) # type: ignore[arg-type] elif isinstance(title, PlainTextObject): parsed_title = title else: raise SlackObjectFormationError(f"Unsupported type for title in an image block: {type(title)}") if slack_file is not None: self.slack_file = ( slack_file if slack_file is None or isinstance(slack_file, SlackFile) else SlackFile(**slack_file) ) self.title = parsed_title @JsonValidator(f"image_url attribute cannot exceed {image_url_max_length} characters") def _validate_image_url_length(self): return self.image_url is None or len(self.image_url) <= self.image_url_max_length @JsonValidator(f"alt_text attribute cannot exceed {alt_text_max_length} characters") def _validate_alt_text_length(self): return len(self.alt_text) <= self.alt_text_max_length @JsonValidator(f"title attribute cannot exceed {title_max_length} characters") def _validate_title_length(self): return self.title is None or self.title.text is None or len(self.title.text) <= self.title_max_length ``` Blocks are a series of components that can be combined to create visually rich and compellingly interactive messages. [https://docs.slack.dev/reference/block-kit/blocks](https://docs.slack.dev/reference/block-kit/blocks) A simple image block, designed to make those cat photos really pop. [https://docs.slack.dev/reference/block-kit/blocks/image-block](https://docs.slack.dev/reference/block-kit/blocks/image-block) ## Args **`alt_text`** : `required` A plain-text summary of the image. This should not contain any markup. Maximum length for this field is 2000 characters. **`image_url`** The URL of the image to be displayed. Maximum length for this field is 3000 characters. **`slack_file`** A Slack image file object that defines the source of the image. **`title`** An optional title for the image in the form of a text object that can only be of type: plain\_text. Maximum length for the text in this field is 2000 characters. **`block_id`** A string acting as a unique identifier for a block. If not specified, one will be generated. Maximum length for this field is 255 characters. block\_id should be unique for each message and each iteration of a message. If a message is updated, use a new block\_id. ### Ancestors * [Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var alt_text_max_length` The type of the None singleton. `var image_url_max_length` The type of the None singleton. `var title_max_length` The type of the None singleton. `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"alt_text", "image_url", "title", "slack_file"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")**`: * `[block_id_max_length](blocks.html#slack_sdk.models.blocks.blocks.Block.block_id_max_length "slack_sdk.models.blocks.blocks.Block.block_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.blocks.Block.get_non_null_attributes")` * `[logger](blocks.html#slack_sdk.models.blocks.blocks.Block.logger "slack_sdk.models.blocks.blocks.Block.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.blocks.Block.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.blocks.Block.validate_json")` `class ImageElement (*, alt_text: str | None = None, image_url: str | None = None, slack_file: Dict[str, Any] | [SlackFile](basic_components.html#slack_sdk.models.blocks.basic_components.SlackFile "slack_sdk.models.blocks.basic_components.SlackFile") | None = None, **others: dict)` Expand source code ``` class ImageElement(BlockElement): type = "image" image_url_max_length = 3000 alt_text_max_length = 2000 @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"alt_text", "image_url", "slack_file"}) def __init__( self, *, alt_text: Optional[str] = None, image_url: Optional[str] = None, slack_file: Optional[Union[Dict[str, Any], SlackFile]] = None, **others: dict, ): """An element to insert an image - this element can be used in section and context blocks only. If you want a block with only an image in it, you're looking for the image block. https://docs.slack.dev/reference/block-kit/block-elements/image-element Args: alt_text (required): A plain-text summary of the image. This should not contain any markup. image_url: The URL of the image to be displayed. slack_file: A Slack image file object that defines the source of the image. """ super().__init__(type=self.type) show_unknown_key_warning(self, others) self.image_url = image_url self.alt_text = alt_text self.slack_file = slack_file if slack_file is None or isinstance(slack_file, SlackFile) else SlackFile(**slack_file) @JsonValidator(f"image_url attribute cannot exceed {image_url_max_length} characters") def _validate_image_url_length(self) -> bool: return self.image_url is None or len(self.image_url) <= self.image_url_max_length @JsonValidator(f"alt_text attribute cannot exceed {alt_text_max_length} characters") def _validate_alt_text_length(self) -> bool: return len(self.alt_text) <= self.alt_text_max_length # type: ignore[arg-type] ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) An element to insert an image - this element can be used in section and context blocks only. If you want a block with only an image in it, you're looking for the image block. [https://docs.slack.dev/reference/block-kit/block-elements/image-element](https://docs.slack.dev/reference/block-kit/block-elements/image-element) ## Args **`alt_text`** : `required` A plain-text summary of the image. This should not contain any markup. **`image_url`** The URL of the image to be displayed. **`slack_file`** A Slack image file object that defines the source of the image. ### Ancestors * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var alt_text_max_length` The type of the None singleton. `var image_url_max_length` The type of the None singleton. `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"alt_text", "image_url", "slack_file"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.BlockElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.BlockElement.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.BlockElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.BlockElement.validate_json")` `class InputBlock (*, label: str | dict | [PlainTextObject](basic_components.html#slack_sdk.models.blocks.basic_components.PlainTextObject "slack_sdk.models.blocks.basic_components.PlainTextObject"), element: str | dict | [InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement"), block_id: str | None = None, hint: str | dict | [PlainTextObject](basic_components.html#slack_sdk.models.blocks.basic_components.PlainTextObject "slack_sdk.models.blocks.basic_components.PlainTextObject") | None = None, dispatch_action: bool | None = None, optional: bool | None = None, **others: dict)` Expand source code ``` class InputBlock(Block): type = "input" label_max_length = 2000 hint_max_length = 2000 @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"label", "hint", "element", "optional", "dispatch_action"}) def __init__( self, *, label: Union[str, dict, PlainTextObject], element: Union[str, dict, InputInteractiveElement], block_id: Optional[str] = None, hint: Optional[Union[str, dict, PlainTextObject]] = None, dispatch_action: Optional[bool] = None, optional: Optional[bool] = None, **others: dict, ): """A block that collects information from users - it can hold a plain-text input element, a select menu element, a multi-select menu element, or a datepicker. https://docs.slack.dev/reference/block-kit/blocks/input-block Args: label (required): A label that appears above an input element in the form of a text object that must have type of plain_text. Maximum length for the text in this field is 2000 characters. element (required): An plain-text input element, a checkbox element, a radio button element, a select menu element, a multi-select menu element, or a datepicker. block_id: A string acting as a unique identifier for a block. If not specified, one will be generated. Maximum length for this field is 255 characters. block_id should be unique for each message or view and each iteration of a message or view. If a message or view is updated, use a new block_id. hint: An optional hint that appears below an input element in a lighter grey. It must be a text object with a type of plain_text. Maximum length for the text in this field is 2000 characters. dispatch_action: A boolean that indicates whether or not the use of elements in this block should dispatch a block_actions payload. Defaults to false. optional: A boolean that indicates whether the input element may be empty when a user submits the modal. Defaults to false. """ super().__init__(type=self.type, block_id=block_id) show_unknown_key_warning(self, others) self.label = TextObject.parse(label, default_type=PlainTextObject.type) self.element = BlockElement.parse(element) # type: ignore[arg-type] self.hint = TextObject.parse(hint, default_type=PlainTextObject.type) # type: ignore[arg-type] self.dispatch_action = dispatch_action self.optional = optional @JsonValidator(f"label attribute cannot exceed {label_max_length} characters") def _validate_label_length(self): return self.label is None or self.label.text is None or len(self.label.text) <= self.label_max_length @JsonValidator(f"hint attribute cannot exceed {hint_max_length} characters") def _validate_hint_length(self): return self.hint is None or self.hint.text is None or len(self.hint.text) <= self.label_max_length @JsonValidator( ( "element attribute must be a string, select element, multi-select element, " "or a datepicker. (Sub-classes of InputInteractiveElement)" ) ) def _validate_element_type(self): return self.element is None or isinstance(self.element, (str, InputInteractiveElement)) ``` Blocks are a series of components that can be combined to create visually rich and compellingly interactive messages. [https://docs.slack.dev/reference/block-kit/blocks](https://docs.slack.dev/reference/block-kit/blocks) A block that collects information from users - it can hold a plain-text input element, a select menu element, a multi-select menu element, or a datepicker. [https://docs.slack.dev/reference/block-kit/blocks/input-block](https://docs.slack.dev/reference/block-kit/blocks/input-block) ## Args **`label`** : `required` A label that appears above an input element in the form of a text object that must have type of plain\_text. Maximum length for the text in this field is 2000 characters. **`element`** : `required` An plain-text input element, a checkbox element, a radio button element, a select menu element, a multi-select menu element, or a datepicker. **`block_id`** A string acting as a unique identifier for a block. If not specified, one will be generated. Maximum length for this field is 255 characters. block\_id should be unique for each message or view and each iteration of a message or view. If a message or view is updated, use a new block\_id. **`hint`** An optional hint that appears below an input element in a lighter grey. It must be a text object with a type of plain\_text. Maximum length for the text in this field is 2000 characters. **`dispatch_action`** A boolean that indicates whether or not the use of elements in this block should dispatch a block\_actions payload. Defaults to false. **`optional`** A boolean that indicates whether the input element may be empty when a user submits the modal. Defaults to false. ### Ancestors * [Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var hint_max_length` The type of the None singleton. `var label_max_length` The type of the None singleton. `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"label", "hint", "element", "optional", "dispatch_action"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")**`: * `[block_id_max_length](blocks.html#slack_sdk.models.blocks.blocks.Block.block_id_max_length "slack_sdk.models.blocks.blocks.Block.block_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.blocks.Block.get_non_null_attributes")` * `[logger](blocks.html#slack_sdk.models.blocks.blocks.Block.logger "slack_sdk.models.blocks.blocks.Block.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.blocks.Block.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.blocks.Block.validate_json")` `class InputInteractiveElement (*, action_id: str | None = None, placeholder: str | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject") | None = None, type: str | None = None, subtype: str | None = None, confirm: dict | [ConfirmObject](basic_components.html#slack_sdk.models.blocks.basic_components.ConfirmObject "slack_sdk.models.blocks.basic_components.ConfirmObject") | None = None, focus_on_load: bool | None = None, **others: dict)` Expand source code ``` class InputInteractiveElement(InteractiveElement, metaclass=ABCMeta): placeholder_max_length = 150 attributes = {"type", "action_id", "placeholder", "confirm", "focus_on_load"} @property def subtype(self) -> Optional[str]: return self.type def __init__( self, *, action_id: Optional[str] = None, placeholder: Optional[Union[str, TextObject]] = None, type: Optional[str] = None, subtype: Optional[str] = None, confirm: Optional[Union[dict, ConfirmObject]] = None, focus_on_load: Optional[bool] = None, **others: dict, ): """InteractiveElement that is usable in input blocks We generally recommend using the concrete subclasses for better supports of available properties. """ if subtype: self._subtype_warning() super().__init__(action_id=action_id, type=type or subtype) # Note that we don't intentionally have show_unknown_key_warning for the unknown key warnings here. # It's fine to pass any kwargs to the held dict here although the class does not do any validation. # show_unknown_key_warning(self, others) self.placeholder = TextObject.parse(placeholder) # type: ignore[arg-type] self.confirm = ConfirmObject.parse(confirm) # type: ignore[arg-type] self.focus_on_load = focus_on_load @JsonValidator(f"placeholder attribute cannot exceed {placeholder_max_length} characters") def _validate_placeholder_length(self) -> bool: return ( self.placeholder is None or self.placeholder.text is None or len(self.placeholder.text) <= self.placeholder_max_length ) ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) InteractiveElement that is usable in input blocks We generally recommend using the concrete subclasses for better supports of available properties. ### Ancestors * [InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement") * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Subclasses * [ChannelMultiSelectElement](block_elements.html#slack_sdk.models.blocks.block_elements.ChannelMultiSelectElement "slack_sdk.models.blocks.block_elements.ChannelMultiSelectElement") * [ChannelSelectElement](block_elements.html#slack_sdk.models.blocks.block_elements.ChannelSelectElement "slack_sdk.models.blocks.block_elements.ChannelSelectElement") * [CheckboxesElement](block_elements.html#slack_sdk.models.blocks.block_elements.CheckboxesElement "slack_sdk.models.blocks.block_elements.CheckboxesElement") * [ConversationMultiSelectElement](block_elements.html#slack_sdk.models.blocks.block_elements.ConversationMultiSelectElement "slack_sdk.models.blocks.block_elements.ConversationMultiSelectElement") * [ConversationSelectElement](block_elements.html#slack_sdk.models.blocks.block_elements.ConversationSelectElement "slack_sdk.models.blocks.block_elements.ConversationSelectElement") * [DatePickerElement](block_elements.html#slack_sdk.models.blocks.block_elements.DatePickerElement "slack_sdk.models.blocks.block_elements.DatePickerElement") * [DateTimePickerElement](block_elements.html#slack_sdk.models.blocks.block_elements.DateTimePickerElement "slack_sdk.models.blocks.block_elements.DateTimePickerElement") * [EmailInputElement](block_elements.html#slack_sdk.models.blocks.block_elements.EmailInputElement "slack_sdk.models.blocks.block_elements.EmailInputElement") * [ExternalDataMultiSelectElement](block_elements.html#slack_sdk.models.blocks.block_elements.ExternalDataMultiSelectElement "slack_sdk.models.blocks.block_elements.ExternalDataMultiSelectElement") * [ExternalDataSelectElement](block_elements.html#slack_sdk.models.blocks.block_elements.ExternalDataSelectElement "slack_sdk.models.blocks.block_elements.ExternalDataSelectElement") * [FileInputElement](block_elements.html#slack_sdk.models.blocks.block_elements.FileInputElement "slack_sdk.models.blocks.block_elements.FileInputElement") * [NumberInputElement](block_elements.html#slack_sdk.models.blocks.block_elements.NumberInputElement "slack_sdk.models.blocks.block_elements.NumberInputElement") * [PlainTextInputElement](block_elements.html#slack_sdk.models.blocks.block_elements.PlainTextInputElement "slack_sdk.models.blocks.block_elements.PlainTextInputElement") * [RadioButtonsElement](block_elements.html#slack_sdk.models.blocks.block_elements.RadioButtonsElement "slack_sdk.models.blocks.block_elements.RadioButtonsElement") * [RichTextInputElement](block_elements.html#slack_sdk.models.blocks.block_elements.RichTextInputElement "slack_sdk.models.blocks.block_elements.RichTextInputElement") * [SelectElement](block_elements.html#slack_sdk.models.blocks.block_elements.SelectElement "slack_sdk.models.blocks.block_elements.SelectElement") * [StaticMultiSelectElement](block_elements.html#slack_sdk.models.blocks.block_elements.StaticMultiSelectElement "slack_sdk.models.blocks.block_elements.StaticMultiSelectElement") * [StaticSelectElement](block_elements.html#slack_sdk.models.blocks.block_elements.StaticSelectElement "slack_sdk.models.blocks.block_elements.StaticSelectElement") * [TimePickerElement](block_elements.html#slack_sdk.models.blocks.block_elements.TimePickerElement "slack_sdk.models.blocks.block_elements.TimePickerElement") * [UrlInputElement](block_elements.html#slack_sdk.models.blocks.block_elements.UrlInputElement "slack_sdk.models.blocks.block_elements.UrlInputElement") * [UserMultiSelectElement](block_elements.html#slack_sdk.models.blocks.block_elements.UserMultiSelectElement "slack_sdk.models.blocks.block_elements.UserMultiSelectElement") * [UserSelectElement](block_elements.html#slack_sdk.models.blocks.block_elements.UserSelectElement "slack_sdk.models.blocks.block_elements.UserSelectElement") ### Class variables `var attributes` The type of the None singleton. `var placeholder_max_length` The type of the None singleton. ### Instance variables `prop subtype : str | None` Expand source code ``` @property def subtype(self) -> Optional[str]: return self.type ``` ### Inherited members * `**[InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement")**`: * `[action_id_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement.action_id_max_length "slack_sdk.models.blocks.block_elements.InteractiveElement.action_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.InteractiveElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.InteractiveElement.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.InteractiveElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.InteractiveElement.validate_json")` `class InteractiveElement (*, action_id: str | None = None, type: str | None = None, subtype: str | None = None, **others: dict)` Expand source code ``` class InteractiveElement(BlockElement): action_id_max_length = 255 @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"alt_text", "action_id"}) def __init__( self, *, action_id: Optional[str] = None, type: Optional[str] = None, subtype: Optional[str] = None, **others: dict, ): """An interactive block element. We generally recommend using the concrete subclasses for better supports of available properties. """ if subtype: self._subtype_warning() super().__init__(type=type or subtype) # Note that we don't intentionally have show_unknown_key_warning for the unknown key warnings here. # It's fine to pass any kwargs to the held dict here although the class does not do any validation. # show_unknown_key_warning(self, others) self.action_id = action_id @JsonValidator(f"action_id attribute cannot exceed {action_id_max_length} characters") def _validate_action_id_length(self) -> bool: return self.action_id is None or len(self.action_id) <= self.action_id_max_length ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) An interactive block element. We generally recommend using the concrete subclasses for better supports of available properties. ### Ancestors * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Subclasses * [ButtonElement](block_elements.html#slack_sdk.models.blocks.block_elements.ButtonElement "slack_sdk.models.blocks.block_elements.ButtonElement") * [FeedbackButtonsElement](block_elements.html#slack_sdk.models.blocks.block_elements.FeedbackButtonsElement "slack_sdk.models.blocks.block_elements.FeedbackButtonsElement") * [IconButtonElement](block_elements.html#slack_sdk.models.blocks.block_elements.IconButtonElement "slack_sdk.models.blocks.block_elements.IconButtonElement") * [InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement") * [OverflowMenuElement](block_elements.html#slack_sdk.models.blocks.block_elements.OverflowMenuElement "slack_sdk.models.blocks.block_elements.OverflowMenuElement") * [WorkflowButtonElement](block_elements.html#slack_sdk.models.blocks.block_elements.WorkflowButtonElement "slack_sdk.models.blocks.block_elements.WorkflowButtonElement") ### Class variables `var action_id_max_length` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"alt_text", "action_id"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.BlockElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.BlockElement.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.BlockElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.BlockElement.validate_json")` `class LinkButtonElement (*, text: str | dict | [PlainTextObject](basic_components.html#slack_sdk.models.blocks.basic_components.PlainTextObject "slack_sdk.models.blocks.basic_components.PlainTextObject"), url: str, action_id: str | None = None, style: str | None = None, **others: dict)` Expand source code ``` class LinkButtonElement(ButtonElement): def __init__( self, *, text: Union[str, dict, PlainTextObject], url: str, action_id: Optional[str] = None, style: Optional[str] = None, **others: dict, ): """A simple button that simply opens a given URL. You will still receive an interaction payload and will need to send an acknowledgement response. This is a helper class that makes creating links simpler. https://docs.slack.dev/reference/block-kit/block-elements/button-element/ Args: text (required): A text object that defines the button's text. Can only be of type: plain_text. Maximum length for the text in this field is 75 characters. url (required): A URL to load in the user's browser when the button is clicked. Maximum length for this field is 3000 characters. If you're using url, you'll still receive an interaction payload and will need to send an acknowledgement response. action_id (required): An identifier for this action. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action_ids in the containing block. Maximum length for this field is 255 characters. style: Decorates buttons with alternative visual color schemes. Use this option with restraint. "primary" gives buttons a green outline and text, ideal for affirmation or confirmation actions. "primary" should only be used for one button within a set. "danger" gives buttons a red outline and text, and should be used when the action is destructive. Use "danger" even more sparingly than "primary". If you don't include this field, the default button style will be used. """ super().__init__( # NOTE: value must be always absent text=text, url=url, action_id=action_id, value=None, style=style, ) show_unknown_key_warning(self, others) ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) A simple button that simply opens a given URL. You will still receive an interaction payload and will need to send an acknowledgement response. This is a helper class that makes creating links simpler. [https://docs.slack.dev/reference/block-kit/block-elements/button-element/](https://docs.slack.dev/reference/block-kit/block-elements/button-element/) ## Args **`text`** : `required` A text object that defines the button's text. Can only be of type: plain\_text. Maximum length for the text in this field is 75 characters. **`url`** : `required` A URL to load in the user's browser when the button is clicked. Maximum length for this field is 3000 characters. If you're using url, you'll still receive an interaction payload and will need to send an acknowledgement response. **`action_id`** : `required` An identifier for this action. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action\_ids in the containing block. Maximum length for this field is 255 characters. **`style`** Decorates buttons with alternative visual color schemes. Use this option with restraint. "primary" gives buttons a green outline and text, ideal for affirmation or confirmation actions. "primary" should only be used for one button within a set. "danger" gives buttons a red outline and text, and should be used when the action is destructive. Use "danger" even more sparingly than "primary". If you don't include this field, the default button style will be used. ### Ancestors * [ButtonElement](block_elements.html#slack_sdk.models.blocks.block_elements.ButtonElement "slack_sdk.models.blocks.block_elements.ButtonElement") * [InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement") * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Inherited members * `**[ButtonElement](block_elements.html#slack_sdk.models.blocks.block_elements.ButtonElement "slack_sdk.models.blocks.block_elements.ButtonElement")**`: * `[action_id_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement.action_id_max_length "slack_sdk.models.blocks.block_elements.ButtonElement.action_id_max_length")` * `[attributes](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement.attributes "slack_sdk.models.blocks.block_elements.ButtonElement.attributes")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.ButtonElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.ButtonElement.logger")` * `[text_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.ButtonElement.text_max_length "slack_sdk.models.blocks.block_elements.ButtonElement.text_max_length")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.ButtonElement.to_dict")` * `[type](block_elements.html#slack_sdk.models.blocks.block_elements.ButtonElement.type "slack_sdk.models.blocks.block_elements.ButtonElement.type")` * `[url_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.ButtonElement.url_max_length "slack_sdk.models.blocks.block_elements.ButtonElement.url_max_length")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.ButtonElement.validate_json")` * `[value_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.ButtonElement.value_max_length "slack_sdk.models.blocks.block_elements.ButtonElement.value_max_length")` `class MarkdownBlock (*, text: str, block_id: str | None = None, **others: dict)` Expand source code ``` class MarkdownBlock(Block): type = "markdown" text_max_length = 12000 @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"text"}) def __init__( self, *, text: str, block_id: Optional[str] = None, **others: dict, ): """Displays formatted markdown. https://docs.slack.dev/reference/block-kit/blocks/markdown-block/ Args: block_id: A string acting as a unique identifier for a block. If not specified, one will be generated. Maximum length for this field is 255 characters. block_id should be unique for each message and each iteration of a message. If a message is updated, use a new block_id. text (required): The standard markdown-formatted text. Limit 12,000 characters max. """ super().__init__(type=self.type, block_id=block_id) show_unknown_key_warning(self, others) self.text = text @JsonValidator("text attribute must be specified") def _validate_text(self): return self.text != "" @JsonValidator(f"text attribute cannot exceed {text_max_length} characters") def _validate_alt_text_length(self): return len(self.text) <= self.text_max_length ``` Blocks are a series of components that can be combined to create visually rich and compellingly interactive messages. [https://docs.slack.dev/reference/block-kit/blocks](https://docs.slack.dev/reference/block-kit/blocks) Displays formatted markdown. [https://docs.slack.dev/reference/block-kit/blocks/markdown-block/](https://docs.slack.dev/reference/block-kit/blocks/markdown-block/) ## Args **`block_id`** A string acting as a unique identifier for a block. If not specified, one will be generated. Maximum length for this field is 255 characters. block\_id should be unique for each message and each iteration of a message. If a message is updated, use a new block\_id. **`text`** : `required` The standard markdown-formatted text. Limit 12,000 characters max. ### Ancestors * [Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var text_max_length` The type of the None singleton. `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"text"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")**`: * `[block_id_max_length](blocks.html#slack_sdk.models.blocks.blocks.Block.block_id_max_length "slack_sdk.models.blocks.blocks.Block.block_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.blocks.Block.get_non_null_attributes")` * `[logger](blocks.html#slack_sdk.models.blocks.blocks.Block.logger "slack_sdk.models.blocks.blocks.Block.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.blocks.Block.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.blocks.Block.validate_json")` `class MarkdownTextObject (*, text: str, verbatim: bool | None = None)` Expand source code ``` class MarkdownTextObject(TextObject): """mrkdwn typed text object""" type = "mrkdwn" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"verbatim"}) def __init__(self, *, text: str, verbatim: Optional[bool] = None): """A Markdown text object, meaning markdown characters will be parsed as formatting information. https://docs.slack.dev/reference/block-kit/composition-objects/text-object Args: text (required): The text for the block. This field accepts any of the standard text formatting markup when type is mrkdwn. verbatim: When set to false (as is default) URLs will be auto-converted into links, conversation names will be link-ified, and certain mentions will be automatically parsed. Using a value of true will skip any preprocessing of this nature, although you can still include manual parsing strings. This field is only usable when type is mrkdwn. """ super().__init__(text=text, type=self.type) self.verbatim = verbatim @staticmethod def from_str(text: str) -> "MarkdownTextObject": """Transforms a string into the required object shape to act as a MarkdownTextObject""" return MarkdownTextObject(text=text) @staticmethod def direct_from_string(text: str) -> Dict[str, Any]: """Transforms a string into the required object shape to act as a MarkdownTextObject""" return MarkdownTextObject.from_str(text).to_dict() @staticmethod def from_link(link: Link, title: str = "") -> "MarkdownTextObject": """ Transform a Link object directly into the required object shape to act as a MarkdownTextObject """ if title: title = f": {title}" return MarkdownTextObject(text=f"{link}{title}") @staticmethod def direct_from_link(link: Link, title: str = "") -> Dict[str, Any]: """ Transform a Link object directly into the required object shape to act as a MarkdownTextObject """ return MarkdownTextObject.from_link(link, title).to_dict() ``` mrkdwn typed text object A Markdown text object, meaning markdown characters will be parsed as formatting information. [https://docs.slack.dev/reference/block-kit/composition-objects/text-object](https://docs.slack.dev/reference/block-kit/composition-objects/text-object) ## Args **`text`** : `required` The text for the block. This field accepts any of the standard text formatting markup when type is mrkdwn. **`verbatim`** When set to false (as is default) URLs will be auto-converted into links, conversation names will be link-ified, and certain mentions will be automatically parsed. Using a value of true will skip any preprocessing of this nature, although you can still include manual parsing strings. This field is only usable when type is mrkdwn. ### Ancestors * [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Static methods `def direct_from_link(link: [Link](../messages/index.html#slack_sdk.models.messages.Link "slack_sdk.models.messages.Link"), title: str = '') ‑> Dict[str, Any]` Expand source code ``` @staticmethod def direct_from_link(link: Link, title: str = "") -> Dict[str, Any]: """ Transform a Link object directly into the required object shape to act as a MarkdownTextObject """ return MarkdownTextObject.from_link(link, title).to_dict() ``` Transform a Link object directly into the required object shape to act as a MarkdownTextObject `def direct_from_string(text: str) ‑> Dict[str, Any]` Expand source code ``` @staticmethod def direct_from_string(text: str) -> Dict[str, Any]: """Transforms a string into the required object shape to act as a MarkdownTextObject""" return MarkdownTextObject.from_str(text).to_dict() ``` Transforms a string into the required object shape to act as a MarkdownTextObject `def from_link(link: [Link](../messages/index.html#slack_sdk.models.messages.Link "slack_sdk.models.messages.Link"), title: str = '') ‑> [MarkdownTextObject](basic_components.html#slack_sdk.models.blocks.basic_components.MarkdownTextObject "slack_sdk.models.blocks.basic_components.MarkdownTextObject")` Expand source code ``` @staticmethod def from_link(link: Link, title: str = "") -> "MarkdownTextObject": """ Transform a Link object directly into the required object shape to act as a MarkdownTextObject """ if title: title = f": {title}" return MarkdownTextObject(text=f"{link}{title}") ``` Transform a Link object directly into the required object shape to act as a MarkdownTextObject `def from_str(text: str) ‑> [MarkdownTextObject](basic_components.html#slack_sdk.models.blocks.basic_components.MarkdownTextObject "slack_sdk.models.blocks.basic_components.MarkdownTextObject")` Expand source code ``` @staticmethod def from_str(text: str) -> "MarkdownTextObject": """Transforms a string into the required object shape to act as a MarkdownTextObject""" return MarkdownTextObject(text=text) ``` Transforms a string into the required object shape to act as a MarkdownTextObject ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"verbatim"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.basic_components.TextObject.get_non_null_attributes")` * `[logger](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject.logger "slack_sdk.models.blocks.basic_components.TextObject.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.basic_components.TextObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.basic_components.TextObject.validate_json")` `class NumberInputElement (*, action_id: str | None = None, is_decimal_allowed: bool | None = False, initial_value: int | float | str | None = None, min_value: int | float | str | None = None, max_value: int | float | str | None = None, dispatch_action_config: dict | [DispatchActionConfig](basic_components.html#slack_sdk.models.blocks.basic_components.DispatchActionConfig "slack_sdk.models.blocks.basic_components.DispatchActionConfig") | None = None, focus_on_load: bool | None = None, placeholder: str | dict | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject") | None = None, **others: dict)` Expand source code ``` class NumberInputElement(InputInteractiveElement): type = "number_input" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union( { "initial_value", "is_decimal_allowed", "min_value", "max_value", "dispatch_action_config", } ) def __init__( self, *, action_id: Optional[str] = None, is_decimal_allowed: Optional[bool] = False, initial_value: Optional[Union[int, float, str]] = None, min_value: Optional[Union[int, float, str]] = None, max_value: Optional[Union[int, float, str]] = None, dispatch_action_config: Optional[Union[dict, DispatchActionConfig]] = None, focus_on_load: Optional[bool] = None, placeholder: Optional[Union[str, dict, TextObject]] = None, **others: dict, ): """ https://docs.slack.dev/reference/block-kit/block-elements/number-input-element/ Args: action_id (required): An identifier for the input value when the parent modal is submitted. You can use this when you receive a view_submission payload to identify the value of the input element. Should be unique among all other action_ids in the containing block. Maximum length for this field is 255 characters. is_decimal_allowed (required): Decimal numbers are allowed if is_decimal_allowed= true, set the value to false otherwise. initial_value: The initial value in the number input when it is loaded. min_value: The minimum value, cannot be greater than max_value. max_value: The maximum value, cannot be less than min_value. dispatch_action_config: A dispatch configuration object that determines when during text input the element returns a block_actions payload. focus_on_load: Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. placeholder: A plain_text only text object that defines the placeholder text shown in the plain-text input. Maximum length for the text in this field is 150 characters. """ super().__init__( type=self.type, action_id=action_id, placeholder=TextObject.parse(placeholder, PlainTextObject.type), # type: ignore[arg-type] focus_on_load=focus_on_load, ) show_unknown_key_warning(self, others) self.initial_value = str(initial_value) if initial_value is not None else None self.is_decimal_allowed = is_decimal_allowed self.min_value = str(min_value) if min_value is not None else None self.max_value = str(max_value) if max_value is not None else None self.dispatch_action_config = dispatch_action_config ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) [https://docs.slack.dev/reference/block-kit/block-elements/number-input-element/](https://docs.slack.dev/reference/block-kit/block-elements/number-input-element/) ## Args **`action_id`** : `required` An identifier for the input value when the parent modal is submitted. You can use this when you receive a view\_submission payload to identify the value of the input element. Should be unique among all other action\_ids in the containing block. Maximum length for this field is 255 characters. **`is_decimal_allowed`** : `required` Decimal numbers are allowed if is\_decimal\_allowed= true, set the value to false otherwise. **`initial_value`** The initial value in the number input when it is loaded. **`min_value`** The minimum value, cannot be greater than max\_value. **`max_value`** The maximum value, cannot be less than min\_value. **`dispatch_action_config`** A dispatch configuration object that determines when during text input the element returns a block\_actions payload. **`focus_on_load`** Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. **`placeholder`** A plain\_text only text object that defines the placeholder text shown in the plain-text input. Maximum length for the text in this field is 150 characters. ### Ancestors * [InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement") * [InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement") * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union( { "initial_value", "is_decimal_allowed", "min_value", "max_value", "dispatch_action_config", } ) ``` Build an unordered collection of unique elements. ### Inherited members * `**[InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement")**`: * `[action_id_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement.action_id_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.action_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.InputInteractiveElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.InputInteractiveElement.logger")` * `[placeholder_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.InputInteractiveElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.InputInteractiveElement.validate_json")` `class Option (*, value: str, label: str | None = None, text: str | Dict[str, Any] | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject") | None = None, description: str | Dict[str, Any] | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject") | None = None, url: str | None = None, **others: Dict[str, Any])` Expand source code ``` class Option(JsonObject): """Option object used in dialogs, legacy message actions (interactivity in attachments), and blocks. JSON must be retrieved with an explicit option_type - the Slack API has different required formats in different situations """ attributes: Set[str] = set() logger = logging.getLogger(__name__) label_max_length = 75 value_max_length = 150 def __init__( self, *, value: str, label: Optional[str] = None, text: Optional[Union[str, Dict[str, Any], TextObject]] = None, # Block Kit description: Optional[Union[str, Dict[str, Any], TextObject]] = None, url: Optional[str] = None, **others: Dict[str, Any], ): """ An object that represents a single selectable item in a block element ( SelectElement, OverflowMenuElement) or dialog element (StaticDialogSelectElement) Blocks: https://docs.slack.dev/reference/block-kit/composition-objects/option-object Dialogs: https://docs.slack.dev/legacy/legacy-dialogs/#select_elements Legacy interactive attachments: https://docs.slack.dev/legacy/legacy-messaging/legacy-interactive-message-field-guide/#option_fields Args: label: A short, user-facing string to label this option to users. Cannot exceed 75 characters. value: A short string that identifies this particular option to your application. It will be part of the payload when this option is selected . Cannot exceed 150 characters. description: A user-facing string that provides more details about this option. Only supported in legacy message actions, not in blocks or dialogs. """ if text: # For better compatibility with Block Kit ("mrkdwn" does not work for it), # we've changed the default text object type to plain_text since version 3.10.0 self._text: Optional[TextObject] = TextObject.parse( text=text, # "text" here can be either a str or a TextObject default_type=PlainTextObject.type, ) self._label: Optional[str] = None else: self._text = None self._label = label # for backward-compatibility with version 2.0-2.5, the following fields return str values self.text: Optional[str] = self._text.text if self._text else None self.label: Optional[str] = self._label self.value: str = value # for backward-compatibility with version 2.0-2.5, the following fields return str values if isinstance(description, str): self.description = description self._block_description = PlainTextObject.from_str(description) elif isinstance(description, dict): self.description = description["text"] self._block_description = TextObject.parse(description) # type: ignore[assignment] elif isinstance(description, TextObject): self.description = description.text self._block_description = description # type: ignore[assignment] else: self.description = None # type: ignore[assignment] self._block_description = None # type: ignore[assignment] # A URL to load in the user's browser when the option is clicked. # The url attribute is only available in overflow menus. # Maximum length for this field is 3000 characters. # If you're using url, you'll still receive an interaction payload # and will need to send an acknowledgement response. self.url: Optional[str] = url show_unknown_key_warning(self, others) @JsonValidator(f"label attribute cannot exceed {label_max_length} characters") def _validate_label_length(self) -> bool: return self._label is None or len(self._label) <= self.label_max_length @JsonValidator(f"text attribute cannot exceed {label_max_length} characters") def _validate_text_length(self) -> bool: return self._text is None or self._text.text is None or len(self._text.text) <= self.label_max_length @JsonValidator(f"value attribute cannot exceed {value_max_length} characters") def _validate_value_length(self) -> bool: return len(self.value) <= self.value_max_length @classmethod def parse_all(cls, options: Optional[Sequence[Union[Dict[str, Any], "Option"]]]) -> Optional[List["Option"]]: if options is None: return None option_objects: List[Option] = [] for o in options: if isinstance(o, dict): d = copy.copy(o) option_objects.append(Option(**d)) elif isinstance(o, Option): option_objects.append(o) else: cls.logger.warning(f"Unknown option object detected and skipped ({o})") return option_objects def to_dict(self, option_type: str = "block") -> Dict[str, Any]: """ Different parent classes must call this with a valid value from OptionTypes - either "dialog", "action", or "block", so that JSON is returned in the correct shape. """ self.validate_json() if option_type == "dialog": return {"label": self.label, "value": self.value} elif option_type == "action" or option_type == "attachment": # "action" can be confusing but it means a legacy message action in attachments # we don't remove the type name for backward compatibility though json: Dict[str, Any] = {"text": self.label, "value": self.value} if self.description is not None: json["description"] = self.description return json else: # if option_type == "block"; this should be the most common case text: TextObject = self._text or PlainTextObject.from_str(self.label) # type: ignore[arg-type] json = { "text": text.to_dict(), "value": self.value, } if self._block_description: json["description"] = self._block_description.to_dict() if self.url: json["url"] = self.url return json @staticmethod def from_single_value(value_and_label: str): """Creates a simple Option instance with the same value and label""" return Option(value=value_and_label, label=value_and_label) ``` Option object used in dialogs, legacy message actions (interactivity in attachments), and blocks. JSON must be retrieved with an explicit option\_type - the Slack API has different required formats in different situations An object that represents a single selectable item in a block element ( SelectElement, OverflowMenuElement) or dialog element (StaticDialogSelectElement) Blocks: [https://docs.slack.dev/reference/block-kit/composition-objects/option-object](https://docs.slack.dev/reference/block-kit/composition-objects/option-object) Dialogs: [https://docs.slack.dev/legacy/legacy-dialogs/#select\_elements](https://docs.slack.dev/legacy/legacy-dialogs/#select_elements) Legacy interactive attachments: [https://docs.slack.dev/legacy/legacy-messaging/legacy-interactive-message-field-guide/#option\_fields](https://docs.slack.dev/legacy/legacy-messaging/legacy-interactive-message-field-guide/#option_fields) ## Args **`label`** A short, user-facing string to label this option to users. Cannot exceed 75 characters. **`value`** A short string that identifies this particular option to your application. It will be part of the payload when this option is selected . Cannot exceed 150 characters. **`description`** A user-facing string that provides more details about this option. Only supported in legacy message actions, not in blocks or dialogs. ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes : Set[str]` The type of the None singleton. `var label_max_length` The type of the None singleton. `var logger` The type of the None singleton. `var value_max_length` The type of the None singleton. ### Static methods `def from_single_value(value_and_label: str)` Expand source code ``` @staticmethod def from_single_value(value_and_label: str): """Creates a simple Option instance with the same value and label""" return Option(value=value_and_label, label=value_and_label) ``` Creates a simple Option instance with the same value and label `def parse_all(options: Sequence[Dict[str, Any] | ForwardRef('[Option](#slack_sdk.models.blocks.Option "slack_sdk.models.blocks.Option")')] | None) ‑> List[[Option](basic_components.html#slack_sdk.models.blocks.basic_components.Option "slack_sdk.models.blocks.basic_components.Option")] | None` ### Methods `def to_dict(self, option_type: str = 'block') ‑> Dict[str, Any]` Expand source code ``` def to_dict(self, option_type: str = "block") -> Dict[str, Any]: """ Different parent classes must call this with a valid value from OptionTypes - either "dialog", "action", or "block", so that JSON is returned in the correct shape. """ self.validate_json() if option_type == "dialog": return {"label": self.label, "value": self.value} elif option_type == "action" or option_type == "attachment": # "action" can be confusing but it means a legacy message action in attachments # we don't remove the type name for backward compatibility though json: Dict[str, Any] = {"text": self.label, "value": self.value} if self.description is not None: json["description"] = self.description return json else: # if option_type == "block"; this should be the most common case text: TextObject = self._text or PlainTextObject.from_str(self.label) # type: ignore[arg-type] json = { "text": text.to_dict(), "value": self.value, } if self._block_description: json["description"] = self._block_description.to_dict() if self.url: json["url"] = self.url return json ``` Different parent classes must call this with a valid value from OptionTypes - either "dialog", "action", or "block", so that JSON is returned in the correct shape. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class OptionGroup (*, label: str | Dict[str, Any] | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject") | None = None, options: Sequence[Dict[str, Any] | [Option](basic_components.html#slack_sdk.models.blocks.basic_components.Option "slack_sdk.models.blocks.basic_components.Option")], **others: Dict[str, Any])` Expand source code ``` class OptionGroup(JsonObject): """ JSON must be retrieved with an explicit option_type - the Slack API has different required formats in different situations """ attributes: Set[str] = set() label_max_length = 75 options_max_length = 100 logger = logging.getLogger(__name__) def __init__( self, *, label: Optional[Union[str, Dict[str, Any], TextObject]] = None, options: Sequence[Union[Dict[str, Any], Option]], **others: Dict[str, Any], ): """ Create a group of Option objects - pass in a label (that will be part of the UI) and a list of Option objects. Blocks: https://docs.slack.dev/reference/block-kit/composition-objects/option-group-object Dialogs: https://docs.slack.dev/legacy/legacy-dialogs/#select_elements Legacy interactive attachments: https://docs.slack.dev/legacy/legacy-messaging/legacy-interactive-message-field-guide/#option_groups Args: label: Text to display at the top of this group of options. options: A list of no more than 100 Option objects. """ # noqa prevent flake8 blowing up on the long URL # default_type=PlainTextObject.type is for backward-compatibility self._label: Optional[TextObject] = TextObject.parse(label, default_type=PlainTextObject.type) # type: ignore[arg-type] # noqa: E501 self.label: Optional[str] = self._label.text if self._label else None self.options = Option.parse_all(options) # compatible with version 2.5 show_unknown_key_warning(self, others) @JsonValidator(f"label attribute cannot exceed {label_max_length} characters") def _validate_label_length(self): return self.label is None or len(self.label) <= self.label_max_length @JsonValidator(f"options attribute cannot exceed {options_max_length} elements") def _validate_options_length(self): return self.options is None or len(self.options) <= self.options_max_length @classmethod def parse_all( cls, option_groups: Optional[Sequence[Union[Dict[str, Any], "OptionGroup"]]] ) -> Optional[List["OptionGroup"]]: if option_groups is None: return None option_group_objects = [] for o in option_groups: if isinstance(o, dict): d = copy.copy(o) option_group_objects.append(OptionGroup(**d)) elif isinstance(o, OptionGroup): option_group_objects.append(o) else: cls.logger.warning(f"Unknown option group object detected and skipped ({o})") return option_group_objects def to_dict(self, option_type: str = "block") -> Dict[str, Any]: self.validate_json() dict_options = [o.to_dict(option_type) for o in self.options] # type: ignore[union-attr] if option_type == "dialog": return { "label": self.label, "options": dict_options, } elif option_type == "action": return { "text": self.label, "options": dict_options, } else: # if option_type == "block"; this should be the most common case dict_label: Dict[str, Any] = self._label.to_dict() # type: ignore[union-attr] return { "label": dict_label, "options": dict_options, } ``` JSON must be retrieved with an explicit option\_type - the Slack API has different required formats in different situations Create a group of Option objects - pass in a label (that will be part of the UI) and a list of Option objects. Blocks: [https://docs.slack.dev/reference/block-kit/composition-objects/option-group-object](https://docs.slack.dev/reference/block-kit/composition-objects/option-group-object) Dialogs: [https://docs.slack.dev/legacy/legacy-dialogs/#select\_elements](https://docs.slack.dev/legacy/legacy-dialogs/#select_elements) Legacy interactive attachments: [https://docs.slack.dev/legacy/legacy-messaging/legacy-interactive-message-field-guide/#option\_groups](https://docs.slack.dev/legacy/legacy-messaging/legacy-interactive-message-field-guide/#option_groups) ## Args **`label`** Text to display at the top of this group of options. **`options`** A list of no more than 100 Option objects. ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes : Set[str]` The type of the None singleton. `var label_max_length` The type of the None singleton. `var logger` The type of the None singleton. `var options_max_length` The type of the None singleton. ### Static methods `def parse_all(option_groups: Sequence[Dict[str, Any] | ForwardRef('[OptionGroup](#slack_sdk.models.blocks.OptionGroup "slack_sdk.models.blocks.OptionGroup")')] | None) ‑> List[[OptionGroup](basic_components.html#slack_sdk.models.blocks.basic_components.OptionGroup "slack_sdk.models.blocks.basic_components.OptionGroup")] | None` ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class OverflowMenuElement (*, action_id: str | None = None, options: Sequence[[Option](basic_components.html#slack_sdk.models.blocks.basic_components.Option "slack_sdk.models.blocks.basic_components.Option")], confirm: dict | [ConfirmObject](basic_components.html#slack_sdk.models.blocks.basic_components.ConfirmObject "slack_sdk.models.blocks.basic_components.ConfirmObject") | None = None, **others: dict)` Expand source code ``` class OverflowMenuElement(InteractiveElement): type = "overflow" options_min_length = 1 options_max_length = 5 @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"confirm", "options"}) def __init__( self, *, action_id: Optional[str] = None, options: Sequence[Option], confirm: Optional[Union[dict, ConfirmObject]] = None, **others: dict, ): """ This is like a cross between a button and a select menu - when a user clicks on this overflow button, they will be presented with a list of options to choose from. Unlike the select menu, there is no typeahead field, and the button always appears with an ellipsis ("…") rather than customisable text. As such, it is usually used if you want a more compact layout than a select menu, or to supply a list of less visually important actions after a row of buttons. You can also specify simple URL links as overflow menu options, instead of actions. https://docs.slack.dev/reference/block-kit/block-elements/overflow-menu-element Args: action_id (required): An identifier for the action triggered when a menu option is selected. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action_ids in the containing block. Maximum length for this field is 255 characters. options (required): An array of option objects to display in the menu. Maximum number of options is 5, minimum is 1. confirm: A confirm object that defines an optional confirmation dialog that appears after a menu item is selected. """ super().__init__(action_id=action_id, type=self.type) show_unknown_key_warning(self, others) self.options = options self.confirm = ConfirmObject.parse(confirm) # type: ignore[arg-type] @JsonValidator(f"options attribute must have between {options_min_length} " f"and {options_max_length} items") def _validate_options_length(self) -> bool: return self.options_min_length <= len(self.options) <= self.options_max_length ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) This is like a cross between a button and a select menu - when a user clicks on this overflow button, they will be presented with a list of options to choose from. Unlike the select menu, there is no typeahead field, and the button always appears with an ellipsis ("…") rather than customisable text. As such, it is usually used if you want a more compact layout than a select menu, or to supply a list of less visually important actions after a row of buttons. You can also specify simple URL links as overflow menu options, instead of actions. [https://docs.slack.dev/reference/block-kit/block-elements/overflow-menu-element](https://docs.slack.dev/reference/block-kit/block-elements/overflow-menu-element) ## Args **`action_id`** : `required` An identifier for the action triggered when a menu option is selected. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action\_ids in the containing block. Maximum length for this field is 255 characters. **`options`** : `required` An array of option objects to display in the menu. Maximum number of options is 5, minimum is 1. **`confirm`** A confirm object that defines an optional confirmation dialog that appears after a menu item is selected. ### Ancestors * [InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement") * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var options_max_length` The type of the None singleton. `var options_min_length` The type of the None singleton. `var type` The type of the None singleton. ### Inherited members * `**[InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement")**`: * `[action_id_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement.action_id_max_length "slack_sdk.models.blocks.block_elements.InteractiveElement.action_id_max_length")` * `[attributes](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement.attributes "slack_sdk.models.blocks.block_elements.InteractiveElement.attributes")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.InteractiveElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.InteractiveElement.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.InteractiveElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.InteractiveElement.validate_json")` `class PlainTextInputElement (*, action_id: str | None = None, placeholder: str | dict | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject") | None = None, initial_value: str | None = None, multiline: bool | None = None, min_length: int | None = None, max_length: int | None = None, dispatch_action_config: dict | [DispatchActionConfig](basic_components.html#slack_sdk.models.blocks.basic_components.DispatchActionConfig "slack_sdk.models.blocks.basic_components.DispatchActionConfig") | None = None, focus_on_load: bool | None = None, **others: dict)` Expand source code ``` class PlainTextInputElement(InputInteractiveElement): type = "plain_text_input" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union( { "initial_value", "multiline", "min_length", "max_length", "dispatch_action_config", } ) def __init__( self, *, action_id: Optional[str] = None, placeholder: Optional[Union[str, dict, TextObject]] = None, initial_value: Optional[str] = None, multiline: Optional[bool] = None, min_length: Optional[int] = None, max_length: Optional[int] = None, dispatch_action_config: Optional[Union[dict, DispatchActionConfig]] = None, focus_on_load: Optional[bool] = None, **others: dict, ): """ A plain-text input, similar to the HTML tag, creates a field where a user can enter freeform data. It can appear as a single-line field or a larger textarea using the multiline flag. Plain-text input elements can be used inside of SectionBlocks and ActionsBlocks. https://docs.slack.dev/reference/block-kit/block-elements/plain-text-input-element Args: action_id (required): An identifier for the input value when the parent modal is submitted. You can use this when you receive a view_submission payload to identify the value of the input element. Should be unique among all other action_ids in the containing block. Maximum length for this field is 255 characters. placeholder: A plain_text only text object that defines the placeholder text shown in the plain-text input. Maximum length for the text in this field is 150 characters. initial_value: The initial value in the plain-text input when it is loaded. multiline: Indicates whether the input will be a single line (false) or a larger textarea (true). Defaults to false. min_length: The minimum length of input that the user must provide. If the user provides less, they will receive an error. Maximum value is 3000. max_length: The maximum length of input that the user can provide. If the user provides more, they will receive an error. dispatch_action_config: A dispatch configuration object that determines when during text input the element returns a block_actions payload. focus_on_load: Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. """ super().__init__( type=self.type, action_id=action_id, placeholder=TextObject.parse(placeholder, PlainTextObject.type), # type: ignore[arg-type] focus_on_load=focus_on_load, ) show_unknown_key_warning(self, others) self.initial_value = initial_value self.multiline = multiline self.min_length = min_length self.max_length = max_length self.dispatch_action_config = dispatch_action_config ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) A plain-text input, similar to the HTML tag, creates a field where a user can enter freeform data. It can appear as a single-line field or a larger textarea using the multiline flag. Plain-text input elements can be used inside of SectionBlocks and ActionsBlocks. [https://docs.slack.dev/reference/block-kit/block-elements/plain-text-input-element](https://docs.slack.dev/reference/block-kit/block-elements/plain-text-input-element) ## Args **`action_id`** : `required` An identifier for the input value when the parent modal is submitted. You can use this when you receive a view\_submission payload to identify the value of the input element. Should be unique among all other action\_ids in the containing block. Maximum length for this field is 255 characters. **`placeholder`** A plain\_text only text object that defines the placeholder text shown in the plain-text input. Maximum length for the text in this field is 150 characters. **`initial_value`** The initial value in the plain-text input when it is loaded. **`multiline`** Indicates whether the input will be a single line (false) or a larger textarea (true). Defaults to false. **`min_length`** The minimum length of input that the user must provide. If the user provides less, they will receive an error. Maximum value is 3000. **`max_length`** The maximum length of input that the user can provide. If the user provides more, they will receive an error. **`dispatch_action_config`** A dispatch configuration object that determines when during text input the element returns a block\_actions payload. **`focus_on_load`** Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. ### Ancestors * [InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement") * [InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement") * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union( { "initial_value", "multiline", "min_length", "max_length", "dispatch_action_config", } ) ``` Build an unordered collection of unique elements. ### Inherited members * `**[InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement")**`: * `[action_id_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement.action_id_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.action_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.InputInteractiveElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.InputInteractiveElement.logger")` * `[placeholder_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.InputInteractiveElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.InputInteractiveElement.validate_json")` `class PlainTextObject (*, text: str, emoji: bool | None = None)` Expand source code ``` class PlainTextObject(TextObject): """plain_text typed text object""" type = "plain_text" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"emoji"}) def __init__(self, *, text: str, emoji: Optional[bool] = None): """A plain text object, meaning markdown characters will not be parsed as formatting information. https://docs.slack.dev/reference/block-kit/composition-objects/text-object Args: text (required): The text for the block. This field accepts any of the standard text formatting markup when type is mrkdwn. emoji: Indicates whether emojis in a text field should be escaped into the colon emoji format. This field is only usable when type is plain_text. """ super().__init__(text=text, type=self.type) self.emoji = emoji @staticmethod def from_str(text: str) -> "PlainTextObject": return PlainTextObject(text=text, emoji=True) @staticmethod def direct_from_string(text: str) -> Dict[str, Any]: """Transforms a string into the required object shape to act as a PlainTextObject""" return PlainTextObject.from_str(text).to_dict() ``` plain\_text typed text object A plain text object, meaning markdown characters will not be parsed as formatting information. [https://docs.slack.dev/reference/block-kit/composition-objects/text-object](https://docs.slack.dev/reference/block-kit/composition-objects/text-object) ## Args **`text`** : `required` The text for the block. This field accepts any of the standard text formatting markup when type is mrkdwn. **`emoji`** Indicates whether emojis in a text field should be escaped into the colon emoji format. This field is only usable when type is plain\_text. ### Ancestors * [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Static methods `def direct_from_string(text: str) ‑> Dict[str, Any]` Expand source code ``` @staticmethod def direct_from_string(text: str) -> Dict[str, Any]: """Transforms a string into the required object shape to act as a PlainTextObject""" return PlainTextObject.from_str(text).to_dict() ``` Transforms a string into the required object shape to act as a PlainTextObject `def from_str(text: str) ‑> [PlainTextObject](basic_components.html#slack_sdk.models.blocks.basic_components.PlainTextObject "slack_sdk.models.blocks.basic_components.PlainTextObject")` Expand source code ``` @staticmethod def from_str(text: str) -> "PlainTextObject": return PlainTextObject(text=text, emoji=True) ``` ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"emoji"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.basic_components.TextObject.get_non_null_attributes")` * `[logger](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject.logger "slack_sdk.models.blocks.basic_components.TextObject.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.basic_components.TextObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.basic_components.TextObject.validate_json")` `class PlanBlock (*, title: str, tasks: Sequence[Dict | [TaskCardBlock](blocks.html#slack_sdk.models.blocks.blocks.TaskCardBlock "slack_sdk.models.blocks.blocks.TaskCardBlock")] | None = None, block_id: str | None = None, **others: dict)` Expand source code ``` class PlanBlock(Block): type = "plan" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union( { "title", "tasks", } ) def __init__( self, *, title: str, tasks: Optional[Sequence[Union[Dict, TaskCardBlock]]] = None, block_id: Optional[str] = None, **others: dict, ): """Displays a collection of related tasks. https://docs.slack.dev/reference/block-kit/blocks/plan-block/ Args: block_id: A string acting as a unique identifier for a block. If not specified, one will be generated. Maximum length for this field is 255 characters. block_id should be unique for each message and each iteration of a message. If a message is updated, use a new block_id. title (required): Title of the plan in plain text tasks: A sequence of task card blocks. Each task represents a single action within the plan. """ super().__init__(type=self.type, block_id=block_id) show_unknown_key_warning(self, others) self.title = title self.tasks = tasks ``` Blocks are a series of components that can be combined to create visually rich and compellingly interactive messages. [https://docs.slack.dev/reference/block-kit/blocks](https://docs.slack.dev/reference/block-kit/blocks) Displays a collection of related tasks. [https://docs.slack.dev/reference/block-kit/blocks/plan-block/](https://docs.slack.dev/reference/block-kit/blocks/plan-block/) ## Args **`block_id`** A string acting as a unique identifier for a block. If not specified, one will be generated. Maximum length for this field is 255 characters. block\_id should be unique for each message and each iteration of a message. If a message is updated, use a new block\_id. **`title`** : `required` Title of the plan in plain text **`tasks`** A sequence of task card blocks. Each task represents a single action within the plan. ### Ancestors * [Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union( { "title", "tasks", } ) ``` Build an unordered collection of unique elements. ### Inherited members * `**[Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")**`: * `[block_id_max_length](blocks.html#slack_sdk.models.blocks.blocks.Block.block_id_max_length "slack_sdk.models.blocks.blocks.Block.block_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.blocks.Block.get_non_null_attributes")` * `[logger](blocks.html#slack_sdk.models.blocks.blocks.Block.logger "slack_sdk.models.blocks.blocks.Block.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.blocks.Block.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.blocks.Block.validate_json")` `class RadioButtonsElement (*, action_id: str | None = None, options: Sequence[dict | [Option](basic_components.html#slack_sdk.models.blocks.basic_components.Option "slack_sdk.models.blocks.basic_components.Option")] | None = None, initial_option: dict | [Option](basic_components.html#slack_sdk.models.blocks.basic_components.Option "slack_sdk.models.blocks.basic_components.Option") | None = None, confirm: dict | [ConfirmObject](basic_components.html#slack_sdk.models.blocks.basic_components.ConfirmObject "slack_sdk.models.blocks.basic_components.ConfirmObject") | None = None, focus_on_load: bool | None = None, **others: dict)` Expand source code ``` class RadioButtonsElement(InputInteractiveElement): type = "radio_buttons" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"options", "initial_option"}) def __init__( self, *, action_id: Optional[str] = None, options: Optional[Sequence[Union[dict, Option]]] = None, initial_option: Optional[Union[dict, Option]] = None, confirm: Optional[Union[dict, ConfirmObject]] = None, focus_on_load: Optional[bool] = None, **others: dict, ): """A radio button group that allows a user to choose one item from a list of possible options. https://docs.slack.dev/reference/block-kit/block-elements/radio-button-group-element Args: action_id (required): An identifier for the action triggered when the radio button group is changed. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action_ids in the containing block. Maximum length for this field is 255 characters. options (required): An array of option objects. A maximum of 10 options are allowed. initial_option: An option object that exactly matches one of the options. This option will be selected when the radio button group initially loads. confirm: A confirm object that defines an optional confirmation dialog that appears after clicking one of the radio buttons in this element. focus_on_load: Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. """ super().__init__( type=self.type, action_id=action_id, confirm=ConfirmObject.parse(confirm), # type: ignore[arg-type] focus_on_load=focus_on_load, ) show_unknown_key_warning(self, others) self.options = options self.initial_option = initial_option ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) A radio button group that allows a user to choose one item from a list of possible options. [https://docs.slack.dev/reference/block-kit/block-elements/radio-button-group-element](https://docs.slack.dev/reference/block-kit/block-elements/radio-button-group-element) ## Args **`action_id`** : `required` An identifier for the action triggered when the radio button group is changed. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action\_ids in the containing block. Maximum length for this field is 255 characters. **`options`** : `required` An array of option objects. A maximum of 10 options are allowed. **`initial_option`** An option object that exactly matches one of the options. This option will be selected when the radio button group initially loads. **`confirm`** A confirm object that defines an optional confirmation dialog that appears after clicking one of the radio buttons in this element. **`focus_on_load`** Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. ### Ancestors * [InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement") * [InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement") * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"options", "initial_option"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement")**`: * `[action_id_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement.action_id_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.action_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.InputInteractiveElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.InputInteractiveElement.logger")` * `[placeholder_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.InputInteractiveElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.InputInteractiveElement.validate_json")` `class RawTextObject (*, text: str)` Expand source code ``` class RawTextObject(TextObject): """raw_text typed text object""" type = "raw_text" @property def attributes(self) -> Set[str]: # type: ignore[override] return {"text", "type"} def __init__(self, *, text: str): """A raw text object used in table block cells. https://docs.slack.dev/reference/block-kit/composition-objects/text-object/ https://docs.slack.dev/reference/block-kit/blocks/table-block Args: text (required): The text content for the table block cell. """ super().__init__(text=text, type=self.type) @staticmethod def from_str(text: str) -> "RawTextObject": """Transforms a string into a RawTextObject""" return RawTextObject(text=text) @staticmethod def direct_from_string(text: str) -> Dict[str, Any]: """Transforms a string into the required object shape to act as a RawTextObject""" return RawTextObject.from_str(text).to_dict() @JsonValidator("text attribute must have at least 1 character") def _validate_text_min_length(self): return len(self.text) >= 1 ``` raw\_text typed text object A raw text object used in table block cells. [https://docs.slack.dev/reference/block-kit/composition-objects/text-object/](https://docs.slack.dev/reference/block-kit/composition-objects/text-object/) [https://docs.slack.dev/reference/block-kit/blocks/table-block](https://docs.slack.dev/reference/block-kit/blocks/table-block) ## Args **`text`** : `required` The text content for the table block cell. ### Ancestors * [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Static methods `def direct_from_string(text: str) ‑> Dict[str, Any]` Expand source code ``` @staticmethod def direct_from_string(text: str) -> Dict[str, Any]: """Transforms a string into the required object shape to act as a RawTextObject""" return RawTextObject.from_str(text).to_dict() ``` Transforms a string into the required object shape to act as a RawTextObject `def from_str(text: str) ‑> [RawTextObject](basic_components.html#slack_sdk.models.blocks.basic_components.RawTextObject "slack_sdk.models.blocks.basic_components.RawTextObject")` Expand source code ``` @staticmethod def from_str(text: str) -> "RawTextObject": """Transforms a string into a RawTextObject""" return RawTextObject(text=text) ``` Transforms a string into a RawTextObject ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return {"text", "type"} ``` Build an unordered collection of unique elements. ### Inherited members * `**[TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.basic_components.TextObject.get_non_null_attributes")` * `[logger](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject.logger "slack_sdk.models.blocks.basic_components.TextObject.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.basic_components.TextObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.basic_components.TextObject.validate_json")` `class RichTextBlock (*, elements: Sequence[dict | [RichTextElement](block_elements.html#slack_sdk.models.blocks.block_elements.RichTextElement "slack_sdk.models.blocks.block_elements.RichTextElement")], block_id: str | None = None, **others: dict)` Expand source code ``` class RichTextBlock(Block): type = "rich_text" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"elements"}) def __init__( self, *, elements: Sequence[Union[dict, RichTextElement]], block_id: Optional[str] = None, **others: dict, ): """A block that is used to hold interactive elements. https://docs.slack.dev/reference/block-kit/blocks/rich-text-block Args: elements (required): An array of rich text objects - rich_text_section, rich_text_list, rich_text_quote, rich_text_preformatted block_id: A unique identifier for a block. If not specified, one will be generated. Maximum length for this field is 255 characters. block_id should be unique for each message or view and each iteration of a message or view. If a message or view is updated, use a new block_id. """ super().__init__(type=self.type, block_id=block_id) show_unknown_key_warning(self, others) self.elements = BlockElement.parse_all(elements) ``` Blocks are a series of components that can be combined to create visually rich and compellingly interactive messages. [https://docs.slack.dev/reference/block-kit/blocks](https://docs.slack.dev/reference/block-kit/blocks) A block that is used to hold interactive elements. [https://docs.slack.dev/reference/block-kit/blocks/rich-text-block](https://docs.slack.dev/reference/block-kit/blocks/rich-text-block) ## Args **`elements`** : `required` An array of rich text objects - rich\_text\_section, rich\_text\_list, rich\_text\_quote, rich\_text\_preformatted **`block_id`** A unique identifier for a block. If not specified, one will be generated. Maximum length for this field is 255 characters. block\_id should be unique for each message or view and each iteration of a message or view. If a message or view is updated, use a new block\_id. ### Ancestors * [Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"elements"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")**`: * `[block_id_max_length](blocks.html#slack_sdk.models.blocks.blocks.Block.block_id_max_length "slack_sdk.models.blocks.blocks.Block.block_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.blocks.Block.get_non_null_attributes")` * `[logger](blocks.html#slack_sdk.models.blocks.blocks.Block.logger "slack_sdk.models.blocks.blocks.Block.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.blocks.Block.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.blocks.Block.validate_json")` `class RichTextElement (*, type: str | None = None, subtype: str | None = None, **others: dict)` Expand source code ``` class RichTextElement(BlockElement): pass ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) ### Ancestors * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Subclasses * [RichTextElementParts.Broadcast](block_elements.html#slack_sdk.models.blocks.block_elements.RichTextElementParts.Broadcast "slack_sdk.models.blocks.block_elements.RichTextElementParts.Broadcast") * [RichTextElementParts.Channel](block_elements.html#slack_sdk.models.blocks.block_elements.RichTextElementParts.Channel "slack_sdk.models.blocks.block_elements.RichTextElementParts.Channel") * [RichTextElementParts.Color](block_elements.html#slack_sdk.models.blocks.block_elements.RichTextElementParts.Color "slack_sdk.models.blocks.block_elements.RichTextElementParts.Color") * [RichTextElementParts.Date](block_elements.html#slack_sdk.models.blocks.block_elements.RichTextElementParts.Date "slack_sdk.models.blocks.block_elements.RichTextElementParts.Date") * [RichTextElementParts.Emoji](block_elements.html#slack_sdk.models.blocks.block_elements.RichTextElementParts.Emoji "slack_sdk.models.blocks.block_elements.RichTextElementParts.Emoji") * [RichTextElementParts.Link](block_elements.html#slack_sdk.models.blocks.block_elements.RichTextElementParts.Link "slack_sdk.models.blocks.block_elements.RichTextElementParts.Link") * [RichTextElementParts.Team](block_elements.html#slack_sdk.models.blocks.block_elements.RichTextElementParts.Team "slack_sdk.models.blocks.block_elements.RichTextElementParts.Team") * [RichTextElementParts.Text](block_elements.html#slack_sdk.models.blocks.block_elements.RichTextElementParts.Text "slack_sdk.models.blocks.block_elements.RichTextElementParts.Text") * [RichTextElementParts.User](block_elements.html#slack_sdk.models.blocks.block_elements.RichTextElementParts.User "slack_sdk.models.blocks.block_elements.RichTextElementParts.User") * [RichTextElementParts.UserGroup](block_elements.html#slack_sdk.models.blocks.block_elements.RichTextElementParts.UserGroup "slack_sdk.models.blocks.block_elements.RichTextElementParts.UserGroup") * [RichTextListElement](block_elements.html#slack_sdk.models.blocks.block_elements.RichTextListElement "slack_sdk.models.blocks.block_elements.RichTextListElement") * [RichTextPreformattedElement](block_elements.html#slack_sdk.models.blocks.block_elements.RichTextPreformattedElement "slack_sdk.models.blocks.block_elements.RichTextPreformattedElement") * [RichTextQuoteElement](block_elements.html#slack_sdk.models.blocks.block_elements.RichTextQuoteElement "slack_sdk.models.blocks.block_elements.RichTextQuoteElement") * [RichTextSectionElement](block_elements.html#slack_sdk.models.blocks.block_elements.RichTextSectionElement "slack_sdk.models.blocks.block_elements.RichTextSectionElement") ### Inherited members * `**[BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement")**`: * `[attributes](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.attributes "slack_sdk.models.blocks.block_elements.BlockElement.attributes")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.BlockElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.BlockElement.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.BlockElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.BlockElement.validate_json")` `class RichTextElementParts` Expand source code ``` class RichTextElementParts: class TextStyle: def __init__( self, *, bold: Optional[bool] = None, italic: Optional[bool] = None, strike: Optional[bool] = None, code: Optional[bool] = None, underline: Optional[bool] = None, ): self.bold = bold self.italic = italic self.strike = strike self.code = code self.underline = underline def to_dict(self, *args) -> dict: result = { "bold": self.bold, "italic": self.italic, "strike": self.strike, "code": self.code, "underline": self.underline, } return {k: v for k, v in result.items() if v is not None} class Text(RichTextElement): type = "text" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"text", "style"}) def __init__( self, *, text: str, style: Optional[Union[dict, "RichTextElementParts.TextStyle"]] = None, **others: dict, ): super().__init__(type=self.type) show_unknown_key_warning(self, others) self.text = text self.style = style class Channel(RichTextElement): type = "channel" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"channel_id", "style"}) def __init__( self, *, channel_id: str, style: Optional[Union[dict, "RichTextElementParts.TextStyle"]] = None, **others: dict, ): super().__init__(type=self.type) show_unknown_key_warning(self, others) self.channel_id = channel_id self.style = style class User(RichTextElement): type = "user" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"user_id", "style"}) def __init__( self, *, user_id: str, style: Optional[Union[dict, "RichTextElementParts.TextStyle"]] = None, **others: dict, ): super().__init__(type=self.type) show_unknown_key_warning(self, others) self.user_id = user_id self.style = style class Emoji(RichTextElement): type = "emoji" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"name", "skin_tone", "unicode", "style"}) def __init__( self, *, name: str, skin_tone: Optional[int] = None, unicode: Optional[str] = None, style: Optional[Union[dict, "RichTextElementParts.TextStyle"]] = None, **others: dict, ): super().__init__(type=self.type) show_unknown_key_warning(self, others) self.name = name self.skin_tone = skin_tone self.unicode = unicode self.style = style class Link(RichTextElement): type = "link" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"url", "text", "style"}) def __init__( self, *, url: str, text: Optional[str] = None, style: Optional[Union[dict, "RichTextElementParts.TextStyle"]] = None, **others: dict, ): super().__init__(type=self.type) show_unknown_key_warning(self, others) self.url = url self.text = text self.style = style class Team(RichTextElement): type = "team" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"team_id", "style"}) def __init__( self, *, team_id: str, style: Optional[Union[dict, "RichTextElementParts.TextStyle"]] = None, **others: dict, ): super().__init__(type=self.type) show_unknown_key_warning(self, others) self.team_id = team_id self.style = style class UserGroup(RichTextElement): type = "usergroup" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"usergroup_id", "style"}) def __init__( self, *, usergroup_id: str, style: Optional[Union[dict, "RichTextElementParts.TextStyle"]] = None, **others: dict, ): super().__init__(type=self.type) show_unknown_key_warning(self, others) self.usergroup_id = usergroup_id self.style = style class Date(RichTextElement): type = "date" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"timestamp", "format", "url", "fallback", "style"}) def __init__( self, *, timestamp: int, format: str, url: Optional[str] = None, fallback: Optional[str] = None, style: Optional[Union[dict, "RichTextElementParts.TextStyle"]] = None, **others: dict, ): super().__init__(type=self.type) show_unknown_key_warning(self, others) self.timestamp = timestamp self.format = format self.url = url self.fallback = fallback self.style = style class Broadcast(RichTextElement): type = "broadcast" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"range", "style"}) def __init__( self, *, range: str, # channel, here, .. style: Optional[Union[dict, "RichTextElementParts.TextStyle"]] = None, **others: dict, ): super().__init__(type=self.type) show_unknown_key_warning(self, others) self.range = range self.style = style class Color(RichTextElement): type = "color" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"value", "style"}) def __init__( self, *, value: str, style: Optional[Union[dict, "RichTextElementParts.TextStyle"]] = None, **others: dict, ): super().__init__(type=self.type) show_unknown_key_warning(self, others) self.value = value self.style = style ``` ### Class variables `var Broadcast` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) `var Channel` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) `var Color` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) `var Date` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) `var Emoji` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) `var Link` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) `var Team` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) `var Text` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) `var TextStyle` The type of the None singleton. `var User` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) `var UserGroup` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) `class RichTextInputElement (*, action_id: str | None = None, placeholder: str | dict | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject") | None = None, initial_value: Dict[str, Any] | ForwardRef('[RichTextBlock](#slack_sdk.models.blocks.RichTextBlock "slack_sdk.models.blocks.RichTextBlock")') | None = None, dispatch_action_config: dict | [DispatchActionConfig](basic_components.html#slack_sdk.models.blocks.basic_components.DispatchActionConfig "slack_sdk.models.blocks.basic_components.DispatchActionConfig") | None = None, focus_on_load: bool | None = None, **others: dict)` Expand source code ``` class RichTextInputElement(InputInteractiveElement): type = "rich_text_input" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union( { "initial_value", "dispatch_action_config", } ) def __init__( self, *, action_id: Optional[str] = None, placeholder: Optional[Union[str, dict, TextObject]] = None, # To avoid circular imports, the RichTextBlock type here is intentionally a string initial_value: Optional[Union[Dict[str, Any], "RichTextBlock"]] = None, # type: ignore[name-defined] # noqa: F821 dispatch_action_config: Optional[Union[dict, DispatchActionConfig]] = None, focus_on_load: Optional[bool] = None, **others: dict, ): super().__init__( type=self.type, action_id=action_id, placeholder=TextObject.parse(placeholder, PlainTextObject.type), # type: ignore[arg-type] focus_on_load=focus_on_load, ) show_unknown_key_warning(self, others) self.initial_value = initial_value self.dispatch_action_config = dispatch_action_config ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) InteractiveElement that is usable in input blocks We generally recommend using the concrete subclasses for better supports of available properties. ### Ancestors * [InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement") * [InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement") * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union( { "initial_value", "dispatch_action_config", } ) ``` Build an unordered collection of unique elements. ### Inherited members * `**[InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement")**`: * `[action_id_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement.action_id_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.action_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.InputInteractiveElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.InputInteractiveElement.logger")` * `[placeholder_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.InputInteractiveElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.InputInteractiveElement.validate_json")` `class RichTextListElement (*, elements: Sequence[dict | [RichTextElement](block_elements.html#slack_sdk.models.blocks.block_elements.RichTextElement "slack_sdk.models.blocks.block_elements.RichTextElement")], style: str | None = None, indent: int | None = None, offset: int | None = None, border: int | None = None, **others: dict)` Expand source code ``` class RichTextListElement(RichTextElement): type = "rich_text_list" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"elements", "style", "indent", "offset", "border"}) def __init__( self, *, elements: Sequence[Union[dict, RichTextElement]], style: Optional[str] = None, # bullet, ordered indent: Optional[int] = None, offset: Optional[int] = None, border: Optional[int] = None, **others: dict, ): super().__init__(type=self.type) show_unknown_key_warning(self, others) self.elements = BlockElement.parse_all(elements) self.style = style self.indent = indent self.offset = offset self.border = border ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) ### Ancestors * [RichTextElement](block_elements.html#slack_sdk.models.blocks.block_elements.RichTextElement "slack_sdk.models.blocks.block_elements.RichTextElement") * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"elements", "style", "indent", "offset", "border"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[RichTextElement](block_elements.html#slack_sdk.models.blocks.block_elements.RichTextElement "slack_sdk.models.blocks.block_elements.RichTextElement")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.RichTextElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.RichTextElement.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.RichTextElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.RichTextElement.validate_json")` `class RichTextPreformattedElement (*, elements: Sequence[dict | [RichTextElement](block_elements.html#slack_sdk.models.blocks.block_elements.RichTextElement "slack_sdk.models.blocks.block_elements.RichTextElement")], border: int | None = None, **others: dict)` Expand source code ``` class RichTextPreformattedElement(RichTextElement): type = "rich_text_preformatted" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"elements", "border"}) def __init__( self, *, elements: Sequence[Union[dict, RichTextElement]], border: Optional[int] = None, **others: dict, ): super().__init__(type=self.type) show_unknown_key_warning(self, others) self.elements = BlockElement.parse_all(elements) self.border = border ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) ### Ancestors * [RichTextElement](block_elements.html#slack_sdk.models.blocks.block_elements.RichTextElement "slack_sdk.models.blocks.block_elements.RichTextElement") * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"elements", "border"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[RichTextElement](block_elements.html#slack_sdk.models.blocks.block_elements.RichTextElement "slack_sdk.models.blocks.block_elements.RichTextElement")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.RichTextElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.RichTextElement.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.RichTextElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.RichTextElement.validate_json")` `class RichTextQuoteElement (*, elements: Sequence[dict | [RichTextElement](block_elements.html#slack_sdk.models.blocks.block_elements.RichTextElement "slack_sdk.models.blocks.block_elements.RichTextElement")], **others: dict)` Expand source code ``` class RichTextQuoteElement(RichTextElement): type = "rich_text_quote" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"elements"}) def __init__( self, *, elements: Sequence[Union[dict, RichTextElement]], **others: dict, ): super().__init__(type=self.type) show_unknown_key_warning(self, others) self.elements = BlockElement.parse_all(elements) ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) ### Ancestors * [RichTextElement](block_elements.html#slack_sdk.models.blocks.block_elements.RichTextElement "slack_sdk.models.blocks.block_elements.RichTextElement") * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"elements"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[RichTextElement](block_elements.html#slack_sdk.models.blocks.block_elements.RichTextElement "slack_sdk.models.blocks.block_elements.RichTextElement")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.RichTextElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.RichTextElement.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.RichTextElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.RichTextElement.validate_json")` `class RichTextSectionElement (*, elements: Sequence[dict | [RichTextElement](block_elements.html#slack_sdk.models.blocks.block_elements.RichTextElement "slack_sdk.models.blocks.block_elements.RichTextElement")], **others: dict)` Expand source code ``` class RichTextSectionElement(RichTextElement): type = "rich_text_section" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"elements"}) def __init__( self, *, elements: Sequence[Union[dict, RichTextElement]], **others: dict, ): super().__init__(type=self.type) show_unknown_key_warning(self, others) self.elements = BlockElement.parse_all(elements) ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) ### Ancestors * [RichTextElement](block_elements.html#slack_sdk.models.blocks.block_elements.RichTextElement "slack_sdk.models.blocks.block_elements.RichTextElement") * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"elements"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[RichTextElement](block_elements.html#slack_sdk.models.blocks.block_elements.RichTextElement "slack_sdk.models.blocks.block_elements.RichTextElement")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.RichTextElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.RichTextElement.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.RichTextElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.RichTextElement.validate_json")` `class SectionBlock (*, block_id: str | None = None, text: str | dict | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject") | None = None, fields: Sequence[str | dict | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject")] | None = None, accessory: dict | [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") | None = None, expand: bool | None = None, **others: dict)` Expand source code ``` class SectionBlock(Block): type = "section" fields_max_length = 10 text_max_length = 3000 @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"text", "fields", "accessory", "expand"}) def __init__( self, *, block_id: Optional[str] = None, text: Optional[Union[str, dict, TextObject]] = None, fields: Optional[Sequence[Union[str, dict, TextObject]]] = None, accessory: Optional[Union[dict, BlockElement]] = None, expand: Optional[bool] = None, **others: dict, ): """A section is one of the most flexible blocks available. https://docs.slack.dev/reference/block-kit/blocks/section-block Args: block_id (required): A string acting as a unique identifier for a block. If not specified, one will be generated. You can use this block_id when you receive an interaction payload to identify the source of the action. Maximum length for this field is 255 characters. block_id should be unique for each message and each iteration of a message. If a message is updated, use a new block_id. text (preferred): The text for the block, in the form of a text object. Maximum length for the text in this field is 3000 characters. This field is not required if a valid array of fields objects is provided instead. fields (required if no text is provided): Required if no text is provided. An array of text objects. Any text objects included with fields will be rendered in a compact format that allows for 2 columns of side-by-side text. Maximum number of items is 10. Maximum length for the text in each item is 2000 characters. accessory: One of the available element objects. expand: Whether or not this section block's text should always expand when rendered. If false or not provided, it may be rendered with a 'see more' option to expand and show the full text. For AI Assistant apps, this allows the app to post long messages without users needing to click 'see more' to expand the message. """ super().__init__(type=self.type, block_id=block_id) show_unknown_key_warning(self, others) self.text = TextObject.parse(text) # type: ignore[arg-type] field_objects = [] for f in fields or []: if isinstance(f, str): field_objects.append(MarkdownTextObject.from_str(f)) elif isinstance(f, TextObject): field_objects.append(f) # type: ignore[arg-type] elif isinstance(f, dict) and "type" in f: d = copy.copy(f) t = d.pop("type") if t == MarkdownTextObject.type: field_objects.append(MarkdownTextObject(**d)) else: field_objects.append(PlainTextObject(**d)) # type: ignore[arg-type] else: self.logger.warning(f"Unsupported field detected and skipped: {f}") self.fields = field_objects self.accessory = BlockElement.parse(accessory) # type: ignore[arg-type] self.expand = expand @JsonValidator("text or fields attribute must be specified") def _validate_text_or_fields_populated(self): return self.text is not None or self.fields @JsonValidator(f"fields attribute cannot exceed {fields_max_length} items") def _validate_fields_length(self): return self.fields is None or len(self.fields) <= self.fields_max_length @JsonValidator(f"text attribute cannot exceed {text_max_length} characters") def _validate_alt_text_length(self): return self.text is None or len(self.text.text) <= self.text_max_length ``` Blocks are a series of components that can be combined to create visually rich and compellingly interactive messages. [https://docs.slack.dev/reference/block-kit/blocks](https://docs.slack.dev/reference/block-kit/blocks) A section is one of the most flexible blocks available. [https://docs.slack.dev/reference/block-kit/blocks/section-block](https://docs.slack.dev/reference/block-kit/blocks/section-block) ## Args **`block_id`** : `required` A string acting as a unique identifier for a block. If not specified, one will be generated. You can use this block\_id when you receive an interaction payload to identify the source of the action. Maximum length for this field is 255 characters. block\_id should be unique for each message and each iteration of a message. If a message is updated, use a new block\_id. **`text`** : `preferred` The text for the block, in the form of a text object. Maximum length for the text in this field is 3000 characters. This field is not required if a valid array of fields objects is provided instead. **`fields`** : `required if no text is provided` Required if no text is provided. An array of text objects. Any text objects included with fields will be rendered in a compact format that allows for 2 columns of side-by-side text. Maximum number of items is 10. Maximum length for the text in each item is 2000 characters. **`accessory`** One of the available element objects. **`expand`** Whether or not this section block's text should always expand when rendered. If false or not provided, it may be rendered with a 'see more' option to expand and show the full text. For AI Assistant apps, this allows the app to post long messages without users needing to click 'see more' to expand the message. ### Ancestors * [Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var fields_max_length` The type of the None singleton. `var text_max_length` The type of the None singleton. `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"text", "fields", "accessory", "expand"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")**`: * `[block_id_max_length](blocks.html#slack_sdk.models.blocks.blocks.Block.block_id_max_length "slack_sdk.models.blocks.blocks.Block.block_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.blocks.Block.get_non_null_attributes")` * `[logger](blocks.html#slack_sdk.models.blocks.blocks.Block.logger "slack_sdk.models.blocks.blocks.Block.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.blocks.Block.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.blocks.Block.validate_json")` `class SelectElement (*, action_id: str | None = None, placeholder: str | None = None, options: Sequence[[Option](basic_components.html#slack_sdk.models.blocks.basic_components.Option "slack_sdk.models.blocks.basic_components.Option")] | None = None, option_groups: Sequence[[OptionGroup](basic_components.html#slack_sdk.models.blocks.basic_components.OptionGroup "slack_sdk.models.blocks.basic_components.OptionGroup")] | None = None, initial_option: [Option](basic_components.html#slack_sdk.models.blocks.basic_components.Option "slack_sdk.models.blocks.basic_components.Option") | None = None, confirm: dict | [ConfirmObject](basic_components.html#slack_sdk.models.blocks.basic_components.ConfirmObject "slack_sdk.models.blocks.basic_components.ConfirmObject") | None = None, focus_on_load: bool | None = None, **others: dict)` Expand source code ``` class SelectElement(InputInteractiveElement): type = "static_select" options_max_length = 100 option_groups_max_length = 100 @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"options", "option_groups", "initial_option"}) def __init__( self, *, action_id: Optional[str] = None, placeholder: Optional[str] = None, options: Optional[Sequence[Option]] = None, option_groups: Optional[Sequence[OptionGroup]] = None, initial_option: Optional[Option] = None, confirm: Optional[Union[dict, ConfirmObject]] = None, focus_on_load: Optional[bool] = None, **others: dict, ): """This is the simplest form of select menu, with a static list of options passed in when defining the element. https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element#static_select Args: action_id (required): An identifier for the action triggered when a menu option is selected. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action_ids in the containing block. Maximum length for this field is 255 characters. placeholder (required): A plain_text only text object that defines the placeholder text shown on the menu. Maximum length for the text in this field is 150 characters. options (either options or option_groups is required): An array of option objects. Maximum number of options is 100. If option_groups is specified, this field should not be. option_groups (either options or option_groups is required): An array of option group objects. Maximum number of option groups is 100. If options is specified, this field should not be. initial_option: A single option that exactly matches one of the options or option_groups. This option will be selected when the menu initially loads. confirm: A confirm object that defines an optional confirmation dialog that appears after a menu item is selected. focus_on_load: Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. """ super().__init__( type=self.type, action_id=action_id, placeholder=TextObject.parse(placeholder, PlainTextObject.type), # type: ignore[arg-type] confirm=ConfirmObject.parse(confirm), # type: ignore[arg-type] focus_on_load=focus_on_load, ) show_unknown_key_warning(self, others) self.options = options self.option_groups = option_groups self.initial_option = initial_option @JsonValidator(f"options attribute cannot exceed {options_max_length} elements") def _validate_options_length(self) -> bool: return self.options is None or len(self.options) <= self.options_max_length @JsonValidator(f"option_groups attribute cannot exceed {option_groups_max_length} elements") def _validate_option_groups_length(self) -> bool: return self.option_groups is None or len(self.option_groups) <= self.option_groups_max_length @JsonValidator("options and option_groups cannot both be specified") def _validate_options_and_option_groups_both_specified(self) -> bool: return not (self.options is not None and self.option_groups is not None) @JsonValidator("options or option_groups must be specified") def _validate_neither_options_or_option_groups_is_specified(self) -> bool: return self.options is not None or self.option_groups is not None ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) This is the simplest form of select menu, with a static list of options passed in when defining the element. [https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element#static\_select](https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element#static_select) ## Args **`action_id`** : `required` An identifier for the action triggered when a menu option is selected. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action\_ids in the containing block. Maximum length for this field is 255 characters. **`placeholder`** : `required` A plain\_text only text object that defines the placeholder text shown on the menu. Maximum length for the text in this field is 150 characters. **`options`** : `either options` or `option_groups is required` An array of option objects. Maximum number of options is 100. If option\_groups is specified, this field should not be. **`option_groups`** : `either options` or `option_groups is required` An array of option group objects. Maximum number of option groups is 100. If options is specified, this field should not be. **`initial_option`** A single option that exactly matches one of the options or option\_groups. This option will be selected when the menu initially loads. **`confirm`** A confirm object that defines an optional confirmation dialog that appears after a menu item is selected. **`focus_on_load`** Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. ### Ancestors * [InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement") * [InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement") * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var option_groups_max_length` The type of the None singleton. `var options_max_length` The type of the None singleton. `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"options", "option_groups", "initial_option"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement")**`: * `[action_id_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement.action_id_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.action_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.InputInteractiveElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.InputInteractiveElement.logger")` * `[placeholder_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.InputInteractiveElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.InputInteractiveElement.validate_json")` `class StaticMultiSelectElement (*, placeholder: str | dict | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject") | None = None, action_id: str | None = None, options: Sequence[[Option](basic_components.html#slack_sdk.models.blocks.basic_components.Option "slack_sdk.models.blocks.basic_components.Option")] | None = None, option_groups: Sequence[[OptionGroup](basic_components.html#slack_sdk.models.blocks.basic_components.OptionGroup "slack_sdk.models.blocks.basic_components.OptionGroup")] | None = None, initial_options: Sequence[[Option](basic_components.html#slack_sdk.models.blocks.basic_components.Option "slack_sdk.models.blocks.basic_components.Option")] | None = None, confirm: dict | [ConfirmObject](basic_components.html#slack_sdk.models.blocks.basic_components.ConfirmObject "slack_sdk.models.blocks.basic_components.ConfirmObject") | None = None, max_selected_items: int | None = None, focus_on_load: bool | None = None, **others: dict)` Expand source code ``` class StaticMultiSelectElement(InputInteractiveElement): type = "multi_static_select" options_max_length = 100 option_groups_max_length = 100 @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"options", "option_groups", "initial_options", "max_selected_items"}) def __init__( self, *, placeholder: Optional[Union[str, dict, TextObject]] = None, action_id: Optional[str] = None, options: Optional[Sequence[Option]] = None, option_groups: Optional[Sequence[OptionGroup]] = None, initial_options: Optional[Sequence[Option]] = None, confirm: Optional[Union[dict, ConfirmObject]] = None, max_selected_items: Optional[int] = None, focus_on_load: Optional[bool] = None, **others: dict, ): """ This is the simplest form of select menu, with a static list of options passed in when defining the element. https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#static_multi_select Args: placeholder (required): A plain_text only text object that defines the placeholder text shown on the menu. Maximum length for the text in this field is 150 characters. action_id (required): An identifier for the action triggered when a menu option is selected. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action_ids in the containing block. Maximum length for this field is 255 characters. options (either options or option_groups is required): An array of option objects. Maximum number of options is 100. If option_groups is specified, this field should not be. option_groups (either options or option_groups is required): An array of option group objects. Maximum number of option groups is 100. If options is specified, this field should not be. initial_options: An array of option objects that exactly match one or more of the options within options or option_groups. These options will be selected when the menu initially loads. confirm: A confirm object that defines an optional confirmation dialog that appears before the multi-select choices are submitted. max_selected_items: Specifies the maximum number of items that can be selected in the menu. Minimum number is 1. focus_on_load: Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. """ super().__init__( type=self.type, action_id=action_id, placeholder=TextObject.parse(placeholder, PlainTextObject.type), # type: ignore[arg-type] confirm=ConfirmObject.parse(confirm), # type: ignore[arg-type] focus_on_load=focus_on_load, ) show_unknown_key_warning(self, others) self.options = Option.parse_all(options) self.option_groups = OptionGroup.parse_all(option_groups) self.initial_options = Option.parse_all(initial_options) self.max_selected_items = max_selected_items @JsonValidator(f"options attribute cannot exceed {options_max_length} elements") def _validate_options_length(self) -> bool: return self.options is None or len(self.options) <= self.options_max_length @JsonValidator(f"option_groups attribute cannot exceed {option_groups_max_length} elements") def _validate_option_groups_length(self) -> bool: return self.option_groups is None or len(self.option_groups) <= self.option_groups_max_length @JsonValidator("options and option_groups cannot both be specified") def _validate_options_and_option_groups_both_specified(self) -> bool: return self.options is None or self.option_groups is None @JsonValidator("options or option_groups must be specified") def _validate_neither_options_or_option_groups_is_specified(self) -> bool: return self.options is not None or self.option_groups is not None ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) This is the simplest form of select menu, with a static list of options passed in when defining the element. [https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#static\_multi\_select](https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#static_multi_select) ## Args **`placeholder`** : `required` A plain\_text only text object that defines the placeholder text shown on the menu. Maximum length for the text in this field is 150 characters. **`action_id`** : `required` An identifier for the action triggered when a menu option is selected. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action\_ids in the containing block. Maximum length for this field is 255 characters. **`options`** : `either options` or `option_groups is required` An array of option objects. Maximum number of options is 100. If option\_groups is specified, this field should not be. **`option_groups`** : `either options` or `option_groups is required` An array of option group objects. Maximum number of option groups is 100. If options is specified, this field should not be. **`initial_options`** An array of option objects that exactly match one or more of the options within options or option\_groups. These options will be selected when the menu initially loads. **`confirm`** A confirm object that defines an optional confirmation dialog that appears before the multi-select choices are submitted. **`max_selected_items`** Specifies the maximum number of items that can be selected in the menu. Minimum number is 1. **`focus_on_load`** Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. ### Ancestors * [InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement") * [InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement") * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var option_groups_max_length` The type of the None singleton. `var options_max_length` The type of the None singleton. `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"options", "option_groups", "initial_options", "max_selected_items"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement")**`: * `[action_id_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement.action_id_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.action_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.InputInteractiveElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.InputInteractiveElement.logger")` * `[placeholder_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.InputInteractiveElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.InputInteractiveElement.validate_json")` `class StaticSelectElement (*, placeholder: str | dict | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject") | None = None, action_id: str | None = None, options: Sequence[dict | [Option](basic_components.html#slack_sdk.models.blocks.basic_components.Option "slack_sdk.models.blocks.basic_components.Option")] | None = None, option_groups: Sequence[dict | [OptionGroup](basic_components.html#slack_sdk.models.blocks.basic_components.OptionGroup "slack_sdk.models.blocks.basic_components.OptionGroup")] | None = None, initial_option: dict | [Option](basic_components.html#slack_sdk.models.blocks.basic_components.Option "slack_sdk.models.blocks.basic_components.Option") | None = None, confirm: dict | [ConfirmObject](basic_components.html#slack_sdk.models.blocks.basic_components.ConfirmObject "slack_sdk.models.blocks.basic_components.ConfirmObject") | None = None, focus_on_load: bool | None = None, **others: dict)` Expand source code ``` class StaticSelectElement(InputInteractiveElement): type = "static_select" options_max_length = 100 option_groups_max_length = 100 @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"options", "option_groups", "initial_option"}) def __init__( self, *, placeholder: Optional[Union[str, dict, TextObject]] = None, action_id: Optional[str] = None, options: Optional[Sequence[Union[dict, Option]]] = None, option_groups: Optional[Sequence[Union[dict, OptionGroup]]] = None, initial_option: Optional[Union[dict, Option]] = None, confirm: Optional[Union[dict, ConfirmObject]] = None, focus_on_load: Optional[bool] = None, **others: dict, ): """This is the simplest form of select menu, with a static list of options passed in when defining the element. https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element#static_select Args: placeholder (required): A plain_text only text object that defines the placeholder text shown on the menu. Maximum length for the text in this field is 150 characters. action_id (required): An identifier for the action triggered when a menu option is selected. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action_ids in the containing block. Maximum length for this field is 255 characters. options (either options or option_groups is required): An array of option objects. Maximum number of options is 100. If option_groups is specified, this field should not be. option_groups (either options or option_groups is required): An array of option group objects. Maximum number of option groups is 100. If options is specified, this field should not be. initial_option: A single option that exactly matches one of the options or option_groups. This option will be selected when the menu initially loads. confirm: A confirm object that defines an optional confirmation dialog that appears after a menu item is selected. focus_on_load: Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. """ super().__init__( type=self.type, action_id=action_id, placeholder=TextObject.parse(placeholder, PlainTextObject.type), # type: ignore[arg-type] confirm=ConfirmObject.parse(confirm), # type: ignore[arg-type] focus_on_load=focus_on_load, ) show_unknown_key_warning(self, others) self.options = options self.option_groups = option_groups self.initial_option = initial_option @JsonValidator(f"options attribute cannot exceed {options_max_length} elements") def _validate_options_length(self) -> bool: return self.options is None or len(self.options) <= self.options_max_length @JsonValidator(f"option_groups attribute cannot exceed {option_groups_max_length} elements") def _validate_option_groups_length(self) -> bool: return self.option_groups is None or len(self.option_groups) <= self.option_groups_max_length @JsonValidator("options and option_groups cannot both be specified") def _validate_options_and_option_groups_both_specified(self) -> bool: return not (self.options is not None and self.option_groups is not None) @JsonValidator("options or option_groups must be specified") def _validate_neither_options_or_option_groups_is_specified(self) -> bool: return self.options is not None or self.option_groups is not None ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) This is the simplest form of select menu, with a static list of options passed in when defining the element. [https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element#static\_select](https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element#static_select) ## Args **`placeholder`** : `required` A plain\_text only text object that defines the placeholder text shown on the menu. Maximum length for the text in this field is 150 characters. **`action_id`** : `required` An identifier for the action triggered when a menu option is selected. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action\_ids in the containing block. Maximum length for this field is 255 characters. **`options`** : `either options` or `option_groups is required` An array of option objects. Maximum number of options is 100. If option\_groups is specified, this field should not be. **`option_groups`** : `either options` or `option_groups is required` An array of option group objects. Maximum number of option groups is 100. If options is specified, this field should not be. **`initial_option`** A single option that exactly matches one of the options or option\_groups. This option will be selected when the menu initially loads. **`confirm`** A confirm object that defines an optional confirmation dialog that appears after a menu item is selected. **`focus_on_load`** Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. ### Ancestors * [InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement") * [InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement") * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var option_groups_max_length` The type of the None singleton. `var options_max_length` The type of the None singleton. `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"options", "option_groups", "initial_option"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement")**`: * `[action_id_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement.action_id_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.action_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.InputInteractiveElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.InputInteractiveElement.logger")` * `[placeholder_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.InputInteractiveElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.InputInteractiveElement.validate_json")` `class TableBlock (*, rows: Sequence[Sequence[Dict[str, Any]]], column_settings: Sequence[Dict[str, Any] | None] | None = None, block_id: str | None = None, **others: dict)` Expand source code ``` class TableBlock(Block): type = "table" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"rows", "column_settings"}) def __init__( self, *, rows: Sequence[Sequence[Dict[str, Any]]], column_settings: Optional[Sequence[Optional[Dict[str, Any]]]] = None, block_id: Optional[str] = None, **others: dict, ): """Displays structured information in a table. https://docs.slack.dev/reference/block-kit/blocks/table-block Args: rows (required): An array consisting of table rows. Maximum 100 rows. Each row object is an array with a max of 20 table cells. Table cells can have a type of raw_text or rich_text. column_settings: An array describing column behavior. If there are fewer items in the column_settings array than there are columns in the table, then the items in the the column_settings array will describe the same number of columns in the table as there are in the array itself. Any additional columns will have the default behavior. Maximum 20 items. See below for column settings schema. block_id: A unique identifier for a block. If not specified, a block_id will be generated. You can use this block_id when you receive an interaction payload to identify the source of the action. Maximum length for this field is 255 characters. block_id should be unique for each message and each iteration of a message. If a message is updated, use a new block_id. """ super().__init__(type=self.type, block_id=block_id) show_unknown_key_warning(self, others) self.rows = rows self.column_settings = column_settings @JsonValidator("rows attribute must be specified") def _validate_rows(self): return self.rows is not None and len(self.rows) > 0 ``` Blocks are a series of components that can be combined to create visually rich and compellingly interactive messages. [https://docs.slack.dev/reference/block-kit/blocks](https://docs.slack.dev/reference/block-kit/blocks) Displays structured information in a table. [https://docs.slack.dev/reference/block-kit/blocks/table-block](https://docs.slack.dev/reference/block-kit/blocks/table-block) ## Args **`rows`** : `required` An array consisting of table rows. Maximum 100 rows. Each row object is an array with a max of 20 table cells. Table cells can have a type of raw\_text or rich\_text. **`column_settings`** An array describing column behavior. If there are fewer items in the column\_settings array than there are columns in the table, then the items in the the column\_settings array will describe the same number of columns in the table as there are in the array itself. Any additional columns will have the default behavior. Maximum 20 items. See below for column settings schema. **`block_id`** A unique identifier for a block. If not specified, a block\_id will be generated. You can use this block\_id when you receive an interaction payload to identify the source of the action. Maximum length for this field is 255 characters. block\_id should be unique for each message and each iteration of a message. If a message is updated, use a new block\_id. ### Ancestors * [Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"rows", "column_settings"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")**`: * `[block_id_max_length](blocks.html#slack_sdk.models.blocks.blocks.Block.block_id_max_length "slack_sdk.models.blocks.blocks.Block.block_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.blocks.Block.get_non_null_attributes")` * `[logger](blocks.html#slack_sdk.models.blocks.blocks.Block.logger "slack_sdk.models.blocks.blocks.Block.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.blocks.Block.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.blocks.Block.validate_json")` `class TaskCardBlock (*, task_id: str, title: str, details: [RichTextBlock](blocks.html#slack_sdk.models.blocks.blocks.RichTextBlock "slack_sdk.models.blocks.blocks.RichTextBlock") | dict | None = None, output: [RichTextBlock](blocks.html#slack_sdk.models.blocks.blocks.RichTextBlock "slack_sdk.models.blocks.blocks.RichTextBlock") | dict | None = None, sources: Sequence[[UrlSourceElement](block_elements.html#slack_sdk.models.blocks.block_elements.UrlSourceElement "slack_sdk.models.blocks.block_elements.UrlSourceElement") | dict] | None = None, status: str, block_id: str | None = None, **others: dict)` Expand source code ``` class TaskCardBlock(Block): type = "task_card" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union( { "task_id", "title", "details", "output", "sources", "status", } ) def __init__( self, *, task_id: str, title: str, details: Optional[Union[RichTextBlock, dict]] = None, output: Optional[Union[RichTextBlock, dict]] = None, sources: Optional[Sequence[Union[UrlSourceElement, dict]]] = None, status: str, # pending, in_progress, complete, error block_id: Optional[str] = None, **others: dict, ): """Displays a single task, representing a single action. https://docs.slack.dev/reference/block-kit/blocks/task-card-block/ Args: block_id: A string acting as a unique identifier for a block. If not specified, one will be generated. Maximum length for this field is 255 characters. block_id should be unique for each message and each iteration of a message. If a message is updated, use a new block_id. task_id (required): ID for the task title (required): Title of the task in plain text details: Details of the task in the form of a single "rich_text" entity. output: Output of the task in the form of a single "rich_text" entity. sources: Array of URL source elements used to generate a response. status: The state of a task. Either "pending", "in_progress", "complete", or "error". """ super().__init__(type=self.type, block_id=block_id) show_unknown_key_warning(self, others) self.task_id = task_id self.title = title self.details = details self.output = output self.sources = sources self.status = status @JsonValidator("status must be an expected value (pending, in_progress, complete, or error)") def _validate_rows(self): return self.status in ["pending", "in_progress", "complete", "error"] ``` Blocks are a series of components that can be combined to create visually rich and compellingly interactive messages. [https://docs.slack.dev/reference/block-kit/blocks](https://docs.slack.dev/reference/block-kit/blocks) Displays a single task, representing a single action. [https://docs.slack.dev/reference/block-kit/blocks/task-card-block/](https://docs.slack.dev/reference/block-kit/blocks/task-card-block/) ## Args **`block_id`** A string acting as a unique identifier for a block. If not specified, one will be generated. Maximum length for this field is 255 characters. block\_id should be unique for each message and each iteration of a message. If a message is updated, use a new block\_id. **`task_id`** : `required` ID for the task **`title`** : `required` Title of the task in plain text **`details`** Details of the task in the form of a single "rich\_text" entity. **`output`** Output of the task in the form of a single "rich\_text" entity. **`sources`** Array of URL source elements used to generate a response. **`status`** The state of a task. Either "pending", "in\_progress", "complete", or "error". ### Ancestors * [Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union( { "task_id", "title", "details", "output", "sources", "status", } ) ``` Build an unordered collection of unique elements. ### Inherited members * `**[Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")**`: * `[block_id_max_length](blocks.html#slack_sdk.models.blocks.blocks.Block.block_id_max_length "slack_sdk.models.blocks.blocks.Block.block_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.blocks.Block.get_non_null_attributes")` * `[logger](blocks.html#slack_sdk.models.blocks.blocks.Block.logger "slack_sdk.models.blocks.blocks.Block.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.blocks.Block.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.blocks.Block.validate_json")` `class TextObject (text: str, type: str | None = None, subtype: str | None = None, emoji: bool | None = None, **kwargs)` Expand source code ``` class TextObject(JsonObject): """The interface for text objects (types: plain_text, mrkdwn)""" attributes = {"text", "type", "emoji"} logger = logging.getLogger(__name__) def _subtype_warning(self): warnings.warn( "subtype is deprecated since slackclient 2.6.0, use type instead", DeprecationWarning, ) @property def subtype(self) -> Optional[str]: return self.type @classmethod def parse( cls, text: Union[str, Dict[str, Any], "TextObject"], default_type: str = "mrkdwn", ) -> Optional["TextObject"]: if not text: return None elif isinstance(text, str): if default_type == PlainTextObject.type: return PlainTextObject.from_str(text) else: return MarkdownTextObject.from_str(text) elif isinstance(text, dict): d = copy.copy(text) t = d.pop("type") if t == PlainTextObject.type: return PlainTextObject(**d) else: return MarkdownTextObject(**d) elif isinstance(text, TextObject): return text else: cls.logger.warning(f"Unknown type ({type(text)}) detected when parsing a TextObject") return None def __init__( self, text: str, type: Optional[str] = None, subtype: Optional[str] = None, emoji: Optional[bool] = None, **kwargs, ): """Super class for new text "objects" used in Block kit""" if subtype: self._subtype_warning() self.text = text self.type = type if type else subtype self.emoji = emoji ``` The interface for text objects (types: plain\_text, mrkdwn) Super class for new text "objects" used in Block kit ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Subclasses * [MarkdownTextObject](basic_components.html#slack_sdk.models.blocks.basic_components.MarkdownTextObject "slack_sdk.models.blocks.basic_components.MarkdownTextObject") * [PlainTextObject](basic_components.html#slack_sdk.models.blocks.basic_components.PlainTextObject "slack_sdk.models.blocks.basic_components.PlainTextObject") * [RawTextObject](basic_components.html#slack_sdk.models.blocks.basic_components.RawTextObject "slack_sdk.models.blocks.basic_components.RawTextObject") ### Class variables `var attributes` The type of the None singleton. `var logger` The type of the None singleton. ### Static methods `def parse(text: str | Dict[str, Any] | ForwardRef('[TextObject](#slack_sdk.models.blocks.TextObject "slack_sdk.models.blocks.TextObject")'), default_type: str = 'mrkdwn') ‑> [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject") | None` ### Instance variables `prop subtype : str | None` Expand source code ``` @property def subtype(self) -> Optional[str]: return self.type ``` ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class TimePickerElement (*, action_id: str | None = None, placeholder: str | dict | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject") | None = None, initial_time: str | None = None, confirm: dict | [ConfirmObject](basic_components.html#slack_sdk.models.blocks.basic_components.ConfirmObject "slack_sdk.models.blocks.basic_components.ConfirmObject") | None = None, focus_on_load: bool | None = None, timezone: str | None = None, **others: dict)` Expand source code ``` class TimePickerElement(InputInteractiveElement): type = "timepicker" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"initial_time", "timezone"}) def __init__( self, *, action_id: Optional[str] = None, placeholder: Optional[Union[str, dict, TextObject]] = None, initial_time: Optional[str] = None, confirm: Optional[Union[dict, ConfirmObject]] = None, focus_on_load: Optional[bool] = None, timezone: Optional[str] = None, **others: dict, ): """ An element which allows selection of a time of day. On desktop clients, this time picker will take the form of a dropdown list with free-text entry for precise choices. On mobile clients, the time picker will use native time picker UIs. https://docs.slack.dev/reference/block-kit/block-elements/time-picker-element Args: action_id (required): An identifier for the action triggered when a time is selected. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action_ids in the containing block. Maximum length for this field is 255 characters. placeholder: A plain_text only text object that defines the placeholder text shown on the timepicker. Maximum length for the text in this field is 150 characters. initial_time: The initial time that is selected when the element is loaded. This should be in the format HH:mm, where HH is the 24-hour format of an hour (00 to 23) and mm is minutes with leading zeros (00 to 59), for example 22:25 for 10:25pm. confirm: A confirm object that defines an optional confirmation dialog that appears after a time is selected. focus_on_load: Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. timezone: The timezone to consider for this input value. """ super().__init__( type=self.type, action_id=action_id, placeholder=TextObject.parse(placeholder, PlainTextObject.type), # type: ignore[arg-type] confirm=ConfirmObject.parse(confirm), # type: ignore[arg-type] focus_on_load=focus_on_load, ) show_unknown_key_warning(self, others) self.initial_time = initial_time self.timezone = timezone @JsonValidator("initial_time attribute must be in format 'HH:mm'") def _validate_initial_time_valid(self) -> bool: return self.initial_time is None or re.match(r"([0-1][0-9]|2[0-3]):([0-5][0-9])", self.initial_time) is not None ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) An element which allows selection of a time of day. On desktop clients, this time picker will take the form of a dropdown list with free-text entry for precise choices. On mobile clients, the time picker will use native time picker UIs. [https://docs.slack.dev/reference/block-kit/block-elements/time-picker-element](https://docs.slack.dev/reference/block-kit/block-elements/time-picker-element) ## Args **`action_id`** : `required` An identifier for the action triggered when a time is selected. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action\_ids in the containing block. Maximum length for this field is 255 characters. **`placeholder`** A plain\_text only text object that defines the placeholder text shown on the timepicker. Maximum length for the text in this field is 150 characters. **`initial_time`** The initial time that is selected when the element is loaded. This should be in the format HH:mm, where HH is the 24-hour format of an hour (00 to 23) and mm is minutes with leading zeros (00 to 59), for example 22:25 for 10:25pm. **`confirm`** A confirm object that defines an optional confirmation dialog that appears after a time is selected. **`focus_on_load`** Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. **`timezone`** The timezone to consider for this input value. ### Ancestors * [InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement") * [InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement") * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"initial_time", "timezone"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement")**`: * `[action_id_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement.action_id_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.action_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.InputInteractiveElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.InputInteractiveElement.logger")` * `[placeholder_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.InputInteractiveElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.InputInteractiveElement.validate_json")` `class UrlInputElement (*, action_id: str | None = None, initial_value: str | None = None, dispatch_action_config: dict | [DispatchActionConfig](basic_components.html#slack_sdk.models.blocks.basic_components.DispatchActionConfig "slack_sdk.models.blocks.basic_components.DispatchActionConfig") | None = None, focus_on_load: bool | None = None, placeholder: str | dict | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject") | None = None, **others: dict)` Expand source code ``` class UrlInputElement(InputInteractiveElement): type = "url_text_input" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union( { "initial_value", "dispatch_action_config", } ) def __init__( self, *, action_id: Optional[str] = None, initial_value: Optional[str] = None, dispatch_action_config: Optional[Union[dict, DispatchActionConfig]] = None, focus_on_load: Optional[bool] = None, placeholder: Optional[Union[str, dict, TextObject]] = None, **others: dict, ): """ A URL input element, similar to the Plain-text input element, creates a single line field where a user can enter URL-encoded data. https://docs.slack.dev/reference/block-kit/block-elements/url-input-element Args: action_id (required): An identifier for the input value when the parent modal is submitted. You can use this when you receive a view_submission payload to identify the value of the input element. Should be unique among all other action_ids in the containing block. Maximum length for this field is 255 characters. initial_value: The initial value in the URL input when it is loaded. dispatch_action_config: A dispatch configuration object that determines when during text input the element returns a block_actions payload. focus_on_load: Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. placeholder: A plain_text only text object that defines the placeholder text shown in the URL input. Maximum length for the text in this field is 150 characters. """ super().__init__( type=self.type, action_id=action_id, placeholder=TextObject.parse(placeholder, PlainTextObject.type), # type: ignore[arg-type] focus_on_load=focus_on_load, ) show_unknown_key_warning(self, others) self.initial_value = initial_value self.dispatch_action_config = dispatch_action_config ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) A URL input element, similar to the Plain-text input element, creates a single line field where a user can enter URL-encoded data. [https://docs.slack.dev/reference/block-kit/block-elements/url-input-element](https://docs.slack.dev/reference/block-kit/block-elements/url-input-element) ## Args **`action_id`** : `required` An identifier for the input value when the parent modal is submitted. You can use this when you receive a view\_submission payload to identify the value of the input element. Should be unique among all other action\_ids in the containing block. Maximum length for this field is 255 characters. **`initial_value`** The initial value in the URL input when it is loaded. **`dispatch_action_config`** A dispatch configuration object that determines when during text input the element returns a block\_actions payload. **`focus_on_load`** Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. **`placeholder`** A plain\_text only text object that defines the placeholder text shown in the URL input. Maximum length for the text in this field is 150 characters. ### Ancestors * [InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement") * [InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement") * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union( { "initial_value", "dispatch_action_config", } ) ``` Build an unordered collection of unique elements. ### Inherited members * `**[InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement")**`: * `[action_id_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement.action_id_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.action_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.InputInteractiveElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.InputInteractiveElement.logger")` * `[placeholder_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.InputInteractiveElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.InputInteractiveElement.validate_json")` `class UrlSourceElement (*, url: str, text: str, **others: Dict)` Expand source code ``` class UrlSourceElement(BlockElement): type = "url" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union( { "url", "text", } ) def __init__( self, *, url: str, text: str, **others: Dict, ): """ A URL source element that displays a URL source for referencing within a task card block. https://docs.slack.dev/reference/block-kit/block-elements/url-source-element Args: url (required): The URL type source. text (required): Display text for the URL. """ super().__init__(type=self.type) show_unknown_key_warning(self, others) self.url = url self.text = text ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) A URL source element that displays a URL source for referencing within a task card block. [https://docs.slack.dev/reference/block-kit/block-elements/url-source-element](https://docs.slack.dev/reference/block-kit/block-elements/url-source-element) ## Args **`url`** : `required` The URL type source. **`text`** : `required` Display text for the URL. ### Ancestors * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union( { "url", "text", } ) ``` Build an unordered collection of unique elements. ### Inherited members * `**[BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.BlockElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.BlockElement.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.BlockElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.BlockElement.validate_json")` `class UserMultiSelectElement (*, action_id: str | None = None, placeholder: str | dict | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject") | None = None, initial_users: Sequence[str] | None = None, confirm: dict | [ConfirmObject](basic_components.html#slack_sdk.models.blocks.basic_components.ConfirmObject "slack_sdk.models.blocks.basic_components.ConfirmObject") | None = None, max_selected_items: int | None = None, focus_on_load: bool | None = None, **others: dict)` Expand source code ``` class UserMultiSelectElement(InputInteractiveElement): type = "multi_users_select" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"initial_users", "max_selected_items"}) def __init__( self, *, action_id: Optional[str] = None, placeholder: Optional[Union[str, dict, TextObject]] = None, initial_users: Optional[Sequence[str]] = None, confirm: Optional[Union[dict, ConfirmObject]] = None, max_selected_items: Optional[int] = None, focus_on_load: Optional[bool] = None, **others: dict, ): """ This select menu will populate its options with a list of Slack users visible to the current user in the active workspace. https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#users_multi_select Args: action_id (required): An identifier for the action triggered when a menu option is selected. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action_ids in the containing block. Maximum length for this field is 255 characters. placeholder (required): A plain_text only text object that defines the placeholder text shown on the menu. Maximum length for the text in this field is 150 characters. initial_users: An array of user IDs of any valid users to be pre-selected when the menu loads. confirm: A confirm object that defines an optional confirmation dialog that appears before the multi-select choices are submitted. max_selected_items: Specifies the maximum number of items that can be selected in the menu. Minimum number is 1. focus_on_load: Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. """ super().__init__( type=self.type, action_id=action_id, placeholder=TextObject.parse(placeholder, PlainTextObject.type), # type: ignore[arg-type] confirm=ConfirmObject.parse(confirm), # type: ignore[arg-type] focus_on_load=focus_on_load, ) show_unknown_key_warning(self, others) self.initial_users = initial_users self.max_selected_items = max_selected_items ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) This select menu will populate its options with a list of Slack users visible to the current user in the active workspace. [https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#users\_multi\_select](https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#users_multi_select) ## Args **`action_id`** : `required` An identifier for the action triggered when a menu option is selected. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action\_ids in the containing block. Maximum length for this field is 255 characters. **`placeholder`** : `required` A plain\_text only text object that defines the placeholder text shown on the menu. Maximum length for the text in this field is 150 characters. **`initial_users`** An array of user IDs of any valid users to be pre-selected when the menu loads. **`confirm`** A confirm object that defines an optional confirmation dialog that appears before the multi-select choices are submitted. **`max_selected_items`** Specifies the maximum number of items that can be selected in the menu. Minimum number is 1. **`focus_on_load`** Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. ### Ancestors * [InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement") * [InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement") * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"initial_users", "max_selected_items"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement")**`: * `[action_id_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement.action_id_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.action_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.InputInteractiveElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.InputInteractiveElement.logger")` * `[placeholder_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.InputInteractiveElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.InputInteractiveElement.validate_json")` `class UserSelectElement (*, placeholder: str | dict | [TextObject](basic_components.html#slack_sdk.models.blocks.basic_components.TextObject "slack_sdk.models.blocks.basic_components.TextObject") | None = None, action_id: str | None = None, initial_user: str | None = None, confirm: dict | [ConfirmObject](basic_components.html#slack_sdk.models.blocks.basic_components.ConfirmObject "slack_sdk.models.blocks.basic_components.ConfirmObject") | None = None, focus_on_load: bool | None = None, **others: dict)` Expand source code ``` class UserSelectElement(InputInteractiveElement): type = "users_select" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"initial_user"}) def __init__( self, *, placeholder: Optional[Union[str, dict, TextObject]] = None, action_id: Optional[str] = None, initial_user: Optional[str] = None, confirm: Optional[Union[dict, ConfirmObject]] = None, focus_on_load: Optional[bool] = None, **others: dict, ): """ This select menu will populate its options with a list of Slack users visible to the current user in the active workspace. https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element#users_select Args: placeholder (required): A plain_text only text object that defines the placeholder text shown on the menu. Maximum length for the text in this field is 150 characters. action_id (required): An identifier for the action triggered when a menu option is selected. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action_ids in the containing block. Maximum length for this field is 255 characters. initial_user: The user ID of any valid user to be pre-selected when the menu loads. confirm: A confirm object that defines an optional confirmation dialog that appears after a menu item is selected. focus_on_load: Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. """ super().__init__( type=self.type, action_id=action_id, placeholder=TextObject.parse(placeholder, PlainTextObject.type), # type: ignore[arg-type] confirm=ConfirmObject.parse(confirm), # type: ignore[arg-type] focus_on_load=focus_on_load, ) show_unknown_key_warning(self, others) self.initial_user = initial_user ``` Block Elements are things that exists inside of your Blocks. [https://docs.slack.dev/reference/block-kit/block-elements/](https://docs.slack.dev/reference/block-kit/block-elements/) This select menu will populate its options with a list of Slack users visible to the current user in the active workspace. [https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element#users\_select](https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element#users_select) ## Args **`placeholder`** : `required` A plain\_text only text object that defines the placeholder text shown on the menu. Maximum length for the text in this field is 150 characters. **`action_id`** : `required` An identifier for the action triggered when a menu option is selected. You can use this when you receive an interaction payload to identify the source of the action. Should be unique among all other action\_ids in the containing block. Maximum length for this field is 255 characters. **`initial_user`** The user ID of any valid user to be pre-selected when the menu loads. **`confirm`** A confirm object that defines an optional confirmation dialog that appears after a menu item is selected. **`focus_on_load`** Indicates whether the element will be set to auto focus within the view object. Only one element can be set to true. Defaults to false. ### Ancestors * [InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement") * [InteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement "slack_sdk.models.blocks.block_elements.InteractiveElement") * [BlockElement](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement "slack_sdk.models.blocks.block_elements.BlockElement") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"initial_user"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[InputInteractiveElement](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement "slack_sdk.models.blocks.block_elements.InputInteractiveElement")**`: * `[action_id_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InteractiveElement.action_id_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.action_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.block_elements.InputInteractiveElement.get_non_null_attributes")` * `[logger](block_elements.html#slack_sdk.models.blocks.block_elements.BlockElement.logger "slack_sdk.models.blocks.block_elements.InputInteractiveElement.logger")` * `[placeholder_max_length](block_elements.html#slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length "slack_sdk.models.blocks.block_elements.InputInteractiveElement.placeholder_max_length")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.block_elements.InputInteractiveElement.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.block_elements.InputInteractiveElement.validate_json")` `class VideoBlock (*, block_id: str | None = None, alt_text: str | None = None, video_url: str | None = None, thumbnail_url: str | None = None, title: str | dict | [PlainTextObject](basic_components.html#slack_sdk.models.blocks.basic_components.PlainTextObject "slack_sdk.models.blocks.basic_components.PlainTextObject") | None = None, title_url: str | None = None, description: str | dict | [PlainTextObject](basic_components.html#slack_sdk.models.blocks.basic_components.PlainTextObject "slack_sdk.models.blocks.basic_components.PlainTextObject") | None = None, provider_icon_url: str | None = None, provider_name: str | None = None, author_name: str | None = None, **others: dict)` Expand source code ``` class VideoBlock(Block): type = "video" title_max_length = 200 author_name_max_length = 50 @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union( { "alt_text", "video_url", "thumbnail_url", "title", "title_url", "description", "provider_icon_url", "provider_name", "author_name", } ) def __init__( self, *, block_id: Optional[str] = None, alt_text: Optional[str] = None, video_url: Optional[str] = None, thumbnail_url: Optional[str] = None, title: Optional[Union[str, dict, PlainTextObject]] = None, title_url: Optional[str] = None, description: Optional[Union[str, dict, PlainTextObject]] = None, provider_icon_url: Optional[str] = None, provider_name: Optional[str] = None, author_name: Optional[str] = None, **others: dict, ): """A video block is designed to embed videos in all app surfaces (e.g. link unfurls, messages, modals, App Home) — anywhere you can put blocks! To use the video block within your app, you must have the links.embed:write scope. https://docs.slack.dev/reference/block-kit/blocks/video-block Args: block_id: A string acting as a unique identifier for a block. If not specified, one will be generated. Maximum length for this field is 255 characters. block_id should be unique for each message and each iteration of a message. If a message is updated, use a new block_id. alt_text (required): A tooltip for the video. Required for accessibility video_url (required): The URL to be embedded. Must match any existing unfurl domains within the app and point to a HTTPS URL. thumbnail_url (required): The thumbnail image URL title (required): Video title in plain text format. Must be less than 200 characters. title_url: Hyperlink for the title text. Must correspond to the non-embeddable URL for the video. Must go to an HTTPS URL. description: Description for video in plain text format. provider_icon_url: Icon for the video provider - ex. Youtube icon provider_name: The originating application or domain of the video ex. Youtube author_name: Author name to be displayed. Must be less than 50 characters. """ super().__init__(type=self.type, block_id=block_id) show_unknown_key_warning(self, others) self.alt_text = alt_text self.video_url = video_url self.thumbnail_url = thumbnail_url self.title = TextObject.parse(title, default_type=PlainTextObject.type) # type: ignore[arg-type] self.title_url = title_url self.description = TextObject.parse(description, default_type=PlainTextObject.type) # type: ignore[arg-type] self.provider_icon_url = provider_icon_url self.provider_name = provider_name self.author_name = author_name @JsonValidator("alt_text attribute must be specified") def _validate_alt_text(self): return self.alt_text is not None @JsonValidator("video_url attribute must be specified") def _validate_video_url(self): return self.video_url is not None @JsonValidator("thumbnail_url attribute must be specified") def _validate_thumbnail_url(self): return self.thumbnail_url is not None @JsonValidator("title attribute must be specified") def _validate_title(self): return self.title is not None @JsonValidator(f"title attribute cannot exceed {title_max_length} characters") def _validate_title_length(self): return self.title is None or len(self.title.text) < self.title_max_length @JsonValidator(f"author_name attribute cannot exceed {author_name_max_length} characters") def _validate_author_name_length(self): return self.author_name is None or len(self.author_name) < self.author_name_max_length ``` Blocks are a series of components that can be combined to create visually rich and compellingly interactive messages. [https://docs.slack.dev/reference/block-kit/blocks](https://docs.slack.dev/reference/block-kit/blocks) A video block is designed to embed videos in all app surfaces (e.g. link unfurls, messages, modals, App Home) — anywhere you can put blocks! To use the video block within your app, you must have the links.embed:write scope. [https://docs.slack.dev/reference/block-kit/blocks/video-block](https://docs.slack.dev/reference/block-kit/blocks/video-block) ## Args **`block_id`** A string acting as a unique identifier for a block. If not specified, one will be generated. Maximum length for this field is 255 characters. block\_id should be unique for each message and each iteration of a message. If a message is updated, use a new block\_id. **`alt_text`** : `required` A tooltip for the video. Required for accessibility **`video_url`** : `required` The URL to be embedded. Must match any existing unfurl domains within the app and point to a HTTPS URL. **`thumbnail_url`** : `required` The thumbnail image URL **`title`** : `required` Video title in plain text format. Must be less than 200 characters. **`title_url`** Hyperlink for the title text. Must correspond to the non-embeddable URL for the video. Must go to an HTTPS URL. **`description`** Description for video in plain text format. **`provider_icon_url`** Icon for the video provider - ex. Youtube icon **`provider_name`** The originating application or domain of the video ex. Youtube **`author_name`** Author name to be displayed. Must be less than 50 characters. ### Ancestors * [Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var author_name_max_length` The type of the None singleton. `var title_max_length` The type of the None singleton. `var type` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union( { "alt_text", "video_url", "thumbnail_url", "title", "title_url", "description", "provider_icon_url", "provider_name", "author_name", } ) ``` Build an unordered collection of unique elements. ### Inherited members * `**[Block](blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")**`: * `[block_id_max_length](blocks.html#slack_sdk.models.blocks.blocks.Block.block_id_max_length "slack_sdk.models.blocks.blocks.Block.block_id_max_length")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.blocks.blocks.Block.get_non_null_attributes")` * `[logger](blocks.html#slack_sdk.models.blocks.blocks.Block.logger "slack_sdk.models.blocks.blocks.Block.logger")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.blocks.blocks.Block.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.blocks.blocks.Block.validate_json")` --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/models/dialogs # Module slack_sdk.models.dialogs ## Classes `class AbstractDialogSelector (*, name: str, label: str, optional: bool = False, value: [Option](../blocks/basic_components.html#slack_sdk.models.blocks.basic_components.Option "slack_sdk.models.blocks.basic_components.Option") | str | None = None, placeholder: str | None = None)` Expand source code ``` class AbstractDialogSelector(JsonObject, metaclass=ABCMeta): DataSourceTypes = DynamicSelectElementTypes.union({"external", "static"}) attributes = {"data_source", "label", "name", "optional", "placeholder", "type"} name_max_length = 300 label_max_length = 48 placeholder_max_length = 150 @property @abstractmethod def data_source(self) -> str: pass def __init__( self, *, name: str, label: str, optional: bool = False, value: Optional[Union[Option, str]] = None, placeholder: Optional[str] = None, ): self.name = name self.label = label self.optional = optional self.value = value self.placeholder = placeholder self.type = "select" @JsonValidator(f"name attribute cannot exceed {name_max_length} characters") def name_length(self) -> bool: return len(self.name) < self.name_max_length @JsonValidator(f"label attribute cannot exceed {label_max_length} characters") def label_length(self) -> bool: return len(self.label) < self.label_max_length @JsonValidator(f"placeholder attribute cannot exceed {placeholder_max_length} characters") def placeholder_length(self) -> bool: return self.placeholder is None or len(self.placeholder) < self.placeholder_max_length @EnumValidator("data_source", DataSourceTypes) def data_source_valid(self) -> bool: return self.data_source in self.DataSourceTypes def to_dict(self) -> dict: json = super().to_dict() if self.data_source == "external": if isinstance(self.value, Option): json["selected_options"] = extract_json([self.value], "dialog") elif self.value is not None: json["selected_options"] = Option.from_single_value(self.value) else: if isinstance(self.value, Option): json["value"] = self.value.value elif self.value is not None: json["value"] = self.value return json ``` The base class for JSON serializable class objects ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Subclasses * [DialogChannelSelector](#slack_sdk.models.dialogs.DialogChannelSelector "slack_sdk.models.dialogs.DialogChannelSelector") * [DialogConversationSelector](#slack_sdk.models.dialogs.DialogConversationSelector "slack_sdk.models.dialogs.DialogConversationSelector") * [DialogExternalSelector](#slack_sdk.models.dialogs.DialogExternalSelector "slack_sdk.models.dialogs.DialogExternalSelector") * [DialogStaticSelector](#slack_sdk.models.dialogs.DialogStaticSelector "slack_sdk.models.dialogs.DialogStaticSelector") * [DialogUserSelector](#slack_sdk.models.dialogs.DialogUserSelector "slack_sdk.models.dialogs.DialogUserSelector") ### Class variables `var DataSourceTypes` The type of the None singleton. `var attributes` The type of the None singleton. `var label_max_length` The type of the None singleton. `var name_max_length` The type of the None singleton. `var placeholder_max_length` The type of the None singleton. ### Instance variables `prop data_source : str` Expand source code ``` @property @abstractmethod def data_source(self) -> str: pass ``` ### Methods `def data_source_valid(self) ‑> bool` Expand source code ``` @EnumValidator("data_source", DataSourceTypes) def data_source_valid(self) -> bool: return self.data_source in self.DataSourceTypes ``` `def label_length(self) ‑> bool` Expand source code ``` @JsonValidator(f"label attribute cannot exceed {label_max_length} characters") def label_length(self) -> bool: return len(self.label) < self.label_max_length ``` `def name_length(self) ‑> bool` Expand source code ``` @JsonValidator(f"name attribute cannot exceed {name_max_length} characters") def name_length(self) -> bool: return len(self.name) < self.name_max_length ``` `def placeholder_length(self) ‑> bool` Expand source code ``` @JsonValidator(f"placeholder attribute cannot exceed {placeholder_max_length} characters") def placeholder_length(self) -> bool: return self.placeholder is None or len(self.placeholder) < self.placeholder_max_length ``` ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class DialogBuilder` Expand source code ``` class DialogBuilder(JsonObject): attributes: Set[str] = set() _callback_id: Optional[str] _elements: List[Union[DialogTextComponent, AbstractDialogSelector]] _submit_label: Optional[str] _notify_on_cancel: bool _state: Optional[str] title_max_length = 24 submit_label_max_length = 24 elements_max_length = 10 state_max_length = 3000 def __init__(self): """ Create a DialogBuilder to more easily construct the JSON required to submit a dialog to Slack """ self._title = None self._callback_id = None self._elements = [] self._submit_label = None self._notify_on_cancel = False self._state = None def title(self, title: str) -> "DialogBuilder": """ Specify a title for this dialog Args: title: must not exceed 24 characters """ self._title = title return self def state(self, state: Union[dict, str]) -> "DialogBuilder": """ Pass state into this dialog - dictionaries will be automatically formatted to JSON Args: state: Extra state information that you need to pass from this dialog back to your application on submission """ if isinstance(state, dict): self._state = dumps(state) else: self._state = state return self def callback_id(self, callback_id: str) -> "DialogBuilder": """ Specify a callback ID for this dialog, which your application will then receive upon dialog submission Args: callback_id: a string identifying this particular dialog """ self._callback_id = callback_id return self def submit_label(self, label: str) -> "DialogBuilder": """ The label to use on the 'Submit' button on the dialog. Defaults to 'Submit' if not specified. Args: label: must not exceed 24 characters, and must be a single word (no spaces) """ self._submit_label = label return self def notify_on_cancel(self, notify: bool) -> "DialogBuilder": """ Whether this dialog should send a request to your application even if the user cancels their interaction. Defaults to False. Args: notify: Set to True to indicate that your application should receive a request even if the user cancels interaction with the dialog. """ self._notify_on_cancel = notify return self def text_field( self, *, name: str, label: str, optional: bool = False, placeholder: Optional[str] = None, hint: Optional[str] = None, value: Optional[str] = None, min_length: int = 0, max_length: int = 150, subtype: Optional[str] = None, ) -> "DialogBuilder": """ Text elements are single-line plain text fields. https://docs.slack.dev/legacy/legacy-dialogs/#attributes_text_elements Args: name: Name of form element. Required. No more than 300 characters. label: Label displayed to user. Required. 48 character maximum. optional: Provide true when the form element is not required. By default, form elements are required. placeholder: A string displayed as needed to help guide users in completing the element. 150 character maximum. hint: Helpful text provided to assist users in answering a question. Up to 150 characters. value: A default value for this field. Up to 150 characters. min_length: Minimum input length allowed for element. Up to 150 characters. Defaults to 0. max_length: Maximum input length allowed for element. Up to 150 characters. Defaults to 150. subtype: A subtype for this text input. Accepts email, number, tel, or url. In some form factors, optimized input is provided for this subtype. """ self._elements.append( DialogTextField( name=name, label=label, optional=optional, placeholder=placeholder, hint=hint, value=value, min_length=min_length, max_length=max_length, subtype=subtype, ) ) return self def text_area( self, *, name: str, label: str, optional: bool = False, placeholder: Optional[str] = None, hint: Optional[str] = None, value: Optional[str] = None, min_length: int = 0, max_length: int = 3000, subtype: Optional[str] = None, ) -> "DialogBuilder": """ A textarea is a multi-line plain text editing control. You've likely encountered these on the world wide web. Use this element if you want a relatively long answer from users. The element UI provides a remaining character count to the max_length you have set or the default, 3000. https://docs.slack.dev/legacy/legacy-dialogs/#attributes_textarea_elements Args: name: Name of form element. Required. No more than 300 characters. label: Label displayed to user. Required. 48 character maximum. optional: Provide true when the form element is not required. By default, form elements are required. placeholder: A string displayed as needed to help guide users in completing the element. 150 character maximum. hint: Helpful text provided to assist users in answering a question. Up to 150 characters. value: A default value for this field. Up to 3000 characters. min_length: Minimum input length allowed for element. 1-3000 characters. Defaults to 0. max_length: Maximum input length allowed for element. 0-3000 characters. Defaults to 3000. subtype: A subtype for this text input. Accepts email, number, tel, or url. In some form factors, optimized input is provided for this subtype. """ self._elements.append( DialogTextArea( name=name, label=label, optional=optional, placeholder=placeholder, hint=hint, value=value, min_length=min_length, max_length=max_length, subtype=subtype, ) ) return self def static_selector( self, *, name: str, label: str, options: Union[Sequence[Option], Sequence[OptionGroup]], optional: bool = False, value: Optional[str] = None, placeholder: Optional[str] = None, ) -> "DialogBuilder": """ Use the select element for multiple choice selections allowing users to pick a single item from a list. True to web roots, this selection is displayed as a dropdown menu. A select element may contain up to 100 selections, provided as a list of Option or OptionGroup objects https://docs.slack.dev/legacy/legacy-dialogs/#attributes_select_elements Args: name: Name of form element. Required. No more than 300 characters. label: Label displayed to user. Required. No more than 48 characters. options: A list of up to 100 Option or OptionGroup objects. Object types cannot be mixed. optional: Provide true when the form element is not required. By default, form elements are required. value: Provide a default selected value. placeholder: A string displayed as needed to help guide users in completing the element. 150 character maximum. """ self._elements.append( DialogStaticSelector( name=name, label=label, options=options, optional=optional, value=value, placeholder=placeholder, ) ) return self def external_selector( self, *, name: str, label: str, optional: bool = False, value: Optional[Option] = None, placeholder: Optional[str] = None, min_query_length: Optional[int] = None, ) -> "DialogBuilder": """ Use the select element for multiple choice selections allowing users to pick a single item from a list. True to web roots, this selection is displayed as a dropdown menu. A list of options can be loaded from an external URL and used in your dialog menus. https://docs.slack.dev/legacy/legacy-dialogs/#dynamic_select_elements_external Args: name: Name of form element. Required. No more than 300 characters. label: Label displayed to user. Required. No more than 48 characters. min_query_length: Specify the number of characters that must be typed by a user into a dynamic select menu before dispatching to your application. optional: Provide true when the form element is not required. By default, form elements are required. value: Provide a default selected value. This should be a single Option or OptionGroup that exactly matches one that will be returned from your external endpoint. placeholder: A string displayed as needed to help guide users in completing the element. 150 character maximum. """ self._elements.append( DialogExternalSelector( name=name, label=label, optional=optional, value=value, placeholder=placeholder, min_query_length=min_query_length, ) ) return self def user_selector( self, *, name: str, label: str, optional: bool = False, value: Optional[str] = None, placeholder: Optional[str] = None, ) -> "DialogBuilder": """ Now you can easily populate a select menu with a list of users. For example, when you are creating a bug tracking app, you want to include a field for an assignee. Slack pre-populates the user list in client-side, so your app doesn't need access to a related OAuth scope. https://docs.slack.dev/legacy/legacy-dialogs/#dynamic_select_elements_users Args: name: Name of form element. Required. No more than 300 characters. label: Label displayed to user. Required. No more than 48 characters. optional: Provide true when the form element is not required. By default, form elements are required. value: Provide a default selected value. placeholder: A string displayed as needed to help guide users in completing the element. 150 character maximum. """ self._elements.append( DialogUserSelector( name=name, label=label, optional=optional, value=value, placeholder=placeholder, ) ) return self def channel_selector( self, *, name: str, label: str, optional: bool = False, value: Optional[str] = None, placeholder: Optional[str] = None, ) -> "DialogBuilder": """ You can also provide a select menu with a list of channels. Specify your data_source as channels to limit only to public channels https://docs.slack.dev/legacy/legacy-dialogs/#dynamic_select_elements_channels_conversations Args: name: Name of form element. Required. No more than 300 characters. label: Label displayed to user. Required. No more than 48 characters. optional: Provide true when the form element is not required. By default, form elements are required. value: Provide a default selected value. placeholder: A string displayed as needed to help guide users in completing the element. 150 character maximum. """ self._elements.append( DialogChannelSelector( name=name, label=label, optional=optional, value=value, placeholder=placeholder, ) ) return self def conversation_selector( self, *, name: str, label: str, optional: bool = False, value: Optional[str] = None, placeholder: Optional[str] = None, ) -> "DialogBuilder": """ You can also provide a select menu with a list of conversations - including private channels, direct messages, MPIMs, and whatever else we consider a conversation-like thing. https://docs.slack.dev/legacy/legacy-dialogs/#dynamic_select_elements_channels_conversations Args: name: Name of form element. Required. No more than 300 characters. label: Label displayed to user. Required. No more than 48 characters. optional: Provide true when the form element is not required. By default, form elements are required. value: Provide a default selected value. placeholder: A string displayed as needed to help guide users in completing the element. 150 character maximum. """ self._elements.append( DialogConversationSelector( name=name, label=label, optional=optional, value=value, placeholder=placeholder, ) ) return self @JsonValidator("title attribute is required") def title_present(self) -> bool: return self._title is not None @JsonValidator(f"title attribute cannot exceed {title_max_length} characters") def title_length(self) -> bool: return self._title is not None and len(self._title) <= self.title_max_length @JsonValidator("callback_id attribute is required") def callback_id_present(self) -> bool: return self._callback_id is not None @JsonValidator(f"dialogs must contain between 1 and {elements_max_length} elements") def elements_length(self) -> bool: return 0 < len(self._elements) <= self.elements_max_length @JsonValidator(f"submit_label cannot exceed {submit_label_max_length} characters") def submit_label_length(self) -> bool: return self._submit_label is None or len(self._submit_label) <= self.submit_label_max_length @JsonValidator("submit_label can only be one word") def submit_label_valid(self) -> bool: return self._submit_label is None or " " not in self._submit_label @JsonValidator(f"state cannot exceed {state_max_length} characters") def state_length(self) -> bool: return not self._state or len(self._state) <= self.state_max_length def to_dict(self) -> dict: self.validate_json() json = { "title": self._title, "callback_id": self._callback_id, "elements": extract_json(self._elements), "notify_on_cancel": self._notify_on_cancel, } if self._submit_label is not None: json["submit_label"] = self._submit_label if self._state is not None: json["state"] = self._state return json ``` The base class for JSON serializable class objects Create a DialogBuilder to more easily construct the JSON required to submit a dialog to Slack ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes : Set[str]` The type of the None singleton. `var elements_max_length` The type of the None singleton. `var state_max_length` The type of the None singleton. `var submit_label_max_length` The type of the None singleton. `var title_max_length` The type of the None singleton. ### Methods `def callback_id(self, callback_id: str) ‑> [DialogBuilder](#slack_sdk.models.dialogs.DialogBuilder "slack_sdk.models.dialogs.DialogBuilder")` Expand source code ``` def callback_id(self, callback_id: str) -> "DialogBuilder": """ Specify a callback ID for this dialog, which your application will then receive upon dialog submission Args: callback_id: a string identifying this particular dialog """ self._callback_id = callback_id return self ``` Specify a callback ID for this dialog, which your application will then receive upon dialog submission ## Args **`callback_id`** a string identifying this particular dialog `def callback_id_present(self) ‑> bool` Expand source code ``` @JsonValidator("callback_id attribute is required") def callback_id_present(self) -> bool: return self._callback_id is not None ``` `def channel_selector(self, *, name: str, label: str, optional: bool = False, value: str | None = None, placeholder: str | None = None) ‑> [DialogBuilder](#slack_sdk.models.dialogs.DialogBuilder "slack_sdk.models.dialogs.DialogBuilder")` Expand source code ``` def channel_selector( self, *, name: str, label: str, optional: bool = False, value: Optional[str] = None, placeholder: Optional[str] = None, ) -> "DialogBuilder": """ You can also provide a select menu with a list of channels. Specify your data_source as channels to limit only to public channels https://docs.slack.dev/legacy/legacy-dialogs/#dynamic_select_elements_channels_conversations Args: name: Name of form element. Required. No more than 300 characters. label: Label displayed to user. Required. No more than 48 characters. optional: Provide true when the form element is not required. By default, form elements are required. value: Provide a default selected value. placeholder: A string displayed as needed to help guide users in completing the element. 150 character maximum. """ self._elements.append( DialogChannelSelector( name=name, label=label, optional=optional, value=value, placeholder=placeholder, ) ) return self ``` You can also provide a select menu with a list of channels. Specify your data\_source as channels to limit only to public channels [https://docs.slack.dev/legacy/legacy-dialogs/#dynamic\_select\_elements\_channels\_conversations](https://docs.slack.dev/legacy/legacy-dialogs/#dynamic_select_elements_channels_conversations) ## Args **`name`** Name of form element. Required. No more than 300 characters. **`label`** Label displayed to user. Required. No more than 48 characters. **`optional`** Provide true when the form element is not required. By default, form elements are required. **`value`** Provide a default selected value. **`placeholder`** A string displayed as needed to help guide users in completing the element. 150 character maximum. `def conversation_selector(self, *, name: str, label: str, optional: bool = False, value: str | None = None, placeholder: str | None = None) ‑> [DialogBuilder](#slack_sdk.models.dialogs.DialogBuilder "slack_sdk.models.dialogs.DialogBuilder")` Expand source code ``` def conversation_selector( self, *, name: str, label: str, optional: bool = False, value: Optional[str] = None, placeholder: Optional[str] = None, ) -> "DialogBuilder": """ You can also provide a select menu with a list of conversations - including private channels, direct messages, MPIMs, and whatever else we consider a conversation-like thing. https://docs.slack.dev/legacy/legacy-dialogs/#dynamic_select_elements_channels_conversations Args: name: Name of form element. Required. No more than 300 characters. label: Label displayed to user. Required. No more than 48 characters. optional: Provide true when the form element is not required. By default, form elements are required. value: Provide a default selected value. placeholder: A string displayed as needed to help guide users in completing the element. 150 character maximum. """ self._elements.append( DialogConversationSelector( name=name, label=label, optional=optional, value=value, placeholder=placeholder, ) ) return self ``` You can also provide a select menu with a list of conversations - including private channels, direct messages, MPIMs, and whatever else we consider a conversation-like thing. [https://docs.slack.dev/legacy/legacy-dialogs/#dynamic\_select\_elements\_channels\_conversations](https://docs.slack.dev/legacy/legacy-dialogs/#dynamic_select_elements_channels_conversations) ## Args **`name`** Name of form element. Required. No more than 300 characters. **`label`** Label displayed to user. Required. No more than 48 characters. **`optional`** Provide true when the form element is not required. By default, form elements are required. **`value`** Provide a default selected value. **`placeholder`** A string displayed as needed to help guide users in completing the element. 150 character maximum. `def elements_length(self) ‑> bool` Expand source code ``` @JsonValidator(f"dialogs must contain between 1 and {elements_max_length} elements") def elements_length(self) -> bool: return 0 < len(self._elements) <= self.elements_max_length ``` `def external_selector(self, *, name: str, label: str, optional: bool = False, value: [Option](../blocks/basic_components.html#slack_sdk.models.blocks.basic_components.Option "slack_sdk.models.blocks.basic_components.Option") | None = None, placeholder: str | None = None, min_query_length: int | None = None) ‑> [DialogBuilder](#slack_sdk.models.dialogs.DialogBuilder "slack_sdk.models.dialogs.DialogBuilder")` Expand source code ``` def external_selector( self, *, name: str, label: str, optional: bool = False, value: Optional[Option] = None, placeholder: Optional[str] = None, min_query_length: Optional[int] = None, ) -> "DialogBuilder": """ Use the select element for multiple choice selections allowing users to pick a single item from a list. True to web roots, this selection is displayed as a dropdown menu. A list of options can be loaded from an external URL and used in your dialog menus. https://docs.slack.dev/legacy/legacy-dialogs/#dynamic_select_elements_external Args: name: Name of form element. Required. No more than 300 characters. label: Label displayed to user. Required. No more than 48 characters. min_query_length: Specify the number of characters that must be typed by a user into a dynamic select menu before dispatching to your application. optional: Provide true when the form element is not required. By default, form elements are required. value: Provide a default selected value. This should be a single Option or OptionGroup that exactly matches one that will be returned from your external endpoint. placeholder: A string displayed as needed to help guide users in completing the element. 150 character maximum. """ self._elements.append( DialogExternalSelector( name=name, label=label, optional=optional, value=value, placeholder=placeholder, min_query_length=min_query_length, ) ) return self ``` Use the select element for multiple choice selections allowing users to pick a single item from a list. True to web roots, this selection is displayed as a dropdown menu. A list of options can be loaded from an external URL and used in your dialog menus. [https://docs.slack.dev/legacy/legacy-dialogs/#dynamic\_select\_elements\_external](https://docs.slack.dev/legacy/legacy-dialogs/#dynamic_select_elements_external) ## Args **`name`** Name of form element. Required. No more than 300 characters. **`label`** Label displayed to user. Required. No more than 48 characters. **`min_query_length`** Specify the number of characters that must be typed by a user into a dynamic select menu before dispatching to your application. **`optional`** Provide true when the form element is not required. By default, form elements are required. **`value`** Provide a default selected value. This should be a single Option or OptionGroup that exactly matches one that will be returned from your external endpoint. **`placeholder`** A string displayed as needed to help guide users in completing the element. 150 character maximum. `def notify_on_cancel(self, notify: bool) ‑> [DialogBuilder](#slack_sdk.models.dialogs.DialogBuilder "slack_sdk.models.dialogs.DialogBuilder")` Expand source code ``` def notify_on_cancel(self, notify: bool) -> "DialogBuilder": """ Whether this dialog should send a request to your application even if the user cancels their interaction. Defaults to False. Args: notify: Set to True to indicate that your application should receive a request even if the user cancels interaction with the dialog. """ self._notify_on_cancel = notify return self ``` Whether this dialog should send a request to your application even if the user cancels their interaction. Defaults to False. ## Args **`notify`** Set to True to indicate that your application should receive a request even if the user cancels interaction with the dialog. `def state(self, state: dict | str) ‑> [DialogBuilder](#slack_sdk.models.dialogs.DialogBuilder "slack_sdk.models.dialogs.DialogBuilder")` Expand source code ``` def state(self, state: Union[dict, str]) -> "DialogBuilder": """ Pass state into this dialog - dictionaries will be automatically formatted to JSON Args: state: Extra state information that you need to pass from this dialog back to your application on submission """ if isinstance(state, dict): self._state = dumps(state) else: self._state = state return self ``` Pass state into this dialog - dictionaries will be automatically formatted to JSON ## Args **`state`** Extra state information that you need to pass from this dialog back to your application on submission `def state_length(self) ‑> bool` Expand source code ``` @JsonValidator(f"state cannot exceed {state_max_length} characters") def state_length(self) -> bool: return not self._state or len(self._state) <= self.state_max_length ``` `def static_selector(self, *, name: str, label: str, options: Sequence[[Option](../blocks/basic_components.html#slack_sdk.models.blocks.basic_components.Option "slack_sdk.models.blocks.basic_components.Option")] | Sequence[[OptionGroup](../blocks/basic_components.html#slack_sdk.models.blocks.basic_components.OptionGroup "slack_sdk.models.blocks.basic_components.OptionGroup")], optional: bool = False, value: str | None = None, placeholder: str | None = None) ‑> [DialogBuilder](#slack_sdk.models.dialogs.DialogBuilder "slack_sdk.models.dialogs.DialogBuilder")` Expand source code ``` def static_selector( self, *, name: str, label: str, options: Union[Sequence[Option], Sequence[OptionGroup]], optional: bool = False, value: Optional[str] = None, placeholder: Optional[str] = None, ) -> "DialogBuilder": """ Use the select element for multiple choice selections allowing users to pick a single item from a list. True to web roots, this selection is displayed as a dropdown menu. A select element may contain up to 100 selections, provided as a list of Option or OptionGroup objects https://docs.slack.dev/legacy/legacy-dialogs/#attributes_select_elements Args: name: Name of form element. Required. No more than 300 characters. label: Label displayed to user. Required. No more than 48 characters. options: A list of up to 100 Option or OptionGroup objects. Object types cannot be mixed. optional: Provide true when the form element is not required. By default, form elements are required. value: Provide a default selected value. placeholder: A string displayed as needed to help guide users in completing the element. 150 character maximum. """ self._elements.append( DialogStaticSelector( name=name, label=label, options=options, optional=optional, value=value, placeholder=placeholder, ) ) return self ``` Use the select element for multiple choice selections allowing users to pick a single item from a list. True to web roots, this selection is displayed as a dropdown menu. A select element may contain up to 100 selections, provided as a list of Option or OptionGroup objects [https://docs.slack.dev/legacy/legacy-dialogs/#attributes\_select\_elements](https://docs.slack.dev/legacy/legacy-dialogs/#attributes_select_elements) ## Args **`name`** Name of form element. Required. No more than 300 characters. **`label`** Label displayed to user. Required. No more than 48 characters. **`options`** A list of up to 100 Option or OptionGroup objects. Object types cannot be mixed. **`optional`** Provide true when the form element is not required. By default, form elements are required. **`value`** Provide a default selected value. **`placeholder`** A string displayed as needed to help guide users in completing the element. 150 character maximum. `def submit_label(self, label: str) ‑> [DialogBuilder](#slack_sdk.models.dialogs.DialogBuilder "slack_sdk.models.dialogs.DialogBuilder")` Expand source code ``` def submit_label(self, label: str) -> "DialogBuilder": """ The label to use on the 'Submit' button on the dialog. Defaults to 'Submit' if not specified. Args: label: must not exceed 24 characters, and must be a single word (no spaces) """ self._submit_label = label return self ``` The label to use on the 'Submit' button on the dialog. Defaults to 'Submit' if not specified. ## Args **`label`** must not exceed 24 characters, and must be a single word (no spaces) `def submit_label_length(self) ‑> bool` Expand source code ``` @JsonValidator(f"submit_label cannot exceed {submit_label_max_length} characters") def submit_label_length(self) -> bool: return self._submit_label is None or len(self._submit_label) <= self.submit_label_max_length ``` `def submit_label_valid(self) ‑> bool` Expand source code ``` @JsonValidator("submit_label can only be one word") def submit_label_valid(self) -> bool: return self._submit_label is None or " " not in self._submit_label ``` `def text_area(self, *, name: str, label: str, optional: bool = False, placeholder: str | None = None, hint: str | None = None, value: str | None = None, min_length: int = 0, max_length: int = 3000, subtype: str | None = None) ‑> [DialogBuilder](#slack_sdk.models.dialogs.DialogBuilder "slack_sdk.models.dialogs.DialogBuilder")` Expand source code ``` def text_area( self, *, name: str, label: str, optional: bool = False, placeholder: Optional[str] = None, hint: Optional[str] = None, value: Optional[str] = None, min_length: int = 0, max_length: int = 3000, subtype: Optional[str] = None, ) -> "DialogBuilder": """ A textarea is a multi-line plain text editing control. You've likely encountered these on the world wide web. Use this element if you want a relatively long answer from users. The element UI provides a remaining character count to the max_length you have set or the default, 3000. https://docs.slack.dev/legacy/legacy-dialogs/#attributes_textarea_elements Args: name: Name of form element. Required. No more than 300 characters. label: Label displayed to user. Required. 48 character maximum. optional: Provide true when the form element is not required. By default, form elements are required. placeholder: A string displayed as needed to help guide users in completing the element. 150 character maximum. hint: Helpful text provided to assist users in answering a question. Up to 150 characters. value: A default value for this field. Up to 3000 characters. min_length: Minimum input length allowed for element. 1-3000 characters. Defaults to 0. max_length: Maximum input length allowed for element. 0-3000 characters. Defaults to 3000. subtype: A subtype for this text input. Accepts email, number, tel, or url. In some form factors, optimized input is provided for this subtype. """ self._elements.append( DialogTextArea( name=name, label=label, optional=optional, placeholder=placeholder, hint=hint, value=value, min_length=min_length, max_length=max_length, subtype=subtype, ) ) return self ``` A textarea is a multi-line plain text editing control. You've likely encountered these on the world wide web. Use this element if you want a relatively long answer from users. The element UI provides a remaining character count to the max\_length you have set or the default, 3000. [https://docs.slack.dev/legacy/legacy-dialogs/#attributes\_textarea\_elements](https://docs.slack.dev/legacy/legacy-dialogs/#attributes_textarea_elements) ## Args **`name`** Name of form element. Required. No more than 300 characters. **`label`** Label displayed to user. Required. 48 character maximum. **`optional`** Provide true when the form element is not required. By default, form elements are required. **`placeholder`** A string displayed as needed to help guide users in completing the element. 150 character maximum. **`hint`** Helpful text provided to assist users in answering a question. Up to 150 characters. **`value`** A default value for this field. Up to 3000 characters. **`min_length`** Minimum input length allowed for element. 1-3000 characters. Defaults to 0. **`max_length`** Maximum input length allowed for element. 0-3000 characters. Defaults to 3000. **`subtype`** A subtype for this text input. Accepts email, number, tel, or url. In some form factors, optimized input is provided for this subtype. `def text_field(self, *, name: str, label: str, optional: bool = False, placeholder: str | None = None, hint: str | None = None, value: str | None = None, min_length: int = 0, max_length: int = 150, subtype: str | None = None) ‑> [DialogBuilder](#slack_sdk.models.dialogs.DialogBuilder "slack_sdk.models.dialogs.DialogBuilder")` Expand source code ``` def text_field( self, *, name: str, label: str, optional: bool = False, placeholder: Optional[str] = None, hint: Optional[str] = None, value: Optional[str] = None, min_length: int = 0, max_length: int = 150, subtype: Optional[str] = None, ) -> "DialogBuilder": """ Text elements are single-line plain text fields. https://docs.slack.dev/legacy/legacy-dialogs/#attributes_text_elements Args: name: Name of form element. Required. No more than 300 characters. label: Label displayed to user. Required. 48 character maximum. optional: Provide true when the form element is not required. By default, form elements are required. placeholder: A string displayed as needed to help guide users in completing the element. 150 character maximum. hint: Helpful text provided to assist users in answering a question. Up to 150 characters. value: A default value for this field. Up to 150 characters. min_length: Minimum input length allowed for element. Up to 150 characters. Defaults to 0. max_length: Maximum input length allowed for element. Up to 150 characters. Defaults to 150. subtype: A subtype for this text input. Accepts email, number, tel, or url. In some form factors, optimized input is provided for this subtype. """ self._elements.append( DialogTextField( name=name, label=label, optional=optional, placeholder=placeholder, hint=hint, value=value, min_length=min_length, max_length=max_length, subtype=subtype, ) ) return self ``` Text elements are single-line plain text fields. [https://docs.slack.dev/legacy/legacy-dialogs/#attributes\_text\_elements](https://docs.slack.dev/legacy/legacy-dialogs/#attributes_text_elements) ## Args **`name`** Name of form element. Required. No more than 300 characters. **`label`** Label displayed to user. Required. 48 character maximum. **`optional`** Provide true when the form element is not required. By default, form elements are required. **`placeholder`** A string displayed as needed to help guide users in completing the element. 150 character maximum. **`hint`** Helpful text provided to assist users in answering a question. Up to 150 characters. **`value`** A default value for this field. Up to 150 characters. **`min_length`** Minimum input length allowed for element. Up to 150 characters. Defaults to 0. **`max_length`** Maximum input length allowed for element. Up to 150 characters. Defaults to 150. **`subtype`** A subtype for this text input. Accepts email, number, tel, or url. In some form factors, optimized input is provided for this subtype. `def title(self, title: str) ‑> [DialogBuilder](#slack_sdk.models.dialogs.DialogBuilder "slack_sdk.models.dialogs.DialogBuilder")` Expand source code ``` def title(self, title: str) -> "DialogBuilder": """ Specify a title for this dialog Args: title: must not exceed 24 characters """ self._title = title return self ``` Specify a title for this dialog ## Args **`title`** must not exceed 24 characters `def title_length(self) ‑> bool` Expand source code ``` @JsonValidator(f"title attribute cannot exceed {title_max_length} characters") def title_length(self) -> bool: return self._title is not None and len(self._title) <= self.title_max_length ``` `def title_present(self) ‑> bool` Expand source code ``` @JsonValidator("title attribute is required") def title_present(self) -> bool: return self._title is not None ``` `def user_selector(self, *, name: str, label: str, optional: bool = False, value: str | None = None, placeholder: str | None = None) ‑> [DialogBuilder](#slack_sdk.models.dialogs.DialogBuilder "slack_sdk.models.dialogs.DialogBuilder")` Expand source code ``` def user_selector( self, *, name: str, label: str, optional: bool = False, value: Optional[str] = None, placeholder: Optional[str] = None, ) -> "DialogBuilder": """ Now you can easily populate a select menu with a list of users. For example, when you are creating a bug tracking app, you want to include a field for an assignee. Slack pre-populates the user list in client-side, so your app doesn't need access to a related OAuth scope. https://docs.slack.dev/legacy/legacy-dialogs/#dynamic_select_elements_users Args: name: Name of form element. Required. No more than 300 characters. label: Label displayed to user. Required. No more than 48 characters. optional: Provide true when the form element is not required. By default, form elements are required. value: Provide a default selected value. placeholder: A string displayed as needed to help guide users in completing the element. 150 character maximum. """ self._elements.append( DialogUserSelector( name=name, label=label, optional=optional, value=value, placeholder=placeholder, ) ) return self ``` Now you can easily populate a select menu with a list of users. For example, when you are creating a bug tracking app, you want to include a field for an assignee. Slack pre-populates the user list in client-side, so your app doesn't need access to a related OAuth scope. [https://docs.slack.dev/legacy/legacy-dialogs/#dynamic\_select\_elements\_users](https://docs.slack.dev/legacy/legacy-dialogs/#dynamic_select_elements_users) ## Args **`name`** Name of form element. Required. No more than 300 characters. **`label`** Label displayed to user. Required. No more than 48 characters. **`optional`** Provide true when the form element is not required. By default, form elements are required. **`value`** Provide a default selected value. **`placeholder`** A string displayed as needed to help guide users in completing the element. 150 character maximum. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class DialogChannelSelector (*, name: str, label: str, optional: bool = False, value: str | None = None, placeholder: str | None = None)` Expand source code ``` class DialogChannelSelector(AbstractDialogSelector): data_source = "channels" def __init__( self, *, name: str, label: str, optional: bool = False, value: Optional[str] = None, placeholder: Optional[str] = None, ): """ You can also provide a select menu with a list of channels. Specify your data_source as channels to limit only to public channels https://docs.slack.dev/legacy/legacy-dialogs/#dynamic_select_elements_channels_conversations Args: name: Name of form element. Required. No more than 300 characters. label: Label displayed to user. Required. No more than 48 characters. optional: Provide true when the form element is not required. By default, form elements are required. value: Provide a default selected value. placeholder: A string displayed as needed to help guide users in completing the element. 150 character maximum. """ super().__init__( name=name, label=label, optional=optional, value=value, placeholder=placeholder, ) ``` The base class for JSON serializable class objects You can also provide a select menu with a list of channels. Specify your data\_source as channels to limit only to public channels [https://docs.slack.dev/legacy/legacy-dialogs/#dynamic\_select\_elements\_channels\_conversations](https://docs.slack.dev/legacy/legacy-dialogs/#dynamic_select_elements_channels_conversations) ## Args **`name`** Name of form element. Required. No more than 300 characters. **`label`** Label displayed to user. Required. No more than 48 characters. **`optional`** Provide true when the form element is not required. By default, form elements are required. **`value`** Provide a default selected value. **`placeholder`** A string displayed as needed to help guide users in completing the element. 150 character maximum. ### Ancestors * [AbstractDialogSelector](#slack_sdk.models.dialogs.AbstractDialogSelector "slack_sdk.models.dialogs.AbstractDialogSelector") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var data_source` The type of the None singleton. ### Inherited members * `**[AbstractDialogSelector](#slack_sdk.models.dialogs.AbstractDialogSelector "slack_sdk.models.dialogs.AbstractDialogSelector")**`: * `[DataSourceTypes](#slack_sdk.models.dialogs.AbstractDialogSelector.DataSourceTypes "slack_sdk.models.dialogs.AbstractDialogSelector.DataSourceTypes")` * `[attributes](#slack_sdk.models.dialogs.AbstractDialogSelector.attributes "slack_sdk.models.dialogs.AbstractDialogSelector.attributes")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.dialogs.AbstractDialogSelector.get_non_null_attributes")` * `[label_max_length](#slack_sdk.models.dialogs.AbstractDialogSelector.label_max_length "slack_sdk.models.dialogs.AbstractDialogSelector.label_max_length")` * `[name_max_length](#slack_sdk.models.dialogs.AbstractDialogSelector.name_max_length "slack_sdk.models.dialogs.AbstractDialogSelector.name_max_length")` * `[placeholder_max_length](#slack_sdk.models.dialogs.AbstractDialogSelector.placeholder_max_length "slack_sdk.models.dialogs.AbstractDialogSelector.placeholder_max_length")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.dialogs.AbstractDialogSelector.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.dialogs.AbstractDialogSelector.validate_json")` `class DialogConversationSelector (*, name: str, label: str, optional: bool = False, value: str | None = None, placeholder: str | None = None)` Expand source code ``` class DialogConversationSelector(AbstractDialogSelector): data_source = "conversations" def __init__( self, *, name: str, label: str, optional: bool = False, value: Optional[str] = None, placeholder: Optional[str] = None, ): """ You can also provide a select menu with a list of conversations - including private channels, direct messages, MPIMs, and whatever else we consider a conversation-like thing. https://docs.slack.dev/legacy/legacy-dialogs/#dynamic_select_elements_channels_conversations Args: name: Name of form element. Required. No more than 300 characters. label: Label displayed to user. Required. No more than 48 characters. optional: Provide true when the form element is not required. By default, form elements are required. value: Provide a default selected value. placeholder: A string displayed as needed to help guide users in completing the element. 150 character maximum. """ super().__init__( name=name, label=label, optional=optional, value=value, placeholder=placeholder, ) ``` The base class for JSON serializable class objects You can also provide a select menu with a list of conversations - including private channels, direct messages, MPIMs, and whatever else we consider a conversation-like thing. [https://docs.slack.dev/legacy/legacy-dialogs/#dynamic\_select\_elements\_channels\_conversations](https://docs.slack.dev/legacy/legacy-dialogs/#dynamic_select_elements_channels_conversations) ## Args **`name`** Name of form element. Required. No more than 300 characters. **`label`** Label displayed to user. Required. No more than 48 characters. **`optional`** Provide true when the form element is not required. By default, form elements are required. **`value`** Provide a default selected value. **`placeholder`** A string displayed as needed to help guide users in completing the element. 150 character maximum. ### Ancestors * [AbstractDialogSelector](#slack_sdk.models.dialogs.AbstractDialogSelector "slack_sdk.models.dialogs.AbstractDialogSelector") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var data_source` The type of the None singleton. ### Inherited members * `**[AbstractDialogSelector](#slack_sdk.models.dialogs.AbstractDialogSelector "slack_sdk.models.dialogs.AbstractDialogSelector")**`: * `[DataSourceTypes](#slack_sdk.models.dialogs.AbstractDialogSelector.DataSourceTypes "slack_sdk.models.dialogs.AbstractDialogSelector.DataSourceTypes")` * `[attributes](#slack_sdk.models.dialogs.AbstractDialogSelector.attributes "slack_sdk.models.dialogs.AbstractDialogSelector.attributes")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.dialogs.AbstractDialogSelector.get_non_null_attributes")` * `[label_max_length](#slack_sdk.models.dialogs.AbstractDialogSelector.label_max_length "slack_sdk.models.dialogs.AbstractDialogSelector.label_max_length")` * `[name_max_length](#slack_sdk.models.dialogs.AbstractDialogSelector.name_max_length "slack_sdk.models.dialogs.AbstractDialogSelector.name_max_length")` * `[placeholder_max_length](#slack_sdk.models.dialogs.AbstractDialogSelector.placeholder_max_length "slack_sdk.models.dialogs.AbstractDialogSelector.placeholder_max_length")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.dialogs.AbstractDialogSelector.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.dialogs.AbstractDialogSelector.validate_json")` `class DialogExternalSelector (*, name: str, label: str, value: [Option](../blocks/basic_components.html#slack_sdk.models.blocks.basic_components.Option "slack_sdk.models.blocks.basic_components.Option") | None = None, min_query_length: int | None = None, optional: bool | None = False, placeholder: str | None = None)` Expand source code ``` class DialogExternalSelector(AbstractDialogSelector): data_source = "external" @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"min_query_length"}) def __init__( self, *, name: str, label: str, value: Optional[Option] = None, min_query_length: Optional[int] = None, optional: Optional[bool] = False, placeholder: Optional[str] = None, ): """ Use the select element for multiple choice selections allowing users to pick a single item from a list. True to web roots, this selection is displayed as a dropdown menu. A list of options can be loaded from an external URL and used in your dialog menus. https://docs.slack.dev/legacy/legacy-dialogs/#dynamic_select_elements_external Args: name: Name of form element. Required. No more than 300 characters. label: Label displayed to user. Required. No more than 48 characters. min_query_length: Specify the number of characters that must be typed by a user into a dynamic select menu before dispatching to the app. optional: Provide true when the form element is not required. By default, form elements are required. value: Provide a default selected value. This should be a single Option or OptionGroup that exactly matches one that will be returned from your external endpoint. placeholder: A string displayed as needed to help guide users in completing the element. 150 character maximum. """ super().__init__( name=name, label=label, value=value, optional=optional, # type: ignore[arg-type] placeholder=placeholder, ) self.min_query_length = min_query_length ``` The base class for JSON serializable class objects Use the select element for multiple choice selections allowing users to pick a single item from a list. True to web roots, this selection is displayed as a dropdown menu. A list of options can be loaded from an external URL and used in your dialog menus. [https://docs.slack.dev/legacy/legacy-dialogs/#dynamic\_select\_elements\_external](https://docs.slack.dev/legacy/legacy-dialogs/#dynamic_select_elements_external) ## Args **`name`** Name of form element. Required. No more than 300 characters. **`label`** Label displayed to user. Required. No more than 48 characters. **`min_query_length`** Specify the number of characters that must be typed by a user into a dynamic select menu before dispatching to the app. **`optional`** Provide true when the form element is not required. By default, form elements are required. **`value`** Provide a default selected value. This should be a single Option or OptionGroup that exactly matches one that will be returned from your external endpoint. **`placeholder`** A string displayed as needed to help guide users in completing the element. 150 character maximum. ### Ancestors * [AbstractDialogSelector](#slack_sdk.models.dialogs.AbstractDialogSelector "slack_sdk.models.dialogs.AbstractDialogSelector") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var data_source` The type of the None singleton. ### Instance variables `prop attributes : Set[str]` Expand source code ``` @property def attributes(self) -> Set[str]: # type: ignore[override] return super().attributes.union({"min_query_length"}) ``` Build an unordered collection of unique elements. ### Inherited members * `**[AbstractDialogSelector](#slack_sdk.models.dialogs.AbstractDialogSelector "slack_sdk.models.dialogs.AbstractDialogSelector")**`: * `[DataSourceTypes](#slack_sdk.models.dialogs.AbstractDialogSelector.DataSourceTypes "slack_sdk.models.dialogs.AbstractDialogSelector.DataSourceTypes")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.dialogs.AbstractDialogSelector.get_non_null_attributes")` * `[label_max_length](#slack_sdk.models.dialogs.AbstractDialogSelector.label_max_length "slack_sdk.models.dialogs.AbstractDialogSelector.label_max_length")` * `[name_max_length](#slack_sdk.models.dialogs.AbstractDialogSelector.name_max_length "slack_sdk.models.dialogs.AbstractDialogSelector.name_max_length")` * `[placeholder_max_length](#slack_sdk.models.dialogs.AbstractDialogSelector.placeholder_max_length "slack_sdk.models.dialogs.AbstractDialogSelector.placeholder_max_length")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.dialogs.AbstractDialogSelector.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.dialogs.AbstractDialogSelector.validate_json")` `class DialogStaticSelector (*, name: str, label: str, options: Sequence[[Option](../blocks/basic_components.html#slack_sdk.models.blocks.basic_components.Option "slack_sdk.models.blocks.basic_components.Option")] | Sequence[[OptionGroup](../blocks/basic_components.html#slack_sdk.models.blocks.basic_components.OptionGroup "slack_sdk.models.blocks.basic_components.OptionGroup")], optional: bool = False, value: [Option](../blocks/basic_components.html#slack_sdk.models.blocks.basic_components.Option "slack_sdk.models.blocks.basic_components.Option") | str | None = None, placeholder: str | None = None)` Expand source code ``` class DialogStaticSelector(AbstractDialogSelector): """ Use the select element for multiple choice selections allowing users to pick a single item from a list. True to web roots, this selection is displayed as a dropdown menu. https://docs.slack.dev/legacy/legacy-dialogs/#select_elements """ data_source = "static" options_max_length = 100 def __init__( self, *, name: str, label: str, options: Union[Sequence[Option], Sequence[OptionGroup]], optional: bool = False, value: Optional[Union[Option, str]] = None, placeholder: Optional[str] = None, ): """ Use the select element for multiple choice selections allowing users to pick a single item from a list. True to web roots, this selection is displayed as a dropdown menu. A select element may contain up to 100 selections, provided as a list of Option or OptionGroup objects https://docs.slack.dev/legacy/legacy-dialogs/#attributes_select_elements Args: name: Name of form element. Required. No more than 300 characters. label: Label displayed to user. Required. No more than 48 characters. options: A list of up to 100 Option or OptionGroup objects. Object types cannot be mixed. optional: Provide true when the form element is not required. By default, form elements are required. value: Provide a default selected value. placeholder: A string displayed as needed to help guide users in completing the element. 150 character maximum. """ super().__init__( name=name, label=label, optional=optional, value=value, placeholder=placeholder, ) self.options = options @JsonValidator(f"options attribute cannot exceed {options_max_length} items") def options_length(self) -> bool: return len(self.options) < self.options_max_length def to_dict(self) -> dict: json = super().to_dict() if isinstance(self.options[0], OptionGroup): json["option_groups"] = extract_json(self.options, "dialog") else: json["options"] = extract_json(self.options, "dialog") return json ``` Use the select element for multiple choice selections allowing users to pick a single item from a list. True to web roots, this selection is displayed as a dropdown menu. [https://docs.slack.dev/legacy/legacy-dialogs/#select\_elements](https://docs.slack.dev/legacy/legacy-dialogs/#select_elements) Use the select element for multiple choice selections allowing users to pick a single item from a list. True to web roots, this selection is displayed as a dropdown menu. A select element may contain up to 100 selections, provided as a list of Option or OptionGroup objects [https://docs.slack.dev/legacy/legacy-dialogs/#attributes\_select\_elements](https://docs.slack.dev/legacy/legacy-dialogs/#attributes_select_elements) ## Args **`name`** Name of form element. Required. No more than 300 characters. **`label`** Label displayed to user. Required. No more than 48 characters. **`options`** A list of up to 100 Option or OptionGroup objects. Object types cannot be mixed. **`optional`** Provide true when the form element is not required. By default, form elements are required. **`value`** Provide a default selected value. **`placeholder`** A string displayed as needed to help guide users in completing the element. 150 character maximum. ### Ancestors * [AbstractDialogSelector](#slack_sdk.models.dialogs.AbstractDialogSelector "slack_sdk.models.dialogs.AbstractDialogSelector") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var data_source` The type of the None singleton. `var options_max_length` The type of the None singleton. ### Methods `def options_length(self) ‑> bool` Expand source code ``` @JsonValidator(f"options attribute cannot exceed {options_max_length} items") def options_length(self) -> bool: return len(self.options) < self.options_max_length ``` ### Inherited members * `**[AbstractDialogSelector](#slack_sdk.models.dialogs.AbstractDialogSelector "slack_sdk.models.dialogs.AbstractDialogSelector")**`: * `[DataSourceTypes](#slack_sdk.models.dialogs.AbstractDialogSelector.DataSourceTypes "slack_sdk.models.dialogs.AbstractDialogSelector.DataSourceTypes")` * `[attributes](#slack_sdk.models.dialogs.AbstractDialogSelector.attributes "slack_sdk.models.dialogs.AbstractDialogSelector.attributes")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.dialogs.AbstractDialogSelector.get_non_null_attributes")` * `[label_max_length](#slack_sdk.models.dialogs.AbstractDialogSelector.label_max_length "slack_sdk.models.dialogs.AbstractDialogSelector.label_max_length")` * `[name_max_length](#slack_sdk.models.dialogs.AbstractDialogSelector.name_max_length "slack_sdk.models.dialogs.AbstractDialogSelector.name_max_length")` * `[placeholder_max_length](#slack_sdk.models.dialogs.AbstractDialogSelector.placeholder_max_length "slack_sdk.models.dialogs.AbstractDialogSelector.placeholder_max_length")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.dialogs.AbstractDialogSelector.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.dialogs.AbstractDialogSelector.validate_json")` `class DialogTextArea (*, name: str, label: str, optional: bool = False, placeholder: str | None = None, hint: str | None = None, value: str | None = None, min_length: int = 0, max_length: int | None = None, subtype: str | None = None)` Expand source code ``` class DialogTextArea(DialogTextComponent): """ A textarea is a multi-line plain text editing control. You've likely encountered these on the world wide web. Use this element if you want a relatively long answer from users. The element UI provides a remaining character count to the max_length you have set or the default, 3000. https://docs.slack.dev/legacy/legacy-dialogs/#textarea_elements """ type = "textarea" max_value_length = 3000 ``` A textarea is a multi-line plain text editing control. You've likely encountered these on the world wide web. Use this element if you want a relatively long answer from users. The element UI provides a remaining character count to the max\_length you have set or the default, 3000. [https://docs.slack.dev/legacy/legacy-dialogs/#textarea\_elements](https://docs.slack.dev/legacy/legacy-dialogs/#textarea_elements) ### Ancestors * [DialogTextComponent](#slack_sdk.models.dialogs.DialogTextComponent "slack_sdk.models.dialogs.DialogTextComponent") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var max_value_length` The type of the None singleton. `var type` The type of the None singleton. ### Inherited members * `**[DialogTextComponent](#slack_sdk.models.dialogs.DialogTextComponent "slack_sdk.models.dialogs.DialogTextComponent")**`: * `[attributes](#slack_sdk.models.dialogs.DialogTextComponent.attributes "slack_sdk.models.dialogs.DialogTextComponent.attributes")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.dialogs.DialogTextComponent.get_non_null_attributes")` * `[hint_max_length](#slack_sdk.models.dialogs.DialogTextComponent.hint_max_length "slack_sdk.models.dialogs.DialogTextComponent.hint_max_length")` * `[label_max_length](#slack_sdk.models.dialogs.DialogTextComponent.label_max_length "slack_sdk.models.dialogs.DialogTextComponent.label_max_length")` * `[name_max_length](#slack_sdk.models.dialogs.DialogTextComponent.name_max_length "slack_sdk.models.dialogs.DialogTextComponent.name_max_length")` * `[placeholder_max_length](#slack_sdk.models.dialogs.DialogTextComponent.placeholder_max_length "slack_sdk.models.dialogs.DialogTextComponent.placeholder_max_length")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.dialogs.DialogTextComponent.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.dialogs.DialogTextComponent.validate_json")` `class DialogTextComponent (*, name: str, label: str, optional: bool = False, placeholder: str | None = None, hint: str | None = None, value: str | None = None, min_length: int = 0, max_length: int | None = None, subtype: str | None = None)` Expand source code ``` class DialogTextComponent(JsonObject, metaclass=ABCMeta): attributes = { "hint", "label", "max_length", "min_length", "name", "optional", "placeholder", "subtype", "type", "value", } name_max_length = 300 label_max_length = 48 placeholder_max_length = 150 hint_max_length = 150 @property @abstractmethod def type(self): pass @property @abstractmethod def max_value_length(self): pass def __init__( self, *, name: str, label: str, optional: bool = False, placeholder: Optional[str] = None, hint: Optional[str] = None, value: Optional[str] = None, min_length: int = 0, max_length: Optional[int] = None, subtype: Optional[str] = None, ): self.name = name self.label = label self.optional = optional self.placeholder = placeholder self.hint = hint self.value = value self.min_length = min_length self.max_length = max_length or self.max_value_length self.subtype = subtype @JsonValidator(f"name attribute cannot exceed {name_max_length} characters") def name_length(self) -> bool: return len(self.name) < self.name_max_length @JsonValidator(f"label attribute cannot exceed {label_max_length} characters") def label_length(self) -> bool: return len(self.label) < self.label_max_length @JsonValidator(f"placeholder attribute cannot exceed {placeholder_max_length} characters") def placeholder_length(self) -> bool: return self.placeholder is None or len(self.placeholder) < self.placeholder_max_length @JsonValidator(f"hint attribute cannot exceed {hint_max_length} characters") def hint_length(self) -> bool: return self.hint is None or len(self.hint) < self.hint_max_length @JsonValidator("value attribute exceeded bounds") def value_length(self) -> bool: return self.value is None or len(self.value) < self.max_value_length @JsonValidator("min_length attribute must be greater than or equal to 0") def min_length_above_zero(self) -> bool: return self.min_length is None or self.min_length >= 0 @JsonValidator("min_length attribute exceed bounds") def min_length_length(self) -> bool: return self.min_length is None or self.min_length <= self.max_value_length @JsonValidator("min_length attribute must be less than max value attribute") def min_length_below_max_length(self) -> bool: return self.min_length is None or self.min_length < self.max_length @JsonValidator("max_length attribute must be greater than or equal to 0") def max_length_above_zero(self) -> bool: return self.max_length is None or self.max_length > 0 @JsonValidator("max_length attribute exceeded bounds") def max_length_length(self) -> bool: return self.max_length is None or self.max_length <= self.max_value_length @EnumValidator("subtype", TextElementSubtypes) def subtype_valid(self) -> bool: return self.subtype is None or self.subtype in TextElementSubtypes ``` The base class for JSON serializable class objects ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Subclasses * [DialogTextArea](#slack_sdk.models.dialogs.DialogTextArea "slack_sdk.models.dialogs.DialogTextArea") * [DialogTextField](#slack_sdk.models.dialogs.DialogTextField "slack_sdk.models.dialogs.DialogTextField") ### Class variables `var attributes` The type of the None singleton. `var hint_max_length` The type of the None singleton. `var label_max_length` The type of the None singleton. `var name_max_length` The type of the None singleton. `var placeholder_max_length` The type of the None singleton. ### Instance variables `prop max_value_length` Expand source code ``` @property @abstractmethod def max_value_length(self): pass ``` `prop type` Expand source code ``` @property @abstractmethod def type(self): pass ``` ### Methods `def hint_length(self) ‑> bool` Expand source code ``` @JsonValidator(f"hint attribute cannot exceed {hint_max_length} characters") def hint_length(self) -> bool: return self.hint is None or len(self.hint) < self.hint_max_length ``` `def label_length(self) ‑> bool` Expand source code ``` @JsonValidator(f"label attribute cannot exceed {label_max_length} characters") def label_length(self) -> bool: return len(self.label) < self.label_max_length ``` `def max_length_above_zero(self) ‑> bool` Expand source code ``` @JsonValidator("max_length attribute must be greater than or equal to 0") def max_length_above_zero(self) -> bool: return self.max_length is None or self.max_length > 0 ``` `def max_length_length(self) ‑> bool` Expand source code ``` @JsonValidator("max_length attribute exceeded bounds") def max_length_length(self) -> bool: return self.max_length is None or self.max_length <= self.max_value_length ``` `def min_length_above_zero(self) ‑> bool` Expand source code ``` @JsonValidator("min_length attribute must be greater than or equal to 0") def min_length_above_zero(self) -> bool: return self.min_length is None or self.min_length >= 0 ``` `def min_length_below_max_length(self) ‑> bool` Expand source code ``` @JsonValidator("min_length attribute must be less than max value attribute") def min_length_below_max_length(self) -> bool: return self.min_length is None or self.min_length < self.max_length ``` `def min_length_length(self) ‑> bool` Expand source code ``` @JsonValidator("min_length attribute exceed bounds") def min_length_length(self) -> bool: return self.min_length is None or self.min_length <= self.max_value_length ``` `def name_length(self) ‑> bool` Expand source code ``` @JsonValidator(f"name attribute cannot exceed {name_max_length} characters") def name_length(self) -> bool: return len(self.name) < self.name_max_length ``` `def placeholder_length(self) ‑> bool` Expand source code ``` @JsonValidator(f"placeholder attribute cannot exceed {placeholder_max_length} characters") def placeholder_length(self) -> bool: return self.placeholder is None or len(self.placeholder) < self.placeholder_max_length ``` `def subtype_valid(self) ‑> bool` Expand source code ``` @EnumValidator("subtype", TextElementSubtypes) def subtype_valid(self) -> bool: return self.subtype is None or self.subtype in TextElementSubtypes ``` `def value_length(self) ‑> bool` Expand source code ``` @JsonValidator("value attribute exceeded bounds") def value_length(self) -> bool: return self.value is None or len(self.value) < self.max_value_length ``` ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class DialogTextField (*, name: str, label: str, optional: bool = False, placeholder: str | None = None, hint: str | None = None, value: str | None = None, min_length: int = 0, max_length: int | None = None, subtype: str | None = None)` Expand source code ``` class DialogTextField(DialogTextComponent): """ Text elements are single-line plain text fields. https://docs.slack.dev/legacy/legacy-dialogs/#text_elements """ type = "text" max_value_length = 150 ``` Text elements are single-line plain text fields. [https://docs.slack.dev/legacy/legacy-dialogs/#text\_elements](https://docs.slack.dev/legacy/legacy-dialogs/#text_elements) ### Ancestors * [DialogTextComponent](#slack_sdk.models.dialogs.DialogTextComponent "slack_sdk.models.dialogs.DialogTextComponent") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var max_value_length` The type of the None singleton. `var type` The type of the None singleton. ### Inherited members * `**[DialogTextComponent](#slack_sdk.models.dialogs.DialogTextComponent "slack_sdk.models.dialogs.DialogTextComponent")**`: * `[attributes](#slack_sdk.models.dialogs.DialogTextComponent.attributes "slack_sdk.models.dialogs.DialogTextComponent.attributes")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.dialogs.DialogTextComponent.get_non_null_attributes")` * `[hint_max_length](#slack_sdk.models.dialogs.DialogTextComponent.hint_max_length "slack_sdk.models.dialogs.DialogTextComponent.hint_max_length")` * `[label_max_length](#slack_sdk.models.dialogs.DialogTextComponent.label_max_length "slack_sdk.models.dialogs.DialogTextComponent.label_max_length")` * `[name_max_length](#slack_sdk.models.dialogs.DialogTextComponent.name_max_length "slack_sdk.models.dialogs.DialogTextComponent.name_max_length")` * `[placeholder_max_length](#slack_sdk.models.dialogs.DialogTextComponent.placeholder_max_length "slack_sdk.models.dialogs.DialogTextComponent.placeholder_max_length")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.dialogs.DialogTextComponent.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.dialogs.DialogTextComponent.validate_json")` `class DialogUserSelector (*, name: str, label: str, optional: bool = False, value: str | None = None, placeholder: str | None = None)` Expand source code ``` class DialogUserSelector(AbstractDialogSelector): data_source = "users" def __init__( self, *, name: str, label: str, optional: bool = False, value: Optional[str] = None, placeholder: Optional[str] = None, ): """ Now you can easily populate a select menu with a list of users. For example, when you are creating a bug tracking app, you want to include a field for an assignee. Slack pre-populates the user list in client-side, so your app doesn't need access to a related OAuth scope. https://docs.slack.dev/legacy/legacy-dialogs/#dynamic_select_elements_users Args: name: Name of form element. Required. No more than 300 characters. label: Label displayed to user. Required. No more than 48 characters. optional: Provide true when the form element is not required. By default, form elements are required. value: Provide a default selected value. placeholder: A string displayed as needed to help guide users in completing the element. 150 character maximum. """ super().__init__( name=name, label=label, optional=optional, value=value, placeholder=placeholder, ) ``` The base class for JSON serializable class objects Now you can easily populate a select menu with a list of users. For example, when you are creating a bug tracking app, you want to include a field for an assignee. Slack pre-populates the user list in client-side, so your app doesn't need access to a related OAuth scope. [https://docs.slack.dev/legacy/legacy-dialogs/#dynamic\_select\_elements\_users](https://docs.slack.dev/legacy/legacy-dialogs/#dynamic_select_elements_users) ## Args **`name`** Name of form element. Required. No more than 300 characters. **`label`** Label displayed to user. Required. No more than 48 characters. **`optional`** Provide true when the form element is not required. By default, form elements are required. **`value`** Provide a default selected value. **`placeholder`** A string displayed as needed to help guide users in completing the element. 150 character maximum. ### Ancestors * [AbstractDialogSelector](#slack_sdk.models.dialogs.AbstractDialogSelector "slack_sdk.models.dialogs.AbstractDialogSelector") * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var data_source` The type of the None singleton. ### Inherited members * `**[AbstractDialogSelector](#slack_sdk.models.dialogs.AbstractDialogSelector "slack_sdk.models.dialogs.AbstractDialogSelector")**`: * `[DataSourceTypes](#slack_sdk.models.dialogs.AbstractDialogSelector.DataSourceTypes "slack_sdk.models.dialogs.AbstractDialogSelector.DataSourceTypes")` * `[attributes](#slack_sdk.models.dialogs.AbstractDialogSelector.attributes "slack_sdk.models.dialogs.AbstractDialogSelector.attributes")` * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.dialogs.AbstractDialogSelector.get_non_null_attributes")` * `[label_max_length](#slack_sdk.models.dialogs.AbstractDialogSelector.label_max_length "slack_sdk.models.dialogs.AbstractDialogSelector.label_max_length")` * `[name_max_length](#slack_sdk.models.dialogs.AbstractDialogSelector.name_max_length "slack_sdk.models.dialogs.AbstractDialogSelector.name_max_length")` * `[placeholder_max_length](#slack_sdk.models.dialogs.AbstractDialogSelector.placeholder_max_length "slack_sdk.models.dialogs.AbstractDialogSelector.placeholder_max_length")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.dialogs.AbstractDialogSelector.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.dialogs.AbstractDialogSelector.validate_json")` --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/models/messages # Module slack_sdk.models.messages ## Sub-modules `[slack_sdk.models.messages.chunk](chunk.html "slack_sdk.models.messages.chunk")` `[slack_sdk.models.messages.message](message.html "slack_sdk.models.messages.message")` ## Classes `class ChannelLink` Expand source code ``` class ChannelLink(Link): def __init__(self): """Represents an @channel link, which notifies everyone present in this channel. https://docs.slack.dev/messaging/formatting-message-text/ """ super().__init__(url="!channel", text="channel") ``` The base class for all model objects in this module Represents an @channel link, which notifies everyone present in this channel. [https://docs.slack.dev/messaging/formatting-message-text/](https://docs.slack.dev/messaging/formatting-message-text/) ### Ancestors * [Link](#slack_sdk.models.messages.Link "slack_sdk.models.messages.Link") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") `class DateLink (*, date: datetime.datetime | int, date_format: str, fallback: str, link: str | None = None)` Expand source code ``` class DateLink(Link): def __init__( self, *, date: Union[datetime, int], date_format: str, fallback: str, link: Optional[str] = None, ): """Text containing a date or time should display that date in the local timezone of the person seeing the text. https://docs.slack.dev/messaging/formatting-message-text/#date-formatting """ if isinstance(date, datetime): epoch = int(date.timestamp()) else: epoch = date if link is not None: link = f"^{link}" else: link = "" super().__init__(url=f"!date^{epoch}^{date_format}{link}", text=fallback) ``` The base class for all model objects in this module Text containing a date or time should display that date in the local timezone of the person seeing the text. [https://docs.slack.dev/messaging/formatting-message-text/#date-formatting](https://docs.slack.dev/messaging/formatting-message-text/#date-formatting) ### Ancestors * [Link](#slack_sdk.models.messages.Link "slack_sdk.models.messages.Link") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") `class EveryoneLink` Expand source code ``` class EveryoneLink(Link): def __init__(self): """Represents an @everyone link, which notifies all users of this workspace. https://docs.slack.dev/messaging/formatting-message-text/ """ super().__init__(url="!everyone", text="everyone") ``` The base class for all model objects in this module Represents an @everyone link, which notifies all users of this workspace. [https://docs.slack.dev/messaging/formatting-message-text/](https://docs.slack.dev/messaging/formatting-message-text/) ### Ancestors * [Link](#slack_sdk.models.messages.Link "slack_sdk.models.messages.Link") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") `class HereLink` Expand source code ``` class HereLink(Link): def __init__(self): """Represents an @here link, which notifies all online users of this channel. https://docs.slack.dev/messaging/formatting-message-text/ """ super().__init__(url="!here", text="here") ``` The base class for all model objects in this module Represents an @here link, which notifies all online users of this channel. [https://docs.slack.dev/messaging/formatting-message-text/](https://docs.slack.dev/messaging/formatting-message-text/) ### Ancestors * [Link](#slack_sdk.models.messages.Link "slack_sdk.models.messages.Link") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") `class Link (*, url: str, text: str)` Expand source code ``` class Link(BaseObject): def __init__(self, *, url: str, text: str): """Base class used to generate links in Slack's not-quite Markdown, not quite HTML syntax https://docs.slack.dev/messaging/formatting-message-text/#linking_to_urls """ self.url = url self.text = text def __str__(self): if self.text: separator = "|" else: separator = "" return f"<{self.url}{separator}{self.text}>" ``` The base class for all model objects in this module Base class used to generate links in Slack's not-quite Markdown, not quite HTML syntax [https://docs.slack.dev/messaging/formatting-message-text/#linking\_to\_urls](https://docs.slack.dev/messaging/formatting-message-text/#linking_to_urls) ### Ancestors * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Subclasses * [ChannelLink](#slack_sdk.models.messages.ChannelLink "slack_sdk.models.messages.ChannelLink") * [DateLink](#slack_sdk.models.messages.DateLink "slack_sdk.models.messages.DateLink") * [EveryoneLink](#slack_sdk.models.messages.EveryoneLink "slack_sdk.models.messages.EveryoneLink") * [HereLink](#slack_sdk.models.messages.HereLink "slack_sdk.models.messages.HereLink") * [ObjectLink](#slack_sdk.models.messages.ObjectLink "slack_sdk.models.messages.ObjectLink") `class ObjectLink (*, object_id: str, text: str = '')` Expand source code ``` class ObjectLink(Link): prefix_mapping = { "C": "#", # channel "G": "#", # group message "U": "@", # user "W": "@", # workspace user (enterprise) "B": "@", # bot user "S": "!subteam^", # user groups, originally known as subteams } def __init__(self, *, object_id: str, text: str = ""): """Convenience class to create links to specific object types https://docs.slack.dev/messaging/formatting-message-text/#linking-channels """ prefix = self.prefix_mapping.get(object_id[0].upper(), "@") super().__init__(url=f"{prefix}{object_id}", text=text) ``` The base class for all model objects in this module Convenience class to create links to specific object types [https://docs.slack.dev/messaging/formatting-message-text/#linking-channels](https://docs.slack.dev/messaging/formatting-message-text/#linking-channels) ### Ancestors * [Link](#slack_sdk.models.messages.Link "slack_sdk.models.messages.Link") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var prefix_mapping` The type of the None singleton. --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/models/metadata # Module slack_sdk.models.metadata ## Global variables `var EntityType` Custom field types ## Classes `class ContentItemEntityFields (preview: Dict[str, Any] | [EntityImageField](#slack_sdk.models.metadata.EntityImageField "slack_sdk.models.metadata.EntityImageField") | None = None, description: Dict[str, Any] | [EntityStringField](#slack_sdk.models.metadata.EntityStringField "slack_sdk.models.metadata.EntityStringField") | None = None, created_by: Dict[str, Any] | [EntityTypedField](#slack_sdk.models.metadata.EntityTypedField "slack_sdk.models.metadata.EntityTypedField") | None = None, date_created: Dict[str, Any] | [EntityTimestampField](#slack_sdk.models.metadata.EntityTimestampField "slack_sdk.models.metadata.EntityTimestampField") | None = None, date_updated: Dict[str, Any] | [EntityTimestampField](#slack_sdk.models.metadata.EntityTimestampField "slack_sdk.models.metadata.EntityTimestampField") | None = None, last_modified_by: Dict[str, Any] | [EntityTypedField](#slack_sdk.models.metadata.EntityTypedField "slack_sdk.models.metadata.EntityTypedField") | None = None, **kwargs)` Expand source code ``` class ContentItemEntityFields(JsonObject): """Fields specific to content item entities""" attributes = { "preview", "description", "created_by", "date_created", "date_updated", "last_modified_by", } def __init__( self, preview: Optional[Union[Dict[str, Any], EntityImageField]] = None, description: Optional[Union[Dict[str, Any], EntityStringField]] = None, created_by: Optional[Union[Dict[str, Any], EntityTypedField]] = None, date_created: Optional[Union[Dict[str, Any], EntityTimestampField]] = None, date_updated: Optional[Union[Dict[str, Any], EntityTimestampField]] = None, last_modified_by: Optional[Union[Dict[str, Any], EntityTypedField]] = None, **kwargs, ): self.preview = preview self.description = description self.created_by = created_by self.date_created = date_created self.date_updated = date_updated self.last_modified_by = last_modified_by self.additional_attributes = kwargs def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() ``` Fields specific to content item entities ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class EntityActionButton (text: str, action_id: str, value: str | None = None, style: str | None = None, url: str | None = None, accessibility_label: str | None = None, processing_state: Dict[str, Any] | [EntityActionProcessingState](#slack_sdk.models.metadata.EntityActionProcessingState "slack_sdk.models.metadata.EntityActionProcessingState") | None = None, **kwargs)` Expand source code ``` class EntityActionButton(JsonObject): """Action button for entity""" attributes = { "text", "action_id", "value", "style", "url", "accessibility_label", "processing_state", } def __init__( self, text: str, action_id: str, value: Optional[str] = None, style: Optional[str] = None, url: Optional[str] = None, accessibility_label: Optional[str] = None, processing_state: Optional[Union[Dict[str, Any], EntityActionProcessingState]] = None, **kwargs, ): self.text = text self.action_id = action_id self.value = value self.style = style self.url = url self.accessibility_label = accessibility_label self.processing_state = processing_state self.additional_attributes = kwargs def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() ``` Action button for entity ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class EntityActionProcessingState (enabled: bool, interstitial_text: str | None = None, **kwargs)` Expand source code ``` class EntityActionProcessingState(JsonObject): """Processing state configuration for entity action button""" attributes = { "enabled", "interstitial_text", } def __init__( self, enabled: bool, interstitial_text: Optional[str] = None, **kwargs, ): self.enabled = enabled self.interstitial_text = interstitial_text self.additional_attributes = kwargs def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() ``` Processing state configuration for entity action button ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class EntityActions (primary_actions: List[Dict[str, Any] | [EntityActionButton](#slack_sdk.models.metadata.EntityActionButton "slack_sdk.models.metadata.EntityActionButton")] | None = None, overflow_actions: List[Dict[str, Any] | [EntityActionButton](#slack_sdk.models.metadata.EntityActionButton "slack_sdk.models.metadata.EntityActionButton")] | None = None, **kwargs)` Expand source code ``` class EntityActions(JsonObject): """Actions configuration for entity""" attributes = { "primary_actions", "overflow_actions", } def __init__( self, primary_actions: Optional[List[Union[Dict[str, Any], EntityActionButton]]] = None, overflow_actions: Optional[List[Union[Dict[str, Any], EntityActionButton]]] = None, **kwargs, ): self.primary_actions = primary_actions self.overflow_actions = overflow_actions self.additional_attributes = kwargs def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() ``` Actions configuration for entity ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class EntityArrayItemField (type: str | None = None, label: str | None = None, value: str | int | None = None, link: str | None = None, icon: Dict[str, Any] | [EntityIconField](#slack_sdk.models.metadata.EntityIconField "slack_sdk.models.metadata.EntityIconField") | None = None, long: bool | None = None, format: str | None = None, image_url: str | None = None, slack_file: Dict[str, Any] | None = None, alt_text: str | None = None, edit: Dict[str, Any] | [EntityEditSupport](#slack_sdk.models.metadata.EntityEditSupport "slack_sdk.models.metadata.EntityEditSupport") | None = None, tag_color: str | None = None, user: Dict[str, Any] | [EntityUserIDField](#slack_sdk.models.metadata.EntityUserIDField "slack_sdk.models.metadata.EntityUserIDField") | [EntityUserField](#slack_sdk.models.metadata.EntityUserField "slack_sdk.models.metadata.EntityUserField") | None = None, entity_ref: Dict[str, Any] | [EntityRefField](#slack_sdk.models.metadata.EntityRefField "slack_sdk.models.metadata.EntityRefField") | None = None, **kwargs)` Expand source code ``` class EntityArrayItemField(JsonObject): """Array item field for entity (similar to EntityTypedField but with optional type)""" attributes = { "type", "label", "value", "link", "icon", "long", "format", "image_url", "slack_file", "alt_text", "edit", "tag_color", "user", "entity_ref", } def __init__( self, type: Optional[str] = None, label: Optional[str] = None, value: Optional[Union[str, int]] = None, link: Optional[str] = None, icon: Optional[Union[Dict[str, Any], EntityIconField]] = None, long: Optional[bool] = None, format: Optional[str] = None, image_url: Optional[str] = None, slack_file: Optional[Dict[str, Any]] = None, alt_text: Optional[str] = None, edit: Optional[Union[Dict[str, Any], EntityEditSupport]] = None, tag_color: Optional[str] = None, user: Optional[Union[Dict[str, Any], EntityUserIDField, EntityUserField]] = None, entity_ref: Optional[Union[Dict[str, Any], EntityRefField]] = None, **kwargs, ): self.type = type self.label = label self.value = value self.link = link self.icon = icon self.long = long self.format = format self.image_url = image_url self.slack_file = slack_file self.alt_text = alt_text self.edit = edit self.tag_color = tag_color self.user = user self.entity_ref = entity_ref self.additional_attributes = kwargs def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() ``` Array item field for entity (similar to EntityTypedField but with optional type) ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class EntityAttributes (title: Dict[str, Any] | [EntityTitle](#slack_sdk.models.metadata.EntityTitle "slack_sdk.models.metadata.EntityTitle"), display_type: str | None = None, display_id: str | None = None, product_icon: Dict[str, Any] | [EntityIconField](#slack_sdk.models.metadata.EntityIconField "slack_sdk.models.metadata.EntityIconField") | None = None, product_name: str | None = None, locale: str | None = None, full_size_preview: Dict[str, Any] | [EntityFullSizePreview](#slack_sdk.models.metadata.EntityFullSizePreview "slack_sdk.models.metadata.EntityFullSizePreview") | None = None, metadata_last_modified: int | None = None, **kwargs)` Expand source code ``` class EntityAttributes(JsonObject): """Attributes for an entity""" attributes = { "title", "display_type", "display_id", "product_icon", "product_name", "locale", "full_size_preview", "metadata_last_modified", } def __init__( self, title: Union[Dict[str, Any], EntityTitle], display_type: Optional[str] = None, display_id: Optional[str] = None, product_icon: Optional[Union[Dict[str, Any], EntityIconField]] = None, product_name: Optional[str] = None, locale: Optional[str] = None, full_size_preview: Optional[Union[Dict[str, Any], EntityFullSizePreview]] = None, metadata_last_modified: Optional[int] = None, **kwargs, ): self.title = title self.display_type = display_type self.display_id = display_id self.product_icon = product_icon self.product_name = product_name self.locale = locale self.full_size_preview = full_size_preview self.metadata_last_modified = metadata_last_modified self.additional_attributes = kwargs def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() ``` Attributes for an entity ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class EntityBooleanCheckboxField (type: str, text: str, description: str | None, **kwargs)` Expand source code ``` class EntityBooleanCheckboxField(JsonObject): """Boolean checkbox properties""" attributes = {"type", "text", "description"} def __init__( self, type: str, text: str, description: Optional[str], **kwargs, ): self.type = type self.text = text self.description = description self.additional_attributes = kwargs def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() ``` Boolean checkbox properties ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class EntityBooleanTextField (type: str, true_text: str, false_text: str, true_description: str | None, false_description: str | None, **kwargs)` Expand source code ``` class EntityBooleanTextField(JsonObject): """Boolean text properties""" attributes = {"type", "true_text", "false_text", "true_description", "false_description"} def __init__( self, type: str, true_text: str, false_text: str, true_description: Optional[str], false_description: Optional[str], **kwargs, ): self.type = type self.true_text = (true_text,) self.false_text = (false_text,) self.true_description = (true_description,) self.false_description = (false_description,) self.additional_attributes = kwargs def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() ``` Boolean text properties ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class EntityCustomField (label: str, key: str, type: str, value: str | int | List[Dict[str, Any] | [EntityArrayItemField](#slack_sdk.models.metadata.EntityArrayItemField "slack_sdk.models.metadata.EntityArrayItemField")] | None = None, link: str | None = None, icon: Dict[str, Any] | [EntityIconField](#slack_sdk.models.metadata.EntityIconField "slack_sdk.models.metadata.EntityIconField") | None = None, long: bool | None = None, format: str | None = None, image_url: str | None = None, slack_file: Dict[str, Any] | None = None, alt_text: str | None = None, tag_color: str | None = None, edit: Dict[str, Any] | [EntityEditSupport](#slack_sdk.models.metadata.EntityEditSupport "slack_sdk.models.metadata.EntityEditSupport") | None = None, item_type: str | None = None, user: Dict[str, Any] | [EntityUserIDField](#slack_sdk.models.metadata.EntityUserIDField "slack_sdk.models.metadata.EntityUserIDField") | [EntityUserField](#slack_sdk.models.metadata.EntityUserField "slack_sdk.models.metadata.EntityUserField") | None = None, entity_ref: Dict[str, Any] | [EntityRefField](#slack_sdk.models.metadata.EntityRefField "slack_sdk.models.metadata.EntityRefField") | None = None, boolean: Dict[str, Any] | [EntityBooleanCheckboxField](#slack_sdk.models.metadata.EntityBooleanCheckboxField "slack_sdk.models.metadata.EntityBooleanCheckboxField") | [EntityBooleanTextField](#slack_sdk.models.metadata.EntityBooleanTextField "slack_sdk.models.metadata.EntityBooleanTextField") | None = None, **kwargs)` Expand source code ``` class EntityCustomField(JsonObject): """Custom field for entity with flexible types""" attributes = { "label", "key", "type", "value", "link", "icon", "long", "format", "image_url", "slack_file", "alt_text", "tag_color", "edit", "item_type", "user", "entity_ref", "boolean", } def __init__( self, label: str, key: str, type: str, value: Optional[Union[str, int, List[Union[Dict[str, Any], EntityArrayItemField]]]] = None, link: Optional[str] = None, icon: Optional[Union[Dict[str, Any], EntityIconField]] = None, long: Optional[bool] = None, format: Optional[str] = None, image_url: Optional[str] = None, slack_file: Optional[Dict[str, Any]] = None, alt_text: Optional[str] = None, tag_color: Optional[str] = None, edit: Optional[Union[Dict[str, Any], EntityEditSupport]] = None, item_type: Optional[str] = None, user: Optional[Union[Dict[str, Any], EntityUserIDField, EntityUserField]] = None, entity_ref: Optional[Union[Dict[str, Any], EntityRefField]] = None, boolean: Optional[Union[Dict[str, Any], EntityBooleanCheckboxField, EntityBooleanTextField]] = None, **kwargs, ): self.label = label self.key = key self.type = type self.value = value self.link = link self.icon = icon self.long = long self.format = format self.image_url = image_url self.slack_file = slack_file self.alt_text = alt_text self.tag_color = tag_color self.edit = edit self.item_type = item_type self.user = user self.entity_ref = entity_ref self.boolean = boolean self.additional_attributes = kwargs def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() @EnumValidator("type", CustomFieldType) def type_valid(self): return self.type is None or self.type in CustomFieldType ``` Custom field for entity with flexible types ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Methods `def type_valid(self)` Expand source code ``` @EnumValidator("type", CustomFieldType) def type_valid(self): return self.type is None or self.type in CustomFieldType ``` ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class EntityEditNumberConfig (is_decimal_allowed: bool | None = None, min_value: int | float | None = None, max_value: int | float | None = None, **kwargs)` Expand source code ``` class EntityEditNumberConfig(JsonObject): """Number configuration for entity edit support""" attributes = { "is_decimal_allowed", "min_value", "max_value", } def __init__( self, is_decimal_allowed: Optional[bool] = None, min_value: Optional[Union[int, float]] = None, max_value: Optional[Union[int, float]] = None, **kwargs, ): self.is_decimal_allowed = is_decimal_allowed self.min_value = min_value self.max_value = max_value self.additional_attributes = kwargs def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() ``` Number configuration for entity edit support ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class EntityEditSelectConfig (current_value: str | None = None, current_values: List[str] | None = None, static_options: List[Dict[str, Any]] | None = None, fetch_options_dynamically: bool | None = None, min_query_length: int | None = None, **kwargs)` Expand source code ``` class EntityEditSelectConfig(JsonObject): """Select configuration for entity edit support""" attributes = { "current_value", "current_values", "static_options", "fetch_options_dynamically", "min_query_length", } def __init__( self, current_value: Optional[str] = None, current_values: Optional[List[str]] = None, static_options: Optional[List[Dict[str, Any]]] = None, # Option[] fetch_options_dynamically: Optional[bool] = None, min_query_length: Optional[int] = None, **kwargs, ): self.current_value = current_value self.current_values = current_values self.static_options = static_options self.fetch_options_dynamically = fetch_options_dynamically self.min_query_length = min_query_length self.additional_attributes = kwargs def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() ``` Select configuration for entity edit support ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class EntityEditSupport (enabled: bool, placeholder: Dict[str, Any] | None = None, hint: Dict[str, Any] | None = None, optional: bool | None = None, select: Dict[str, Any] | [EntityEditSelectConfig](#slack_sdk.models.metadata.EntityEditSelectConfig "slack_sdk.models.metadata.EntityEditSelectConfig") | None = None, number: Dict[str, Any] | [EntityEditNumberConfig](#slack_sdk.models.metadata.EntityEditNumberConfig "slack_sdk.models.metadata.EntityEditNumberConfig") | None = None, text: Dict[str, Any] | [EntityEditTextConfig](#slack_sdk.models.metadata.EntityEditTextConfig "slack_sdk.models.metadata.EntityEditTextConfig") | None = None, **kwargs)` Expand source code ``` class EntityEditSupport(JsonObject): """Edit support configuration for entity fields""" attributes = { "enabled", "placeholder", "hint", "optional", "select", "number", "text", } def __init__( self, enabled: bool, placeholder: Optional[Dict[str, Any]] = None, # PlainTextElement hint: Optional[Dict[str, Any]] = None, # PlainTextElement optional: Optional[bool] = None, select: Optional[Union[Dict[str, Any], EntityEditSelectConfig]] = None, number: Optional[Union[Dict[str, Any], EntityEditNumberConfig]] = None, text: Optional[Union[Dict[str, Any], EntityEditTextConfig]] = None, **kwargs, ): self.enabled = enabled self.placeholder = placeholder self.hint = hint self.optional = optional self.select = select self.number = number self.text = text self.additional_attributes = kwargs def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() ``` Edit support configuration for entity fields ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class EntityEditTextConfig (min_length: int | None = None, max_length: int | None = None, **kwargs)` Expand source code ``` class EntityEditTextConfig(JsonObject): """Text configuration for entity edit support""" attributes = { "min_length", "max_length", } def __init__( self, min_length: Optional[int] = None, max_length: Optional[int] = None, **kwargs, ): self.min_length = min_length self.max_length = max_length self.additional_attributes = kwargs def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() ``` Text configuration for entity edit support ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class EntityFullSizePreview (is_supported: bool, preview_url: str | None = None, mime_type: str | None = None, error: Dict[str, Any] | [EntityFullSizePreviewError](#slack_sdk.models.metadata.EntityFullSizePreviewError "slack_sdk.models.metadata.EntityFullSizePreviewError") | None = None, **kwargs)` Expand source code ``` class EntityFullSizePreview(JsonObject): """Full-size preview configuration for entity""" attributes = { "is_supported", "preview_url", "mime_type", "error", } def __init__( self, is_supported: bool, preview_url: Optional[str] = None, mime_type: Optional[str] = None, error: Optional[Union[Dict[str, Any], EntityFullSizePreviewError]] = None, **kwargs, ): self.is_supported = is_supported self.preview_url = preview_url self.mime_type = mime_type self.error = error self.additional_attributes = kwargs def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() ``` Full-size preview configuration for entity ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class EntityFullSizePreviewError (code: str, message: str | None = None, **kwargs)` Expand source code ``` class EntityFullSizePreviewError(JsonObject): """Error information for full-size preview""" attributes = { "code", "message", } def __init__( self, code: str, message: Optional[str] = None, **kwargs, ): self.code = code self.message = message self.additional_attributes = kwargs def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() ``` Error information for full-size preview ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class EntityIconField (alt_text: str, url: str | None = None, slack_file: Dict[str, Any] | [EntityIconSlackFile](#slack_sdk.models.metadata.EntityIconSlackFile "slack_sdk.models.metadata.EntityIconSlackFile") | None = None, **kwargs)` Expand source code ``` class EntityIconField(JsonObject): """Icon field for entity attributes""" attributes = { "alt_text", "url", "slack_file", } def __init__( self, alt_text: str, url: Optional[str] = None, slack_file: Optional[Union[Dict[str, Any], EntityIconSlackFile]] = None, **kwargs, ): self.alt_text = alt_text self.url = url self.slack_file = slack_file self.additional_attributes = kwargs def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() ``` Icon field for entity attributes ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class EntityIconSlackFile (id: str | None = None, url: str | None = None, **kwargs)` Expand source code ``` class EntityIconSlackFile(JsonObject): """Slack file reference for entity icon""" attributes = { "id", "url", } def __init__( self, id: Optional[str] = None, url: Optional[str] = None, **kwargs, ): self.id = id self.url = url self.additional_attributes = kwargs def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() ``` Slack file reference for entity icon ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class EntityImageField (alt_text: str, label: str | None = None, image_url: str | None = None, slack_file: Dict[str, Any] | None = None, title: str | None = None, type: str | None = None, **kwargs)` Expand source code ``` class EntityImageField(JsonObject): """Image field for entity""" attributes = { "alt_text", "label", "image_url", "slack_file", "title", "type", } def __init__( self, alt_text: str, label: Optional[str] = None, image_url: Optional[str] = None, slack_file: Optional[Dict[str, Any]] = None, title: Optional[str] = None, type: Optional[str] = None, **kwargs, ): self.alt_text = alt_text self.label = label self.image_url = image_url self.slack_file = slack_file self.title = title self.type = type self.additional_attributes = kwargs def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() ``` Image field for entity ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class EntityMetadata (entity_type: str, entity_payload: Dict[str, Any] | [EntityPayload](#slack_sdk.models.metadata.EntityPayload "slack_sdk.models.metadata.EntityPayload"), external_ref: Dict[str, Any] | [ExternalRef](#slack_sdk.models.metadata.ExternalRef "slack_sdk.models.metadata.ExternalRef"), url: str, app_unfurl_url: str | None = None, **kwargs)` Expand source code ``` class EntityMetadata(JsonObject): """Work object entity metadata https://docs.slack.dev/messaging/work-objects/ """ attributes = { "entity_type", "entity_payload", "external_ref", "url", "app_unfurl_url", } def __init__( self, entity_type: str, entity_payload: Union[Dict[str, Any], EntityPayload], external_ref: Union[Dict[str, Any], ExternalRef], url: str, app_unfurl_url: Optional[str] = None, **kwargs, ): self.entity_type = entity_type self.entity_payload = entity_payload self.external_ref = external_ref self.url = url self.app_unfurl_url = app_unfurl_url self.additional_attributes = kwargs def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() @EnumValidator("entity_type", EntityType) def entity_type_valid(self): return self.entity_type is None or self.entity_type in EntityType ``` Work object entity metadata [https://docs.slack.dev/messaging/work-objects/](https://docs.slack.dev/messaging/work-objects/) ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Methods `def entity_type_valid(self)` Expand source code ``` @EnumValidator("entity_type", EntityType) def entity_type_valid(self): return self.entity_type is None or self.entity_type in EntityType ``` ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class EntityPayload (attributes: Dict[str, Any] | [EntityAttributes](#slack_sdk.models.metadata.EntityAttributes "slack_sdk.models.metadata.EntityAttributes"), fields: Dict[str, Any] | [ContentItemEntityFields](#slack_sdk.models.metadata.ContentItemEntityFields "slack_sdk.models.metadata.ContentItemEntityFields") | [FileEntityFields](#slack_sdk.models.metadata.FileEntityFields "slack_sdk.models.metadata.FileEntityFields") | [IncidentEntityFields](#slack_sdk.models.metadata.IncidentEntityFields "slack_sdk.models.metadata.IncidentEntityFields") | [TaskEntityFields](#slack_sdk.models.metadata.TaskEntityFields "slack_sdk.models.metadata.TaskEntityFields") | None = None, custom_fields: List[Dict[str, Any] | [EntityCustomField](#slack_sdk.models.metadata.EntityCustomField "slack_sdk.models.metadata.EntityCustomField")] | None = None, slack_file: Dict[str, Any] | [FileEntitySlackFile](#slack_sdk.models.metadata.FileEntitySlackFile "slack_sdk.models.metadata.FileEntitySlackFile") | None = None, display_order: List[str] | None = None, actions: Dict[str, Any] | [EntityActions](#slack_sdk.models.metadata.EntityActions "slack_sdk.models.metadata.EntityActions") | None = None, **kwargs)` Expand source code ``` class EntityPayload(JsonObject): """Payload schema for an entity""" attributes = { "attributes", "fields", "custom_fields", "slack_file", "display_order", "actions", } def __init__( self, attributes: Union[Dict[str, Any], EntityAttributes], fields: Optional[ Union[Dict[str, Any], ContentItemEntityFields, FileEntityFields, IncidentEntityFields, TaskEntityFields] ] = None, custom_fields: Optional[List[Union[Dict[str, Any], EntityCustomField]]] = None, slack_file: Optional[Union[Dict[str, Any], FileEntitySlackFile]] = None, display_order: Optional[List[str]] = None, actions: Optional[Union[Dict[str, Any], EntityActions]] = None, **kwargs, ): # Store entity attributes data with a different internal name to avoid # shadowing the class-level 'attributes' set used for JSON serialization self._entity_attributes = attributes self.fields = fields self.custom_fields = custom_fields self.slack_file = slack_file self.display_order = display_order self.actions = actions self.additional_attributes = kwargs @property def entity_attributes(self) -> Union[Dict[str, Any], EntityAttributes]: """Get the entity attributes data. Note: Use this property to access the attributes data. The class-level 'attributes' is reserved for the JSON serialization schema. """ return self._entity_attributes @entity_attributes.setter def entity_attributes(self, value: Union[Dict[str, Any], EntityAttributes]): """Set the entity attributes data.""" self._entity_attributes = value def get_object_attribute(self, key: str): if key == "attributes": return self._entity_attributes else: return getattr(self, key, None) def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() ``` Payload schema for an entity ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Instance variables `prop entity_attributes : Dict[str, Any] | [EntityAttributes](#slack_sdk.models.metadata.EntityAttributes "slack_sdk.models.metadata.EntityAttributes")` Expand source code ``` @property def entity_attributes(self) -> Union[Dict[str, Any], EntityAttributes]: """Get the entity attributes data. Note: Use this property to access the attributes data. The class-level 'attributes' is reserved for the JSON serialization schema. """ return self._entity_attributes ``` Get the entity attributes data. Note: Use this property to access the attributes data. The class-level 'attributes' is reserved for the JSON serialization schema. ### Methods `def get_object_attribute(self, key: str)` Expand source code ``` def get_object_attribute(self, key: str): if key == "attributes": return self._entity_attributes else: return getattr(self, key, None) ``` ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class EntityRefField (entity_url: str, external_ref: Dict[str, Any] | [ExternalRef](#slack_sdk.models.metadata.ExternalRef "slack_sdk.models.metadata.ExternalRef"), title: str, display_type: str | None = None, icon: Dict[str, Any] | [EntityIconField](#slack_sdk.models.metadata.EntityIconField "slack_sdk.models.metadata.EntityIconField") | None = None, **kwargs)` Expand source code ``` class EntityRefField(JsonObject): """Entity reference field""" attributes = { "entity_url", "external_ref", "title", "display_type", "icon", } def __init__( self, entity_url: str, external_ref: Union[Dict[str, Any], ExternalRef], title: str, display_type: Optional[str] = None, icon: Optional[Union[Dict[str, Any], EntityIconField]] = None, **kwargs, ): self.entity_url = entity_url self.external_ref = external_ref self.title = title self.display_type = display_type self.icon = icon self.additional_attributes = kwargs def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() ``` Entity reference field ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class EntityStringField (value: str, label: str | None = None, format: str | None = None, link: str | None = None, icon: Dict[str, Any] | [EntityIconField](#slack_sdk.models.metadata.EntityIconField "slack_sdk.models.metadata.EntityIconField") | None = None, long: bool | None = None, type: str | None = None, tag_color: str | None = None, edit: Dict[str, Any] | [EntityEditSupport](#slack_sdk.models.metadata.EntityEditSupport "slack_sdk.models.metadata.EntityEditSupport") | None = None, **kwargs)` Expand source code ``` class EntityStringField(JsonObject): """String field for entity""" attributes = { "value", "label", "format", "link", "icon", "long", "type", "tag_color", "edit", } def __init__( self, value: str, label: Optional[str] = None, format: Optional[str] = None, link: Optional[str] = None, icon: Optional[Union[Dict[str, Any], EntityIconField]] = None, long: Optional[bool] = None, type: Optional[str] = None, tag_color: Optional[str] = None, edit: Optional[Union[Dict[str, Any], EntityEditSupport]] = None, **kwargs, ): self.value = value self.label = label self.format = format self.link = link self.icon = icon self.long = long self.type = type self.tag_color = tag_color self.edit = edit self.additional_attributes = kwargs def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() ``` String field for entity ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class EntityTimestampField (value: int, label: str | None = None, type: str | None = None, edit: Dict[str, Any] | [EntityEditSupport](#slack_sdk.models.metadata.EntityEditSupport "slack_sdk.models.metadata.EntityEditSupport") | None = None, **kwargs)` Expand source code ``` class EntityTimestampField(JsonObject): """Timestamp field for entity""" attributes = { "value", "label", "type", "edit", } def __init__( self, value: int, label: Optional[str] = None, type: Optional[str] = None, edit: Optional[Union[Dict[str, Any], EntityEditSupport]] = None, **kwargs, ): self.value = value self.label = label self.type = type self.edit = edit self.additional_attributes = kwargs def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() ``` Timestamp field for entity ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class EntityTitle (text: str, edit: Dict[str, Any] | [EntityEditSupport](#slack_sdk.models.metadata.EntityEditSupport "slack_sdk.models.metadata.EntityEditSupport") | None = None, **kwargs)` Expand source code ``` class EntityTitle(JsonObject): """Title for entity attributes""" attributes = { "text", "edit", } def __init__( self, text: str, edit: Optional[Union[Dict[str, Any], EntityEditSupport]] = None, **kwargs, ): self.text = text self.edit = edit self.additional_attributes = kwargs def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() ``` Title for entity attributes ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class EntityTypedField (type: str, label: str | None = None, value: str | int | None = None, link: str | None = None, icon: Dict[str, Any] | [EntityIconField](#slack_sdk.models.metadata.EntityIconField "slack_sdk.models.metadata.EntityIconField") | None = None, long: bool | None = None, format: str | None = None, image_url: str | None = None, slack_file: Dict[str, Any] | None = None, alt_text: str | None = None, edit: Dict[str, Any] | [EntityEditSupport](#slack_sdk.models.metadata.EntityEditSupport "slack_sdk.models.metadata.EntityEditSupport") | None = None, tag_color: str | None = None, user: Dict[str, Any] | [EntityUserIDField](#slack_sdk.models.metadata.EntityUserIDField "slack_sdk.models.metadata.EntityUserIDField") | [EntityUserField](#slack_sdk.models.metadata.EntityUserField "slack_sdk.models.metadata.EntityUserField") | None = None, entity_ref: Dict[str, Any] | [EntityRefField](#slack_sdk.models.metadata.EntityRefField "slack_sdk.models.metadata.EntityRefField") | None = None, **kwargs)` Expand source code ``` class EntityTypedField(JsonObject): """Typed field for entity with various display options""" attributes = { "type", "label", "value", "link", "icon", "long", "format", "image_url", "slack_file", "alt_text", "edit", "tag_color", "user", "entity_ref", } def __init__( self, type: str, label: Optional[str] = None, value: Optional[Union[str, int]] = None, link: Optional[str] = None, icon: Optional[Union[Dict[str, Any], EntityIconField]] = None, long: Optional[bool] = None, format: Optional[str] = None, image_url: Optional[str] = None, slack_file: Optional[Dict[str, Any]] = None, alt_text: Optional[str] = None, edit: Optional[Union[Dict[str, Any], EntityEditSupport]] = None, tag_color: Optional[str] = None, user: Optional[Union[Dict[str, Any], EntityUserIDField, EntityUserField]] = None, entity_ref: Optional[Union[Dict[str, Any], EntityRefField]] = None, **kwargs, ): self.type = type self.label = label self.value = value self.link = link self.icon = icon self.long = long self.format = format self.image_url = image_url self.slack_file = slack_file self.alt_text = alt_text self.edit = edit self.tag_color = tag_color self.user = user self.entity_ref = entity_ref self.additional_attributes = kwargs def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() ``` Typed field for entity with various display options ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class EntityUserField (text: str, url: str | None = None, email: str | None = None, icon: Dict[str, Any] | [EntityIconField](#slack_sdk.models.metadata.EntityIconField "slack_sdk.models.metadata.EntityIconField") | None = None, **kwargs)` Expand source code ``` class EntityUserField(JsonObject): """User field for entity""" attributes = { "text", "url", "email", "icon", } def __init__( self, text: str, url: Optional[str] = None, email: Optional[str] = None, icon: Optional[Union[Dict[str, Any], EntityIconField]] = None, **kwargs, ): self.text = text self.url = url self.email = email self.icon = icon self.additional_attributes = kwargs def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() ``` User field for entity ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class EntityUserIDField (user_id: str, **kwargs)` Expand source code ``` class EntityUserIDField(JsonObject): """User ID field for entity""" attributes = { "user_id", } def __init__( self, user_id: str, **kwargs, ): self.user_id = user_id self.additional_attributes = kwargs def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() ``` User ID field for entity ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class EventAndEntityMetadata (event_type: str | None = None, event_payload: Dict[str, Any] | None = None, entities: List[Dict[str, Any] | [EntityMetadata](#slack_sdk.models.metadata.EntityMetadata "slack_sdk.models.metadata.EntityMetadata")] | None = None, **kwargs)` Expand source code ``` class EventAndEntityMetadata(JsonObject): """Message metadata with entities https://docs.slack.dev/messaging/message-metadata/ https://docs.slack.dev/messaging/work-objects/ """ attributes = {"event_type", "event_payload", "entities"} def __init__( self, event_type: Optional[str] = None, event_payload: Optional[Dict[str, Any]] = None, entities: Optional[List[Union[Dict[str, Any], EntityMetadata]]] = None, **kwargs, ): self.event_type = event_type self.event_payload = event_payload self.entities = entities self.additional_attributes = kwargs def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() ``` Message metadata with entities [https://docs.slack.dev/messaging/message-metadata/](https://docs.slack.dev/messaging/message-metadata/) [https://docs.slack.dev/messaging/work-objects/](https://docs.slack.dev/messaging/work-objects/) ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class ExternalRef (id: str, type: str | None = None, **kwargs)` Expand source code ``` class ExternalRef(JsonObject): """Reference (and optional type) used to identify an entity within the developer's system""" attributes = { "id", "type", } def __init__( self, id: str, type: Optional[str] = None, **kwargs, ): self.id = id self.type = type self.additional_attributes = kwargs def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() ``` Reference (and optional type) used to identify an entity within the developer's system ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class FileEntityFields (preview: Dict[str, Any] | [EntityImageField](#slack_sdk.models.metadata.EntityImageField "slack_sdk.models.metadata.EntityImageField") | None = None, created_by: Dict[str, Any] | [EntityTypedField](#slack_sdk.models.metadata.EntityTypedField "slack_sdk.models.metadata.EntityTypedField") | None = None, date_created: Dict[str, Any] | [EntityTimestampField](#slack_sdk.models.metadata.EntityTimestampField "slack_sdk.models.metadata.EntityTimestampField") | None = None, date_updated: Dict[str, Any] | [EntityTimestampField](#slack_sdk.models.metadata.EntityTimestampField "slack_sdk.models.metadata.EntityTimestampField") | None = None, last_modified_by: Dict[str, Any] | [EntityTypedField](#slack_sdk.models.metadata.EntityTypedField "slack_sdk.models.metadata.EntityTypedField") | None = None, file_size: Dict[str, Any] | [EntityStringField](#slack_sdk.models.metadata.EntityStringField "slack_sdk.models.metadata.EntityStringField") | None = None, mime_type: Dict[str, Any] | [EntityStringField](#slack_sdk.models.metadata.EntityStringField "slack_sdk.models.metadata.EntityStringField") | None = None, full_size_preview: Dict[str, Any] | [EntityFullSizePreview](#slack_sdk.models.metadata.EntityFullSizePreview "slack_sdk.models.metadata.EntityFullSizePreview") | None = None, **kwargs)` Expand source code ``` class FileEntityFields(JsonObject): """Fields specific to file entities""" attributes = { "preview", "created_by", "date_created", "date_updated", "last_modified_by", "file_size", "mime_type", "full_size_preview", } def __init__( self, preview: Optional[Union[Dict[str, Any], EntityImageField]] = None, created_by: Optional[Union[Dict[str, Any], EntityTypedField]] = None, date_created: Optional[Union[Dict[str, Any], EntityTimestampField]] = None, date_updated: Optional[Union[Dict[str, Any], EntityTimestampField]] = None, last_modified_by: Optional[Union[Dict[str, Any], EntityTypedField]] = None, file_size: Optional[Union[Dict[str, Any], EntityStringField]] = None, mime_type: Optional[Union[Dict[str, Any], EntityStringField]] = None, full_size_preview: Optional[Union[Dict[str, Any], EntityFullSizePreview]] = None, **kwargs, ): self.preview = preview self.created_by = created_by self.date_created = date_created self.date_updated = date_updated self.last_modified_by = last_modified_by self.file_size = file_size self.mime_type = mime_type self.full_size_preview = full_size_preview self.additional_attributes = kwargs def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() ``` Fields specific to file entities ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class FileEntitySlackFile (id: str, type: str | None = None, **kwargs)` Expand source code ``` class FileEntitySlackFile(JsonObject): """Slack file reference for file entities""" attributes = { "id", "type", } def __init__( self, id: str, type: Optional[str] = None, **kwargs, ): self.id = id self.type = type self.additional_attributes = kwargs def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() ``` Slack file reference for file entities ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class IncidentEntityFields (status: Dict[str, Any] | [EntityStringField](#slack_sdk.models.metadata.EntityStringField "slack_sdk.models.metadata.EntityStringField") | None = None, priority: Dict[str, Any] | [EntityStringField](#slack_sdk.models.metadata.EntityStringField "slack_sdk.models.metadata.EntityStringField") | None = None, urgency: Dict[str, Any] | [EntityStringField](#slack_sdk.models.metadata.EntityStringField "slack_sdk.models.metadata.EntityStringField") | None = None, created_by: Dict[str, Any] | [EntityTypedField](#slack_sdk.models.metadata.EntityTypedField "slack_sdk.models.metadata.EntityTypedField") | None = None, assigned_to: Dict[str, Any] | [EntityTypedField](#slack_sdk.models.metadata.EntityTypedField "slack_sdk.models.metadata.EntityTypedField") | None = None, date_created: Dict[str, Any] | [EntityTimestampField](#slack_sdk.models.metadata.EntityTimestampField "slack_sdk.models.metadata.EntityTimestampField") | None = None, date_updated: Dict[str, Any] | [EntityTimestampField](#slack_sdk.models.metadata.EntityTimestampField "slack_sdk.models.metadata.EntityTimestampField") | None = None, description: Dict[str, Any] | [EntityStringField](#slack_sdk.models.metadata.EntityStringField "slack_sdk.models.metadata.EntityStringField") | None = None, service: Dict[str, Any] | [EntityStringField](#slack_sdk.models.metadata.EntityStringField "slack_sdk.models.metadata.EntityStringField") | None = None, **kwargs)` Expand source code ``` class IncidentEntityFields(JsonObject): """Fields specific to incident entities""" attributes = { "status", "priority", "urgency", "created_by", "assigned_to", "date_created", "date_updated", "description", "service", } def __init__( self, status: Optional[Union[Dict[str, Any], EntityStringField]] = None, priority: Optional[Union[Dict[str, Any], EntityStringField]] = None, urgency: Optional[Union[Dict[str, Any], EntityStringField]] = None, created_by: Optional[Union[Dict[str, Any], EntityTypedField]] = None, assigned_to: Optional[Union[Dict[str, Any], EntityTypedField]] = None, date_created: Optional[Union[Dict[str, Any], EntityTimestampField]] = None, date_updated: Optional[Union[Dict[str, Any], EntityTimestampField]] = None, description: Optional[Union[Dict[str, Any], EntityStringField]] = None, service: Optional[Union[Dict[str, Any], EntityStringField]] = None, **kwargs, ): self.status = status self.priority = priority self.urgency = urgency self.created_by = created_by self.assigned_to = assigned_to self.date_created = date_created self.date_updated = date_updated self.description = description self.service = service self.additional_attributes = kwargs def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() ``` Fields specific to incident entities ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class Metadata (event_type: str, event_payload: Dict[str, Any], **kwargs)` Expand source code ``` class Metadata(JsonObject): """Message metadata https://docs.slack.dev/messaging/message-metadata/ """ attributes = { "event_type", "event_payload", } def __init__( self, event_type: str, event_payload: Dict[str, Any], **kwargs, ): self.event_type = event_type self.event_payload = event_payload self.additional_attributes = kwargs def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() ``` Message metadata [https://docs.slack.dev/messaging/message-metadata/](https://docs.slack.dev/messaging/message-metadata/) ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class TaskEntityFields (description: Dict[str, Any] | [EntityStringField](#slack_sdk.models.metadata.EntityStringField "slack_sdk.models.metadata.EntityStringField") | None = None, created_by: Dict[str, Any] | [EntityTypedField](#slack_sdk.models.metadata.EntityTypedField "slack_sdk.models.metadata.EntityTypedField") | None = None, date_created: Dict[str, Any] | [EntityTimestampField](#slack_sdk.models.metadata.EntityTimestampField "slack_sdk.models.metadata.EntityTimestampField") | None = None, date_updated: Dict[str, Any] | [EntityTimestampField](#slack_sdk.models.metadata.EntityTimestampField "slack_sdk.models.metadata.EntityTimestampField") | None = None, assignee: Dict[str, Any] | [EntityTypedField](#slack_sdk.models.metadata.EntityTypedField "slack_sdk.models.metadata.EntityTypedField") | None = None, status: Dict[str, Any] | [EntityStringField](#slack_sdk.models.metadata.EntityStringField "slack_sdk.models.metadata.EntityStringField") | None = None, due_date: Dict[str, Any] | [EntityTypedField](#slack_sdk.models.metadata.EntityTypedField "slack_sdk.models.metadata.EntityTypedField") | None = None, priority: Dict[str, Any] | [EntityStringField](#slack_sdk.models.metadata.EntityStringField "slack_sdk.models.metadata.EntityStringField") | None = None, **kwargs)` Expand source code ``` class TaskEntityFields(JsonObject): """Fields specific to task entities""" attributes = { "description", "created_by", "date_created", "date_updated", "assignee", "status", "due_date", "priority", } def __init__( self, description: Optional[Union[Dict[str, Any], EntityStringField]] = None, created_by: Optional[Union[Dict[str, Any], EntityTypedField]] = None, date_created: Optional[Union[Dict[str, Any], EntityTimestampField]] = None, date_updated: Optional[Union[Dict[str, Any], EntityTimestampField]] = None, assignee: Optional[Union[Dict[str, Any], EntityTypedField]] = None, status: Optional[Union[Dict[str, Any], EntityStringField]] = None, due_date: Optional[Union[Dict[str, Any], EntityTypedField]] = None, priority: Optional[Union[Dict[str, Any], EntityStringField]] = None, **kwargs, ): self.description = description self.created_by = created_by self.date_created = date_created self.date_updated = date_updated self.assignee = assignee self.status = status self.due_date = due_date self.priority = priority self.additional_attributes = kwargs def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() ``` Fields specific to task entities ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/models/views # Module slack_sdk.models.views ## Classes `class View (type: str, id: str | None = None, callback_id: str | None = None, external_id: str | None = None, team_id: str | None = None, bot_id: str | None = None, app_id: str | None = None, root_view_id: str | None = None, previous_view_id: str | None = None, title: str | dict | [PlainTextObject](../blocks/basic_components.html#slack_sdk.models.blocks.basic_components.PlainTextObject "slack_sdk.models.blocks.basic_components.PlainTextObject") | None = None, submit: str | dict | [PlainTextObject](../blocks/basic_components.html#slack_sdk.models.blocks.basic_components.PlainTextObject "slack_sdk.models.blocks.basic_components.PlainTextObject") | None = None, close: str | dict | [PlainTextObject](../blocks/basic_components.html#slack_sdk.models.blocks.basic_components.PlainTextObject "slack_sdk.models.blocks.basic_components.PlainTextObject") | None = None, blocks: Sequence[dict | [Block](../blocks/blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")] | None = None, private_metadata: str | None = None, state: dict | ForwardRef('[ViewState](#slack_sdk.models.views.ViewState "slack_sdk.models.views.ViewState")') | None = None, hash: str | None = None, clear_on_close: bool | None = None, notify_on_close: bool | None = None, **kwargs)` Expand source code ``` class View(JsonObject): """View object for modals and Home tabs. https://docs.slack.dev/reference/views/ """ types = ["modal", "home", "workflow_step"] attributes = { "type", "id", "callback_id", "external_id", "team_id", "bot_id", "app_id", "root_view_id", "previous_view_id", "title", "submit", "close", "blocks", "private_metadata", "state", "hash", "clear_on_close", "notify_on_close", } def __init__( self, # "modal", "home", and "workflow_step" type: str, id: Optional[str] = None, callback_id: Optional[str] = None, external_id: Optional[str] = None, team_id: Optional[str] = None, bot_id: Optional[str] = None, app_id: Optional[str] = None, root_view_id: Optional[str] = None, previous_view_id: Optional[str] = None, title: Optional[Union[str, dict, PlainTextObject]] = None, submit: Optional[Union[str, dict, PlainTextObject]] = None, close: Optional[Union[str, dict, PlainTextObject]] = None, blocks: Optional[Sequence[Union[dict, Block]]] = None, private_metadata: Optional[str] = None, state: Optional[Union[dict, "ViewState"]] = None, hash: Optional[str] = None, clear_on_close: Optional[bool] = None, notify_on_close: Optional[bool] = None, **kwargs, ): self.type = type self.id = id self.callback_id = callback_id self.external_id = external_id self.team_id = team_id self.bot_id = bot_id self.app_id = app_id self.root_view_id = root_view_id self.previous_view_id = previous_view_id self.title = TextObject.parse(title, default_type=PlainTextObject.type) # type: ignore[arg-type] self.submit = TextObject.parse(submit, default_type=PlainTextObject.type) # type: ignore[arg-type] self.close = TextObject.parse(close, default_type=PlainTextObject.type) # type: ignore[arg-type] self.blocks = Block.parse_all(blocks) self.private_metadata = private_metadata self.state = state if self.state is not None and isinstance(self.state, dict): self.state = ViewState(**self.state) self.hash = hash self.clear_on_close = clear_on_close self.notify_on_close = notify_on_close self.additional_attributes = kwargs title_max_length = 24 blocks_max_length = 100 close_max_length = 24 submit_max_length = 24 private_metadata_max_length = 3000 callback_id_max_length: int = 255 @JsonValidator('type must be either "modal", "home" or "workflow_step"') def _validate_type(self): return self.type is not None and self.type in self.types @JsonValidator(f"title must be between 1 and {title_max_length} characters") def _validate_title_length(self): return self.title is None or 1 <= len(self.title.text) <= self.title_max_length @JsonValidator(f"views must contain between 1 and {blocks_max_length} blocks") def _validate_blocks_length(self): return self.blocks is None or 0 < len(self.blocks) <= self.blocks_max_length @JsonValidator("home view cannot have submit and close") def _validate_home_tab_structure(self): return self.type != "home" or (self.type == "home" and self.close is None and self.submit is None) @JsonValidator(f"close cannot exceed {close_max_length} characters") def _validate_close_length(self): return self.close is None or len(self.close.text) <= self.close_max_length @JsonValidator(f"submit cannot exceed {submit_max_length} characters") def _validate_submit_length(self): return self.submit is None or len(self.submit.text) <= int(self.submit_max_length) @JsonValidator(f"private_metadata cannot exceed {private_metadata_max_length} characters") def _validate_private_metadata_max_length(self): return self.private_metadata is None or len(self.private_metadata) <= self.private_metadata_max_length @JsonValidator(f"callback_id cannot exceed {callback_id_max_length} characters") def _validate_callback_id_max_length(self): return self.callback_id is None or len(self.callback_id) <= self.callback_id_max_length def __str__(self): return str(self.get_non_null_attributes()) def __repr__(self): return self.__str__() ``` View object for modals and Home tabs. [https://docs.slack.dev/reference/views/](https://docs.slack.dev/reference/views/) ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. `var blocks_max_length` The type of the None singleton. `var callback_id_max_length : int` The type of the None singleton. `var close_max_length` The type of the None singleton. `var private_metadata_max_length` The type of the None singleton. `var submit_max_length` The type of the None singleton. `var title_max_length` The type of the None singleton. `var types` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class ViewState (*, values: Dict[str, Dict[str, dict | ForwardRef('[ViewStateValue](#slack_sdk.models.views.ViewStateValue "slack_sdk.models.views.ViewStateValue")')]])` Expand source code ``` class ViewState(JsonObject): attributes = {"values"} logger = logging.getLogger(__name__) @classmethod def _show_warning_about_unknown(cls, value): c = value.__class__ name = ".".join([c.__module__, c.__name__]) cls.logger.warning(f"Unknown type for view.state.values detected ({name}) and ViewState skipped to add it") def __init__( self, *, values: Dict[str, Dict[str, Union[dict, "ViewStateValue"]]], ): value_objects: Dict[str, Dict[str, ViewStateValue]] = {} new_state_values = copy.copy(values) if isinstance(new_state_values, dict): # just in case for block_id, actions in new_state_values.items(): if actions is None: continue elif isinstance(actions, dict): new_actions: Dict[str, Union[ViewStateValue, dict]] = copy.copy(actions) for action_id, v in actions.items(): if isinstance(v, dict): d = copy.copy(v) value_object = ViewStateValue(**d) elif isinstance(v, ViewStateValue): value_object = v else: self._show_warning_about_unknown(v) continue new_actions[action_id] = value_object value_objects[block_id] = new_actions # type: ignore[assignment] else: self._show_warning_about_unknown(v) self.values = value_objects def to_dict(self, *args) -> Dict[str, Dict[str, Dict[str, dict]]]: self.validate_json() if self.values is not None: dict_values: Dict[str, Dict[str, dict]] = {} for block_id, actions in self.values.items(): if actions: dict_value: Dict[str, dict] = {action_id: value.to_dict() for action_id, value in actions.items()} dict_values[block_id] = dict_value return {"values": dict_values} else: return {} ``` The base class for JSON serializable class objects ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. `var logger` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` `class ViewStateValue (*, type: str | None = None, value: str | None = None, selected_date: str | None = None, selected_time: str | None = None, selected_conversation: str | None = None, selected_channel: str | None = None, selected_user: str | None = None, selected_option: dict | [Option](../blocks/basic_components.html#slack_sdk.models.blocks.basic_components.Option "slack_sdk.models.blocks.basic_components.Option") | None = None, selected_conversations: Sequence[str] | None = None, selected_channels: Sequence[str] | None = None, selected_users: Sequence[str] | None = None, selected_options: Sequence[dict | [Option](../blocks/basic_components.html#slack_sdk.models.blocks.basic_components.Option "slack_sdk.models.blocks.basic_components.Option")] | None = None)` Expand source code ``` class ViewStateValue(JsonObject): attributes = { "type", "value", "selected_date", "selected_time", "selected_conversation", "selected_channel", "selected_user", "selected_option", "selected_conversations", "selected_channels", "selected_users", "selected_options", } def __init__( self, *, type: Optional[str] = None, value: Optional[str] = None, selected_date: Optional[str] = None, selected_time: Optional[str] = None, selected_conversation: Optional[str] = None, selected_channel: Optional[str] = None, selected_user: Optional[str] = None, selected_option: Optional[Union[dict, Option]] = None, selected_conversations: Optional[Sequence[str]] = None, selected_channels: Optional[Sequence[str]] = None, selected_users: Optional[Sequence[str]] = None, selected_options: Optional[Sequence[Union[dict, Option]]] = None, ): self.type = type self.value = value self.selected_date = selected_date self.selected_time = selected_time self.selected_conversation = selected_conversation self.selected_channel = selected_channel self.selected_user = selected_user self.selected_option = selected_option self.selected_conversations = selected_conversations self.selected_channels = selected_channels self.selected_users = selected_users if isinstance(selected_options, list): self.selected_options = [] for option in selected_options: if isinstance(option, Option): self.selected_options.append(option) elif isinstance(option, dict): self.selected_options.append(Option(**option)) else: self.selected_options = selected_options # type: ignore[assignment] ``` The base class for JSON serializable class objects ### Ancestors * [JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject") * [BaseObject](../basic_objects.html#slack_sdk.models.basic_objects.BaseObject "slack_sdk.models.basic_objects.BaseObject") ### Class variables `var attributes` The type of the None singleton. ### Inherited members * `**[JsonObject](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject "slack_sdk.models.basic_objects.JsonObject")**`: * `[get_non_null_attributes](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes "slack_sdk.models.basic_objects.JsonObject.get_non_null_attributes")` * `[to_dict](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.to_dict "slack_sdk.models.basic_objects.JsonObject.to_dict")` * `[validate_json](../basic_objects.html#slack_sdk.models.basic_objects.JsonObject.validate_json "slack_sdk.models.basic_objects.JsonObject.validate_json")` --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/oauth # Module slack_sdk.oauth Modules for implementing the Slack OAuth flow [https://docs.slack.dev/tools/python-slack-sdk/oauth](https://docs.slack.dev/tools/python-slack-sdk/oauth) ## Sub-modules `[slack_sdk.oauth.authorize_url_generator](authorize_url_generator/index.html "slack_sdk.oauth.authorize_url_generator")` `[slack_sdk.oauth.installation_store](installation_store/index.html "slack_sdk.oauth.installation_store")` `[slack_sdk.oauth.redirect_uri_page_renderer](redirect_uri_page_renderer/index.html "slack_sdk.oauth.redirect_uri_page_renderer")` `[slack_sdk.oauth.sqlalchemy_utils](sqlalchemy_utils/index.html "slack_sdk.oauth.sqlalchemy_utils")` `[slack_sdk.oauth.state_store](state_store/index.html "slack_sdk.oauth.state_store")` OAuth state parameter data store … `[slack_sdk.oauth.state_utils](state_utils/index.html "slack_sdk.oauth.state_utils")` `[slack_sdk.oauth.token_rotation](token_rotation/index.html "slack_sdk.oauth.token_rotation")` ## Classes `class AuthorizeUrlGenerator (*, client_id: str, redirect_uri: str | None = None, scopes: Sequence[str] | None = None, user_scopes: Sequence[str] | None = None, authorization_url: str = 'https://slack.com/oauth/v2/authorize')` Expand source code ``` class AuthorizeUrlGenerator: def __init__( self, *, client_id: str, redirect_uri: Optional[str] = None, scopes: Optional[Sequence[str]] = None, user_scopes: Optional[Sequence[str]] = None, authorization_url: str = "https://slack.com/oauth/v2/authorize", ): self.client_id = client_id self.redirect_uri = redirect_uri self.scopes = scopes self.user_scopes = user_scopes self.authorization_url = authorization_url def generate(self, state: str, team: Optional[str] = None) -> str: scopes = ",".join(self.scopes) if self.scopes else "" user_scopes = ",".join(self.user_scopes) if self.user_scopes else "" url = ( f"{self.authorization_url}?" f"state={state}&" f"client_id={self.client_id}&" f"scope={scopes}&" f"user_scope={user_scopes}" ) if self.redirect_uri is not None: url += f"&redirect_uri={self.redirect_uri}" if team is not None: url += f"&team={team}" return url ``` ### Methods `def generate(self, state: str, team: str | None = None) ‑> str` Expand source code ``` def generate(self, state: str, team: Optional[str] = None) -> str: scopes = ",".join(self.scopes) if self.scopes else "" user_scopes = ",".join(self.user_scopes) if self.user_scopes else "" url = ( f"{self.authorization_url}?" f"state={state}&" f"client_id={self.client_id}&" f"scope={scopes}&" f"user_scope={user_scopes}" ) if self.redirect_uri is not None: url += f"&redirect_uri={self.redirect_uri}" if team is not None: url += f"&team={team}" return url ``` `class InstallationStore` Expand source code ``` class InstallationStore: """The installation store interface. The minimum required methods are: * save(installation) * find_installation(enterprise_id, team_id, user_id, is_enterprise_install) If you would like to properly handle app uninstallations and token revocations, the following methods should be implemented. * delete_installation(enterprise_id, team_id, user_id) * delete_all(enterprise_id, team_id) If your app needs only bot scope installations, the simpler way to implement would be: * save(installation) * find_bot(enterprise_id, team_id, is_enterprise_install) * delete_bot(enterprise_id, team_id) * delete_all(enterprise_id, team_id) """ @property def logger(self) -> Logger: raise NotImplementedError() def save(self, installation: Installation): """Saves an installation data""" raise NotImplementedError() def save_bot(self, bot: Bot): """Saves a bot installation data""" raise NotImplementedError() def find_bot( self, *, enterprise_id: Optional[str], team_id: Optional[str], is_enterprise_install: Optional[bool] = False, ) -> Optional[Bot]: """Finds a bot scope installation per workspace / org""" raise NotImplementedError() def find_installation( self, *, enterprise_id: Optional[str], team_id: Optional[str], user_id: Optional[str] = None, is_enterprise_install: Optional[bool] = False, ) -> Optional[Installation]: """Finds a relevant installation for the given IDs. If the user_id is absent, this method may return the latest installation in the workspace / org. """ raise NotImplementedError() def delete_bot( self, *, enterprise_id: Optional[str], team_id: Optional[str], ) -> None: """Deletes a bot scope installation per workspace / org""" raise NotImplementedError() def delete_installation( self, *, enterprise_id: Optional[str], team_id: Optional[str], user_id: Optional[str] = None, ) -> None: """Deletes an installation that matches the given IDs""" raise NotImplementedError() def delete_all( self, *, enterprise_id: Optional[str], team_id: Optional[str], ): """Deletes all installation data for the given workspace / org""" self.delete_bot(enterprise_id=enterprise_id, team_id=team_id) self.delete_installation(enterprise_id=enterprise_id, team_id=team_id) ``` The installation store interface. The minimum required methods are: * save(installation) * find\_installation(enterprise\_id, team\_id, user\_id, is\_enterprise\_install) If you would like to properly handle app uninstallations and token revocations, the following methods should be implemented. * delete\_installation(enterprise\_id, team\_id, user\_id) * delete\_all(enterprise\_id, team\_id) If your app needs only bot scope installations, the simpler way to implement would be: * save(installation) * find\_bot(enterprise\_id, team\_id, is\_enterprise\_install) * delete\_bot(enterprise\_id, team\_id) * delete\_all(enterprise\_id, team\_id) ### Subclasses * [AmazonS3InstallationStore](installation_store/amazon_s3/index.html#slack_sdk.oauth.installation_store.amazon_s3.AmazonS3InstallationStore "slack_sdk.oauth.installation_store.amazon_s3.AmazonS3InstallationStore") * [CacheableInstallationStore](installation_store/cacheable_installation_store.html#slack_sdk.oauth.installation_store.cacheable_installation_store.CacheableInstallationStore "slack_sdk.oauth.installation_store.cacheable_installation_store.CacheableInstallationStore") * [FileInstallationStore](installation_store/file/index.html#slack_sdk.oauth.installation_store.file.FileInstallationStore "slack_sdk.oauth.installation_store.file.FileInstallationStore") * [SQLAlchemyInstallationStore](installation_store/sqlalchemy/index.html#slack_sdk.oauth.installation_store.sqlalchemy.SQLAlchemyInstallationStore "slack_sdk.oauth.installation_store.sqlalchemy.SQLAlchemyInstallationStore") * [SQLite3InstallationStore](installation_store/sqlite3/index.html#slack_sdk.oauth.installation_store.sqlite3.SQLite3InstallationStore "slack_sdk.oauth.installation_store.sqlite3.SQLite3InstallationStore") ### Instance variables `prop logger : logging.Logger` Expand source code ``` @property def logger(self) -> Logger: raise NotImplementedError() ``` ### Methods `def delete_all(self, *, enterprise_id: str | None, team_id: str | None)` Expand source code ``` def delete_all( self, *, enterprise_id: Optional[str], team_id: Optional[str], ): """Deletes all installation data for the given workspace / org""" self.delete_bot(enterprise_id=enterprise_id, team_id=team_id) self.delete_installation(enterprise_id=enterprise_id, team_id=team_id) ``` Deletes all installation data for the given workspace / org `def delete_bot(self, *, enterprise_id: str | None, team_id: str | None) ‑> None` Expand source code ``` def delete_bot( self, *, enterprise_id: Optional[str], team_id: Optional[str], ) -> None: """Deletes a bot scope installation per workspace / org""" raise NotImplementedError() ``` Deletes a bot scope installation per workspace / org `def delete_installation(self, *, enterprise_id: str | None, team_id: str | None, user_id: str | None = None) ‑> None` Expand source code ``` def delete_installation( self, *, enterprise_id: Optional[str], team_id: Optional[str], user_id: Optional[str] = None, ) -> None: """Deletes an installation that matches the given IDs""" raise NotImplementedError() ``` Deletes an installation that matches the given IDs `def find_bot(self, *, enterprise_id: str | None, team_id: str | None, is_enterprise_install: bool | None = False) ‑> [Bot](installation_store/models/bot.html#slack_sdk.oauth.installation_store.models.bot.Bot "slack_sdk.oauth.installation_store.models.bot.Bot") | None` Expand source code ``` def find_bot( self, *, enterprise_id: Optional[str], team_id: Optional[str], is_enterprise_install: Optional[bool] = False, ) -> Optional[Bot]: """Finds a bot scope installation per workspace / org""" raise NotImplementedError() ``` Finds a bot scope installation per workspace / org `def find_installation(self, *, enterprise_id: str | None, team_id: str | None, user_id: str | None = None, is_enterprise_install: bool | None = False) ‑> [Installation](installation_store/models/installation.html#slack_sdk.oauth.installation_store.models.installation.Installation "slack_sdk.oauth.installation_store.models.installation.Installation") | None` Expand source code ``` def find_installation( self, *, enterprise_id: Optional[str], team_id: Optional[str], user_id: Optional[str] = None, is_enterprise_install: Optional[bool] = False, ) -> Optional[Installation]: """Finds a relevant installation for the given IDs. If the user_id is absent, this method may return the latest installation in the workspace / org. """ raise NotImplementedError() ``` Finds a relevant installation for the given IDs. If the user\_id is absent, this method may return the latest installation in the workspace / org. `def save(self, installation: [Installation](installation_store/models/installation.html#slack_sdk.oauth.installation_store.models.installation.Installation "slack_sdk.oauth.installation_store.models.installation.Installation"))` Expand source code ``` def save(self, installation: Installation): """Saves an installation data""" raise NotImplementedError() ``` Saves an installation data `def save_bot(self, bot: [Bot](installation_store/models/bot.html#slack_sdk.oauth.installation_store.models.bot.Bot "slack_sdk.oauth.installation_store.models.bot.Bot"))` Expand source code ``` def save_bot(self, bot: Bot): """Saves a bot installation data""" raise NotImplementedError() ``` Saves a bot installation data `class OAuthStateStore` Expand source code ``` class OAuthStateStore: @property def logger(self) -> Logger: raise NotImplementedError() def issue(self, *args, **kwargs) -> str: raise NotImplementedError() def consume(self, state: str) -> bool: raise NotImplementedError() ``` ### Subclasses * [AmazonS3OAuthStateStore](state_store/amazon_s3/index.html#slack_sdk.oauth.state_store.amazon_s3.AmazonS3OAuthStateStore "slack_sdk.oauth.state_store.amazon_s3.AmazonS3OAuthStateStore") * [FileOAuthStateStore](state_store/file/index.html#slack_sdk.oauth.state_store.file.FileOAuthStateStore "slack_sdk.oauth.state_store.file.FileOAuthStateStore") * [SQLAlchemyOAuthStateStore](state_store/sqlalchemy/index.html#slack_sdk.oauth.state_store.sqlalchemy.SQLAlchemyOAuthStateStore "slack_sdk.oauth.state_store.sqlalchemy.SQLAlchemyOAuthStateStore") * [SQLite3OAuthStateStore](state_store/sqlite3/index.html#slack_sdk.oauth.state_store.sqlite3.SQLite3OAuthStateStore "slack_sdk.oauth.state_store.sqlite3.SQLite3OAuthStateStore") ### Instance variables `prop logger : logging.Logger` Expand source code ``` @property def logger(self) -> Logger: raise NotImplementedError() ``` ### Methods `def consume(self, state: str) ‑> bool` Expand source code ``` def consume(self, state: str) -> bool: raise NotImplementedError() ``` `def issue(self, *args, **kwargs) ‑> str` Expand source code ``` def issue(self, *args, **kwargs) -> str: raise NotImplementedError() ``` `class OAuthStateUtils (*, cookie_name: str = 'slack-app-oauth-state', expiration_seconds: int = 600)` Expand source code ``` class OAuthStateUtils: cookie_name: str expiration_seconds: int default_cookie_name: str = "slack-app-oauth-state" default_expiration_seconds: int = 60 * 10 # 10 minutes def __init__( self, *, cookie_name: str = default_cookie_name, expiration_seconds: int = default_expiration_seconds, ): self.cookie_name = cookie_name self.expiration_seconds = expiration_seconds def build_set_cookie_for_new_state(self, state: str) -> str: return f"{self.cookie_name}={state}; " "Secure; " "HttpOnly; " "Path=/; " f"Max-Age={self.expiration_seconds}" def build_set_cookie_for_deletion(self) -> str: return f"{self.cookie_name}=deleted; " "Secure; " "HttpOnly; " "Path=/; " "Expires=Thu, 01 Jan 1970 00:00:00 GMT" def is_valid_browser( self, state: Optional[str], request_headers: Dict[str, Union[str, Sequence[str]]], ) -> bool: if state is None or request_headers is None or request_headers.get("cookie", None) is None: return False cookies = request_headers["cookie"] if isinstance(cookies, str): cookies = [cookies] for cookie in cookies: values = cookie.split(";") for value in values: # handle quoted cookie values (e.g. due to base64 encoding) if value.strip().replace('"', "").replace("'", "") == f"{self.cookie_name}={state}": return True return False ``` ### Class variables `var cookie_name : str` The type of the None singleton. `var default_cookie_name : str` The type of the None singleton. `var default_expiration_seconds : int` The type of the None singleton. `var expiration_seconds : int` The type of the None singleton. ### Methods `def build_set_cookie_for_deletion(self) ‑> str` Expand source code ``` def build_set_cookie_for_deletion(self) -> str: return f"{self.cookie_name}=deleted; " "Secure; " "HttpOnly; " "Path=/; " "Expires=Thu, 01 Jan 1970 00:00:00 GMT" ``` `def build_set_cookie_for_new_state(self, state: str) ‑> str` Expand source code ``` def build_set_cookie_for_new_state(self, state: str) -> str: return f"{self.cookie_name}={state}; " "Secure; " "HttpOnly; " "Path=/; " f"Max-Age={self.expiration_seconds}" ``` `def is_valid_browser(self, state: str | None, request_headers: Dict[str, str | Sequence[str]]) ‑> bool` Expand source code ``` def is_valid_browser( self, state: Optional[str], request_headers: Dict[str, Union[str, Sequence[str]]], ) -> bool: if state is None or request_headers is None or request_headers.get("cookie", None) is None: return False cookies = request_headers["cookie"] if isinstance(cookies, str): cookies = [cookies] for cookie in cookies: values = cookie.split(";") for value in values: # handle quoted cookie values (e.g. due to base64 encoding) if value.strip().replace('"', "").replace("'", "") == f"{self.cookie_name}={state}": return True return False ``` `class OpenIDConnectAuthorizeUrlGenerator (*, client_id: str, redirect_uri: str, scopes: Sequence[str] | None = None, authorization_url: str = 'https://slack.com/openid/connect/authorize')` Expand source code ``` class OpenIDConnectAuthorizeUrlGenerator: """Refer to https://openid.net/specs/openid-connect-core-1_0.html""" def __init__( self, *, client_id: str, redirect_uri: str, scopes: Optional[Sequence[str]] = None, authorization_url: str = "https://slack.com/openid/connect/authorize", ): self.client_id = client_id self.redirect_uri = redirect_uri self.scopes = scopes self.authorization_url = authorization_url def generate(self, state: str, nonce: Optional[str] = None, team: Optional[str] = None) -> str: scopes = ",".join(self.scopes) if self.scopes else "" url = ( f"{self.authorization_url}?" "response_type=code&" f"state={state}&" f"client_id={self.client_id}&" f"scope={scopes}&" f"redirect_uri={self.redirect_uri}" ) if team is not None: url += f"&team={team}" if nonce is not None: url += f"&nonce={nonce}" return url ``` Refer to [https://openid.net/specs/openid-connect-core-1\_0.html](https://openid.net/specs/openid-connect-core-1_0.html) ### Methods `def generate(self, state: str, nonce: str | None = None, team: str | None = None) ‑> str` Expand source code ``` def generate(self, state: str, nonce: Optional[str] = None, team: Optional[str] = None) -> str: scopes = ",".join(self.scopes) if self.scopes else "" url = ( f"{self.authorization_url}?" "response_type=code&" f"state={state}&" f"client_id={self.client_id}&" f"scope={scopes}&" f"redirect_uri={self.redirect_uri}" ) if team is not None: url += f"&team={team}" if nonce is not None: url += f"&nonce={nonce}" return url ``` `class RedirectUriPageRenderer (*, install_path: str, redirect_uri_path: str, success_url: str | None = None, failure_url: str | None = None)` Expand source code ``` class RedirectUriPageRenderer: def __init__( self, *, install_path: str, redirect_uri_path: str, success_url: Optional[str] = None, failure_url: Optional[str] = None, ): self.install_path = install_path self.redirect_uri_path = redirect_uri_path self.success_url = success_url self.failure_url = failure_url def render_success_page( self, app_id: str, team_id: Optional[str], is_enterprise_install: Optional[bool] = None, enterprise_url: Optional[str] = None, ) -> str: url = self.success_url if url is None: if is_enterprise_install is True and enterprise_url is not None and app_id is not None: url = f"{enterprise_url}manage/organization/apps/profile/{app_id}/workspaces/add" elif team_id is None or app_id is None: url = "slack://open" else: url = f"slack://app?team={team_id}&id={app_id}" browser_url = f"https://app.slack.com/client/{team_id}" return f"""

Thank you!

Redirecting to the Slack App... click here. If you use the browser version of Slack, click this link instead.

""" # noqa: E501 def render_failure_page(self, reason: str) -> str: return f"""

Oops, Something Went Wrong!

Please try again from here or contact the app owner (reason: {html.escape(reason)})

""" # noqa: E501 ``` ### Methods `def render_failure_page(self, reason: str) ‑> str` Expand source code ``` def render_failure_page(self, reason: str) -> str: return f"""

Oops, Something Went Wrong!

Please try again from here or contact the app owner (reason: {html.escape(reason)})

""" # noqa: E501 ``` `def render_success_page(self, app_id: str, team_id: str | None, is_enterprise_install: bool | None = None, enterprise_url: str | None = None) ‑> str` Expand source code ``` def render_success_page( self, app_id: str, team_id: Optional[str], is_enterprise_install: Optional[bool] = None, enterprise_url: Optional[str] = None, ) -> str: url = self.success_url if url is None: if is_enterprise_install is True and enterprise_url is not None and app_id is not None: url = f"{enterprise_url}manage/organization/apps/profile/{app_id}/workspaces/add" elif team_id is None or app_id is None: url = "slack://open" else: url = f"slack://app?team={team_id}&id={app_id}" browser_url = f"https://app.slack.com/client/{team_id}" return f"""

Thank you!

Redirecting to the Slack App... click here. If you use the browser version of Slack, click this link instead.

""" # noqa: E501 ``` --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/oauth/authorize_url_generator # Module slack_sdk.oauth.authorize_url_generator ## Classes `class AuthorizeUrlGenerator (*, client_id: str, redirect_uri: str | None = None, scopes: Sequence[str] | None = None, user_scopes: Sequence[str] | None = None, authorization_url: str = 'https://slack.com/oauth/v2/authorize')` Expand source code ``` class AuthorizeUrlGenerator: def __init__( self, *, client_id: str, redirect_uri: Optional[str] = None, scopes: Optional[Sequence[str]] = None, user_scopes: Optional[Sequence[str]] = None, authorization_url: str = "https://slack.com/oauth/v2/authorize", ): self.client_id = client_id self.redirect_uri = redirect_uri self.scopes = scopes self.user_scopes = user_scopes self.authorization_url = authorization_url def generate(self, state: str, team: Optional[str] = None) -> str: scopes = ",".join(self.scopes) if self.scopes else "" user_scopes = ",".join(self.user_scopes) if self.user_scopes else "" url = ( f"{self.authorization_url}?" f"state={state}&" f"client_id={self.client_id}&" f"scope={scopes}&" f"user_scope={user_scopes}" ) if self.redirect_uri is not None: url += f"&redirect_uri={self.redirect_uri}" if team is not None: url += f"&team={team}" return url ``` ### Methods `def generate(self, state: str, team: str | None = None) ‑> str` Expand source code ``` def generate(self, state: str, team: Optional[str] = None) -> str: scopes = ",".join(self.scopes) if self.scopes else "" user_scopes = ",".join(self.user_scopes) if self.user_scopes else "" url = ( f"{self.authorization_url}?" f"state={state}&" f"client_id={self.client_id}&" f"scope={scopes}&" f"user_scope={user_scopes}" ) if self.redirect_uri is not None: url += f"&redirect_uri={self.redirect_uri}" if team is not None: url += f"&team={team}" return url ``` `class OpenIDConnectAuthorizeUrlGenerator (*, client_id: str, redirect_uri: str, scopes: Sequence[str] | None = None, authorization_url: str = 'https://slack.com/openid/connect/authorize')` Expand source code ``` class OpenIDConnectAuthorizeUrlGenerator: """Refer to https://openid.net/specs/openid-connect-core-1_0.html""" def __init__( self, *, client_id: str, redirect_uri: str, scopes: Optional[Sequence[str]] = None, authorization_url: str = "https://slack.com/openid/connect/authorize", ): self.client_id = client_id self.redirect_uri = redirect_uri self.scopes = scopes self.authorization_url = authorization_url def generate(self, state: str, nonce: Optional[str] = None, team: Optional[str] = None) -> str: scopes = ",".join(self.scopes) if self.scopes else "" url = ( f"{self.authorization_url}?" "response_type=code&" f"state={state}&" f"client_id={self.client_id}&" f"scope={scopes}&" f"redirect_uri={self.redirect_uri}" ) if team is not None: url += f"&team={team}" if nonce is not None: url += f"&nonce={nonce}" return url ``` Refer to [https://openid.net/specs/openid-connect-core-1\_0.html](https://openid.net/specs/openid-connect-core-1_0.html) ### Methods `def generate(self, state: str, nonce: str | None = None, team: str | None = None) ‑> str` Expand source code ``` def generate(self, state: str, nonce: Optional[str] = None, team: Optional[str] = None) -> str: scopes = ",".join(self.scopes) if self.scopes else "" url = ( f"{self.authorization_url}?" "response_type=code&" f"state={state}&" f"client_id={self.client_id}&" f"scope={scopes}&" f"redirect_uri={self.redirect_uri}" ) if team is not None: url += f"&team={team}" if nonce is not None: url += f"&nonce={nonce}" return url ``` --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/oauth/installation_store # Module slack_sdk.oauth.installation_store ## Sub-modules `[slack_sdk.oauth.installation_store.amazon_s3](amazon_s3/index.html "slack_sdk.oauth.installation_store.amazon_s3")` `[slack_sdk.oauth.installation_store.async_cacheable_installation_store](async_cacheable_installation_store.html "slack_sdk.oauth.installation_store.async_cacheable_installation_store")` `[slack_sdk.oauth.installation_store.async_installation_store](async_installation_store.html "slack_sdk.oauth.installation_store.async_installation_store")` `[slack_sdk.oauth.installation_store.cacheable_installation_store](cacheable_installation_store.html "slack_sdk.oauth.installation_store.cacheable_installation_store")` `[slack_sdk.oauth.installation_store.file](file/index.html "slack_sdk.oauth.installation_store.file")` `[slack_sdk.oauth.installation_store.installation_store](installation_store.html "slack_sdk.oauth.installation_store.installation_store")` Slack installation data store … `[slack_sdk.oauth.installation_store.internals](internals.html "slack_sdk.oauth.installation_store.internals")` `[slack_sdk.oauth.installation_store.models](models/index.html "slack_sdk.oauth.installation_store.models")` `[slack_sdk.oauth.installation_store.sqlalchemy](sqlalchemy/index.html "slack_sdk.oauth.installation_store.sqlalchemy")` `[slack_sdk.oauth.installation_store.sqlite3](sqlite3/index.html "slack_sdk.oauth.installation_store.sqlite3")` ## Classes `class Bot (*, app_id: str | None = None, enterprise_id: str | None = None, enterprise_name: str | None = None, team_id: str | None = None, team_name: str | None = None, bot_token: str, bot_id: str, bot_user_id: str, bot_scopes: str | Sequence[str] = '', bot_refresh_token: str | None = None, bot_token_expires_in: int | None = None, bot_token_expires_at: int | datetime.datetime | str | None = None, is_enterprise_install: bool | None = False, installed_at: float | datetime.datetime | str, custom_values: Dict[str, Any] | None = None)` Expand source code ``` class Bot: app_id: Optional[str] enterprise_id: Optional[str] enterprise_name: Optional[str] team_id: Optional[str] team_name: Optional[str] bot_token: str bot_id: str bot_user_id: str bot_scopes: Sequence[str] # only when token rotation is enabled bot_refresh_token: Optional[str] # only when token rotation is enabled bot_token_expires_at: Optional[int] is_enterprise_install: bool installed_at: float custom_values: Dict[str, Any] def __init__( self, *, app_id: Optional[str] = None, # org / workspace enterprise_id: Optional[str] = None, enterprise_name: Optional[str] = None, team_id: Optional[str] = None, team_name: Optional[str] = None, # bot bot_token: str, bot_id: str, bot_user_id: str, bot_scopes: Union[str, Sequence[str]] = "", # only when token rotation is enabled bot_refresh_token: Optional[str] = None, # only when token rotation is enabled bot_token_expires_in: Optional[int] = None, # only for duplicating this object # only when token rotation is enabled bot_token_expires_at: Optional[Union[int, datetime, str]] = None, is_enterprise_install: Optional[bool] = False, # timestamps # The expected value type is float but the internals handle other types too # for str values, we support only ISO datetime format. installed_at: Union[float, datetime, str], # custom values custom_values: Optional[Dict[str, Any]] = None, ): self.app_id = app_id self.enterprise_id = enterprise_id self.enterprise_name = enterprise_name self.team_id = team_id self.team_name = team_name self.bot_token = bot_token self.bot_id = bot_id self.bot_user_id = bot_user_id if isinstance(bot_scopes, str): self.bot_scopes = bot_scopes.split(",") if len(bot_scopes) > 0 else [] else: self.bot_scopes = bot_scopes self.bot_refresh_token = bot_refresh_token if bot_token_expires_at is not None: self.bot_token_expires_at = _timestamp_to_type(bot_token_expires_at, int) elif bot_token_expires_in is not None: self.bot_token_expires_at = int(time()) + bot_token_expires_in else: self.bot_token_expires_at = None self.is_enterprise_install = is_enterprise_install or False self.installed_at = _timestamp_to_type(installed_at, float) self.custom_values = custom_values if custom_values is not None else {} def set_custom_value(self, name: str, value: Any): self.custom_values[name] = value def get_custom_value(self, name: str) -> Optional[Any]: return self.custom_values.get(name) def _to_standard_value_dict(self) -> Dict[str, Any]: return { "app_id": self.app_id, "enterprise_id": self.enterprise_id, "enterprise_name": self.enterprise_name, "team_id": self.team_id, "team_name": self.team_name, "bot_token": self.bot_token, "bot_id": self.bot_id, "bot_user_id": self.bot_user_id, "bot_scopes": ",".join(self.bot_scopes) if self.bot_scopes else None, "bot_refresh_token": self.bot_refresh_token, "bot_token_expires_at": ( datetime.fromtimestamp(self.bot_token_expires_at, tz=timezone.utc) if self.bot_token_expires_at is not None else None ), "is_enterprise_install": self.is_enterprise_install, "installed_at": datetime.fromtimestamp(self.installed_at, tz=timezone.utc), } def to_dict_for_copying(self) -> Dict[str, Any]: return {"custom_values": self.custom_values, **self._to_standard_value_dict()} def to_dict(self) -> Dict[str, Any]: # prioritize standard_values over custom_values # when the same keys exist in both return {**self.custom_values, **self._to_standard_value_dict()} ``` ### Class variables `var app_id : str | None` The type of the None singleton. `var bot_id : str` The type of the None singleton. `var bot_refresh_token : str | None` The type of the None singleton. `var bot_scopes : Sequence[str]` The type of the None singleton. `var bot_token : str` The type of the None singleton. `var bot_token_expires_at : int | None` The type of the None singleton. `var bot_user_id : str` The type of the None singleton. `var custom_values : Dict[str, Any]` The type of the None singleton. `var enterprise_id : str | None` The type of the None singleton. `var enterprise_name : str | None` The type of the None singleton. `var installed_at : float` The type of the None singleton. `var is_enterprise_install : bool` The type of the None singleton. `var team_id : str | None` The type of the None singleton. `var team_name : str | None` The type of the None singleton. ### Methods `def get_custom_value(self, name: str) ‑> Any | None` Expand source code ``` def get_custom_value(self, name: str) -> Optional[Any]: return self.custom_values.get(name) ``` `def set_custom_value(self, name: str, value: Any)` Expand source code ``` def set_custom_value(self, name: str, value: Any): self.custom_values[name] = value ``` `def to_dict(self) ‑> Dict[str, Any]` Expand source code ``` def to_dict(self) -> Dict[str, Any]: # prioritize standard_values over custom_values # when the same keys exist in both return {**self.custom_values, **self._to_standard_value_dict()} ``` `def to_dict_for_copying(self) ‑> Dict[str, Any]` Expand source code ``` def to_dict_for_copying(self) -> Dict[str, Any]: return {"custom_values": self.custom_values, **self._to_standard_value_dict()} ``` `class FileInstallationStore (*, base_dir: str = '$HOME/.bolt-app-installation', historical_data_enabled: bool = True, client_id: str | None = None, logger: logging.Logger = )` Expand source code ``` class FileInstallationStore(InstallationStore, AsyncInstallationStore): def __init__( self, *, base_dir: str = str(Path.home()) + "/.bolt-app-installation", historical_data_enabled: bool = True, client_id: Optional[str] = None, logger: Logger = logging.getLogger(__name__), ): self.base_dir = base_dir self.historical_data_enabled = historical_data_enabled self.client_id = client_id if self.client_id is not None: self.base_dir = f"{self.base_dir}/{self.client_id}" self._logger = logger @property def logger(self) -> Logger: if self._logger is None: self._logger = logging.getLogger(__name__) return self._logger async def async_save(self, installation: Installation): return self.save(installation) async def async_save_bot(self, bot: Bot): return self.save_bot(bot) def save(self, installation: Installation): none = "none" e_id = installation.enterprise_id or none t_id = installation.team_id or none team_installation_dir = f"{self.base_dir}/{e_id}-{t_id}" self._mkdir(team_installation_dir) self.save_bot(installation.to_bot()) if self.historical_data_enabled: history_version: str = str(installation.installed_at) # per workspace entity: str = json.dumps(installation.__dict__) with open(f"{team_installation_dir}/installer-latest", "w") as f: f.write(entity) with open(f"{team_installation_dir}/installer-{history_version}", "w") as f: f.write(entity) # per workspace per user u_id = installation.user_id or none entity = json.dumps(installation.__dict__) with open(f"{team_installation_dir}/installer-{u_id}-latest", "w") as f: f.write(entity) with open(f"{team_installation_dir}/installer-{u_id}-{history_version}", "w") as f: f.write(entity) else: u_id = installation.user_id or none installer_filepath = f"{team_installation_dir}/installer-{u_id}-latest" with open(installer_filepath, "w") as f: entity = json.dumps(installation.__dict__) f.write(entity) def save_bot(self, bot: Bot): if bot.bot_token is None: self.logger.debug("Skipped saving a new row because of the absence of bot token in it") return none = "none" e_id = bot.enterprise_id or none t_id = bot.team_id or none team_installation_dir = f"{self.base_dir}/{e_id}-{t_id}" self._mkdir(team_installation_dir) if self.historical_data_enabled: history_version: str = str(bot.installed_at) entity: str = json.dumps(bot.__dict__) with open(f"{team_installation_dir}/bot-latest", "w") as f: f.write(entity) with open(f"{team_installation_dir}/bot-{history_version}", "w") as f: f.write(entity) else: with open(f"{team_installation_dir}/bot-latest", "w") as f: entity = json.dumps(bot.__dict__) f.write(entity) async def async_find_bot( self, *, enterprise_id: Optional[str], team_id: Optional[str], is_enterprise_install: Optional[bool] = False, ) -> Optional[Bot]: return self.find_bot( enterprise_id=enterprise_id, team_id=team_id, is_enterprise_install=is_enterprise_install, ) def find_bot( self, *, enterprise_id: Optional[str], team_id: Optional[str], is_enterprise_install: Optional[bool] = False, ) -> Optional[Bot]: none = "none" e_id = enterprise_id or none t_id = team_id or none if is_enterprise_install: t_id = none bot_filepath = f"{self.base_dir}/{e_id}-{t_id}/bot-latest" try: with open(bot_filepath) as f: data = json.loads(f.read()) return Bot(**data) except FileNotFoundError as e: message = f"Installation data missing for enterprise: {e_id}, team: {t_id}: {e}" self.logger.debug(message) return None async def async_find_installation( self, *, enterprise_id: Optional[str], team_id: Optional[str], user_id: Optional[str] = None, is_enterprise_install: Optional[bool] = False, ) -> Optional[Installation]: return self.find_installation( enterprise_id=enterprise_id, team_id=team_id, user_id=user_id, is_enterprise_install=is_enterprise_install, ) def find_installation( self, *, enterprise_id: Optional[str], team_id: Optional[str], user_id: Optional[str] = None, is_enterprise_install: Optional[bool] = False, ) -> Optional[Installation]: none = "none" e_id = enterprise_id or none t_id = team_id or none if is_enterprise_install: t_id = none installation_filepath = f"{self.base_dir}/{e_id}-{t_id}/installer-latest" if user_id is not None: installation_filepath = f"{self.base_dir}/{e_id}-{t_id}/installer-{user_id}-latest" try: installation: Optional[Installation] = None with open(installation_filepath) as f: data = json.loads(f.read()) installation = Installation(**data) has_user_installation = user_id is not None and installation is not None no_bot_token_installation = installation is not None and installation.bot_token is None should_find_bot_installation = has_user_installation or no_bot_token_installation if should_find_bot_installation: # Retrieve the latest bot token, just in case # See also: https://github.com/slackapi/bolt-python/issues/664 latest_bot_installation = self.find_bot( enterprise_id=enterprise_id, team_id=team_id, is_enterprise_install=is_enterprise_install, ) if latest_bot_installation is not None and installation.bot_token != latest_bot_installation.bot_token: # NOTE: this logic is based on the assumption that every single installation has bot scopes # If you need to installation patterns without bot scopes in the same S3 bucket, # please fork this code and implement your own logic. installation.bot_id = latest_bot_installation.bot_id installation.bot_user_id = latest_bot_installation.bot_user_id installation.bot_token = latest_bot_installation.bot_token installation.bot_scopes = latest_bot_installation.bot_scopes installation.bot_refresh_token = latest_bot_installation.bot_refresh_token installation.bot_token_expires_at = latest_bot_installation.bot_token_expires_at return installation except FileNotFoundError as e: message = f"Installation data missing for enterprise: {e_id}, team: {t_id}: {e}" self.logger.debug(message) return None async def async_delete_bot(self, *, enterprise_id: Optional[str], team_id: Optional[str]) -> None: return self.delete_bot(enterprise_id=enterprise_id, team_id=team_id) def delete_bot(self, *, enterprise_id: Optional[str], team_id: Optional[str]) -> None: none = "none" e_id = enterprise_id or none t_id = team_id or none filepath_glob = f"{self.base_dir}/{e_id}-{t_id}/bot-*" self._delete_by_glob(e_id, t_id, filepath_glob) async def async_delete_installation( self, *, enterprise_id: Optional[str], team_id: Optional[str], user_id: Optional[str] = None, ) -> None: return self.delete_installation(enterprise_id=enterprise_id, team_id=team_id, user_id=user_id) def delete_installation( self, *, enterprise_id: Optional[str], team_id: Optional[str], user_id: Optional[str] = None, ) -> None: none = "none" e_id = enterprise_id or none t_id = team_id or none if user_id is not None: filepath_glob = f"{self.base_dir}/{e_id}-{t_id}/installer-{user_id}-*" else: filepath_glob = f"{self.base_dir}/{e_id}-{t_id}/installer-*" self._delete_by_glob(e_id, t_id, filepath_glob) def _delete_by_glob(self, e_id: str, t_id: str, filepath_glob: str): for filepath in glob.glob(filepath_glob): try: os.remove(filepath) except FileNotFoundError as e: message = f"Failed to delete installation data for enterprise: {e_id}, team: {t_id}: {e}" self.logger.warning(message) @staticmethod def _mkdir(path: Union[str, Path]): if isinstance(path, str): path = Path(path) path.mkdir(parents=True, exist_ok=True) ``` The installation store interface. The minimum required methods are: * save(installation) * find\_installation(enterprise\_id, team\_id, user\_id, is\_enterprise\_install) If you would like to properly handle app uninstallations and token revocations, the following methods should be implemented. * delete\_installation(enterprise\_id, team\_id, user\_id) * delete\_all(enterprise\_id, team\_id) If your app needs only bot scope installations, the simpler way to implement would be: * save(installation) * find\_bot(enterprise\_id, team\_id, is\_enterprise\_install) * delete\_bot(enterprise\_id, team\_id) * delete\_all(enterprise\_id, team\_id) ### Ancestors * [InstallationStore](installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore "slack_sdk.oauth.installation_store.installation_store.InstallationStore") * [AsyncInstallationStore](async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore") ### Instance variables `prop logger : logging.Logger` Expand source code ``` @property def logger(self) -> Logger: if self._logger is None: self._logger = logging.getLogger(__name__) return self._logger ``` ### Inherited members * `**[InstallationStore](installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore "slack_sdk.oauth.installation_store.installation_store.InstallationStore")**`: * `[save](installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.save "slack_sdk.oauth.installation_store.installation_store.InstallationStore.save")` * `[save_bot](installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.save_bot "slack_sdk.oauth.installation_store.installation_store.InstallationStore.save_bot")` * `**[InstallationStore](installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore "slack_sdk.oauth.installation_store.installation_store.InstallationStore")**`: * `[find_bot](installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.find_bot "slack_sdk.oauth.installation_store.installation_store.InstallationStore.find_bot")` * `**[InstallationStore](installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore "slack_sdk.oauth.installation_store.installation_store.InstallationStore")**`: * `[find_installation](installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.find_installation "slack_sdk.oauth.installation_store.installation_store.InstallationStore.find_installation")` * `**[InstallationStore](installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore "slack_sdk.oauth.installation_store.installation_store.InstallationStore")**`: * `[delete_bot](installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.delete_bot "slack_sdk.oauth.installation_store.installation_store.InstallationStore.delete_bot")` * `**[InstallationStore](installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore "slack_sdk.oauth.installation_store.installation_store.InstallationStore")**`: * `[delete_all](installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.delete_all "slack_sdk.oauth.installation_store.installation_store.InstallationStore.delete_all")` * `[delete_installation](installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.delete_installation "slack_sdk.oauth.installation_store.installation_store.InstallationStore.delete_installation")` * `**[AsyncInstallationStore](async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore")**`: * `[async_save](async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_save "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_save")` * `[async_save_bot](async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_save_bot "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_save_bot")` * `**[AsyncInstallationStore](async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore")**`: * `[async_find_bot](async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_find_bot "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_find_bot")` * `**[AsyncInstallationStore](async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore")**`: * `[async_find_installation](async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_find_installation "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_find_installation")` * `**[AsyncInstallationStore](async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore")**`: * `[async_delete_bot](async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_delete_bot "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_delete_bot")` * `**[AsyncInstallationStore](async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore")**`: * `[async_delete_installation](async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_delete_installation "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_delete_installation")` * `**[AsyncInstallationStore](async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore")**`: * `[async_delete_all](async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_delete_all "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_delete_all")` `class Installation (*, app_id: str | None = None, enterprise_id: str | None = None, enterprise_name: str | None = None, enterprise_url: str | None = None, team_id: str | None = None, team_name: str | None = None, bot_token: str | None = None, bot_id: str | None = None, bot_user_id: str | None = None, bot_scopes: str | Sequence[str] = '', bot_refresh_token: str | None = None, bot_token_expires_in: int | None = None, bot_token_expires_at: int | datetime.datetime | str | None = None, user_id: str, user_token: str | None = None, user_scopes: str | Sequence[str] = '', user_refresh_token: str | None = None, user_token_expires_in: int | None = None, user_token_expires_at: int | datetime.datetime | str | None = None, incoming_webhook_url: str | None = None, incoming_webhook_channel: str | None = None, incoming_webhook_channel_id: str | None = None, incoming_webhook_configuration_url: str | None = None, is_enterprise_install: bool | None = False, token_type: str | None = None, installed_at: float | datetime.datetime | str | None = None, custom_values: Dict[str, Any] | None = None)` Expand source code ``` class Installation: app_id: Optional[str] enterprise_id: Optional[str] enterprise_name: Optional[str] enterprise_url: Optional[str] team_id: Optional[str] team_name: Optional[str] bot_token: Optional[str] bot_id: Optional[str] bot_user_id: Optional[str] bot_scopes: Optional[Sequence[str]] bot_refresh_token: Optional[str] # only when token rotation is enabled # only when token rotation is enabled # Unix time (seconds): only when token rotation is enabled bot_token_expires_at: Optional[int] user_id: str user_token: Optional[str] user_scopes: Optional[Sequence[str]] user_refresh_token: Optional[str] # only when token rotation is enabled # Unix time (seconds): only when token rotation is enabled user_token_expires_at: Optional[int] incoming_webhook_url: Optional[str] incoming_webhook_channel: Optional[str] incoming_webhook_channel_id: Optional[str] incoming_webhook_configuration_url: Optional[str] is_enterprise_install: bool token_type: Optional[str] installed_at: float custom_values: Dict[str, Any] def __init__( self, *, app_id: Optional[str] = None, # org / workspace enterprise_id: Optional[str] = None, enterprise_name: Optional[str] = None, enterprise_url: Optional[str] = None, team_id: Optional[str] = None, team_name: Optional[str] = None, # bot bot_token: Optional[str] = None, bot_id: Optional[str] = None, bot_user_id: Optional[str] = None, bot_scopes: Union[str, Sequence[str]] = "", bot_refresh_token: Optional[str] = None, # only when token rotation is enabled # only when token rotation is enabled bot_token_expires_in: Optional[int] = None, # only for duplicating this object # only when token rotation is enabled bot_token_expires_at: Optional[Union[int, datetime, str]] = None, # installer user_id: str, user_token: Optional[str] = None, user_scopes: Union[str, Sequence[str]] = "", user_refresh_token: Optional[str] = None, # only when token rotation is enabled # only when token rotation is enabled user_token_expires_in: Optional[int] = None, # only for duplicating this object # only when token rotation is enabled user_token_expires_at: Optional[Union[int, datetime, str]] = None, # incoming webhook incoming_webhook_url: Optional[str] = None, incoming_webhook_channel: Optional[str] = None, incoming_webhook_channel_id: Optional[str] = None, incoming_webhook_configuration_url: Optional[str] = None, # org app is_enterprise_install: Optional[bool] = False, token_type: Optional[str] = None, # timestamps # The expected value type is float but the internals handle other types too # for str values, we supports only ISO datetime format. installed_at: Optional[Union[float, datetime, str]] = None, # custom values custom_values: Optional[Dict[str, Any]] = None, ): self.app_id = app_id self.enterprise_id = enterprise_id self.enterprise_name = enterprise_name self.enterprise_url = enterprise_url self.team_id = team_id self.team_name = team_name self.bot_token = bot_token self.bot_id = bot_id self.bot_user_id = bot_user_id if isinstance(bot_scopes, str): self.bot_scopes = bot_scopes.split(",") if len(bot_scopes) > 0 else [] else: self.bot_scopes = bot_scopes self.bot_refresh_token = bot_refresh_token if bot_token_expires_at is not None: self.bot_token_expires_at = _timestamp_to_type(bot_token_expires_at, int) elif bot_token_expires_in is not None: self.bot_token_expires_at = int(time()) + bot_token_expires_in else: self.bot_token_expires_at = None self.user_id = user_id self.user_token = user_token if isinstance(user_scopes, str): self.user_scopes = user_scopes.split(",") if len(user_scopes) > 0 else [] else: self.user_scopes = user_scopes self.user_refresh_token = user_refresh_token if user_token_expires_at is not None: self.user_token_expires_at = _timestamp_to_type(user_token_expires_at, int) elif user_token_expires_in is not None: self.user_token_expires_at = int(time()) + user_token_expires_in else: self.user_token_expires_at = None self.incoming_webhook_url = incoming_webhook_url self.incoming_webhook_channel = incoming_webhook_channel self.incoming_webhook_channel_id = incoming_webhook_channel_id self.incoming_webhook_configuration_url = incoming_webhook_configuration_url self.is_enterprise_install = is_enterprise_install or False self.token_type = token_type if installed_at is None: self.installed_at = datetime.now().timestamp() else: self.installed_at = _timestamp_to_type(installed_at, float) self.custom_values = custom_values if custom_values is not None else {} def to_bot(self) -> Bot: return Bot( app_id=self.app_id, enterprise_id=self.enterprise_id, enterprise_name=self.enterprise_name, team_id=self.team_id, team_name=self.team_name, bot_token=self.bot_token, # type: ignore[arg-type] bot_id=self.bot_id, # type: ignore[arg-type] bot_user_id=self.bot_user_id, # type: ignore[arg-type] bot_scopes=self.bot_scopes, # type: ignore[arg-type] bot_refresh_token=self.bot_refresh_token, bot_token_expires_at=self.bot_token_expires_at, is_enterprise_install=self.is_enterprise_install, installed_at=self.installed_at, custom_values=self.custom_values, ) def set_custom_value(self, name: str, value: Any): self.custom_values[name] = value def get_custom_value(self, name: str) -> Optional[Any]: return self.custom_values.get(name) def _to_standard_value_dict(self) -> Dict[str, Any]: return { "app_id": self.app_id, "enterprise_id": self.enterprise_id, "enterprise_name": self.enterprise_name, "enterprise_url": self.enterprise_url, "team_id": self.team_id, "team_name": self.team_name, "bot_token": self.bot_token, "bot_id": self.bot_id, "bot_user_id": self.bot_user_id, "bot_scopes": ",".join(self.bot_scopes) if self.bot_scopes else None, "bot_refresh_token": self.bot_refresh_token, "bot_token_expires_at": ( datetime.fromtimestamp(self.bot_token_expires_at, tz=timezone.utc) if self.bot_token_expires_at is not None else None ), "user_id": self.user_id, "user_token": self.user_token, "user_scopes": ",".join(self.user_scopes) if self.user_scopes else None, "user_refresh_token": self.user_refresh_token, "user_token_expires_at": ( datetime.fromtimestamp(self.user_token_expires_at, tz=timezone.utc) if self.user_token_expires_at is not None else None ), "incoming_webhook_url": self.incoming_webhook_url, "incoming_webhook_channel": self.incoming_webhook_channel, "incoming_webhook_channel_id": self.incoming_webhook_channel_id, "incoming_webhook_configuration_url": self.incoming_webhook_configuration_url, "is_enterprise_install": self.is_enterprise_install, "token_type": self.token_type, "installed_at": datetime.fromtimestamp(self.installed_at, tz=timezone.utc), } def to_dict_for_copying(self) -> Dict[str, Any]: return {"custom_values": self.custom_values, **self._to_standard_value_dict()} def to_dict(self) -> Dict[str, Any]: # prioritize standard_values over custom_values # when the same keys exist in both return {**self.custom_values, **self._to_standard_value_dict()} ``` ### Class variables `var app_id : str | None` The type of the None singleton. `var bot_id : str | None` The type of the None singleton. `var bot_refresh_token : str | None` The type of the None singleton. `var bot_scopes : Sequence[str] | None` The type of the None singleton. `var bot_token : str | None` The type of the None singleton. `var bot_token_expires_at : int | None` The type of the None singleton. `var bot_user_id : str | None` The type of the None singleton. `var custom_values : Dict[str, Any]` The type of the None singleton. `var enterprise_id : str | None` The type of the None singleton. `var enterprise_name : str | None` The type of the None singleton. `var enterprise_url : str | None` The type of the None singleton. `var incoming_webhook_channel : str | None` The type of the None singleton. `var incoming_webhook_channel_id : str | None` The type of the None singleton. `var incoming_webhook_configuration_url : str | None` The type of the None singleton. `var incoming_webhook_url : str | None` The type of the None singleton. `var installed_at : float` The type of the None singleton. `var is_enterprise_install : bool` The type of the None singleton. `var team_id : str | None` The type of the None singleton. `var team_name : str | None` The type of the None singleton. `var token_type : str | None` The type of the None singleton. `var user_id : str` The type of the None singleton. `var user_refresh_token : str | None` The type of the None singleton. `var user_scopes : Sequence[str] | None` The type of the None singleton. `var user_token : str | None` The type of the None singleton. `var user_token_expires_at : int | None` The type of the None singleton. ### Methods `def get_custom_value(self, name: str) ‑> Any | None` Expand source code ``` def get_custom_value(self, name: str) -> Optional[Any]: return self.custom_values.get(name) ``` `def set_custom_value(self, name: str, value: Any)` Expand source code ``` def set_custom_value(self, name: str, value: Any): self.custom_values[name] = value ``` `def to_bot(self) ‑> [Bot](models/bot.html#slack_sdk.oauth.installation_store.models.bot.Bot "slack_sdk.oauth.installation_store.models.bot.Bot")` Expand source code ``` def to_bot(self) -> Bot: return Bot( app_id=self.app_id, enterprise_id=self.enterprise_id, enterprise_name=self.enterprise_name, team_id=self.team_id, team_name=self.team_name, bot_token=self.bot_token, # type: ignore[arg-type] bot_id=self.bot_id, # type: ignore[arg-type] bot_user_id=self.bot_user_id, # type: ignore[arg-type] bot_scopes=self.bot_scopes, # type: ignore[arg-type] bot_refresh_token=self.bot_refresh_token, bot_token_expires_at=self.bot_token_expires_at, is_enterprise_install=self.is_enterprise_install, installed_at=self.installed_at, custom_values=self.custom_values, ) ``` `def to_dict(self) ‑> Dict[str, Any]` Expand source code ``` def to_dict(self) -> Dict[str, Any]: # prioritize standard_values over custom_values # when the same keys exist in both return {**self.custom_values, **self._to_standard_value_dict()} ``` `def to_dict_for_copying(self) ‑> Dict[str, Any]` Expand source code ``` def to_dict_for_copying(self) -> Dict[str, Any]: return {"custom_values": self.custom_values, **self._to_standard_value_dict()} ``` `class InstallationStore` Expand source code ``` class InstallationStore: """The installation store interface. The minimum required methods are: * save(installation) * find_installation(enterprise_id, team_id, user_id, is_enterprise_install) If you would like to properly handle app uninstallations and token revocations, the following methods should be implemented. * delete_installation(enterprise_id, team_id, user_id) * delete_all(enterprise_id, team_id) If your app needs only bot scope installations, the simpler way to implement would be: * save(installation) * find_bot(enterprise_id, team_id, is_enterprise_install) * delete_bot(enterprise_id, team_id) * delete_all(enterprise_id, team_id) """ @property def logger(self) -> Logger: raise NotImplementedError() def save(self, installation: Installation): """Saves an installation data""" raise NotImplementedError() def save_bot(self, bot: Bot): """Saves a bot installation data""" raise NotImplementedError() def find_bot( self, *, enterprise_id: Optional[str], team_id: Optional[str], is_enterprise_install: Optional[bool] = False, ) -> Optional[Bot]: """Finds a bot scope installation per workspace / org""" raise NotImplementedError() def find_installation( self, *, enterprise_id: Optional[str], team_id: Optional[str], user_id: Optional[str] = None, is_enterprise_install: Optional[bool] = False, ) -> Optional[Installation]: """Finds a relevant installation for the given IDs. If the user_id is absent, this method may return the latest installation in the workspace / org. """ raise NotImplementedError() def delete_bot( self, *, enterprise_id: Optional[str], team_id: Optional[str], ) -> None: """Deletes a bot scope installation per workspace / org""" raise NotImplementedError() def delete_installation( self, *, enterprise_id: Optional[str], team_id: Optional[str], user_id: Optional[str] = None, ) -> None: """Deletes an installation that matches the given IDs""" raise NotImplementedError() def delete_all( self, *, enterprise_id: Optional[str], team_id: Optional[str], ): """Deletes all installation data for the given workspace / org""" self.delete_bot(enterprise_id=enterprise_id, team_id=team_id) self.delete_installation(enterprise_id=enterprise_id, team_id=team_id) ``` The installation store interface. The minimum required methods are: * save(installation) * find\_installation(enterprise\_id, team\_id, user\_id, is\_enterprise\_install) If you would like to properly handle app uninstallations and token revocations, the following methods should be implemented. * delete\_installation(enterprise\_id, team\_id, user\_id) * delete\_all(enterprise\_id, team\_id) If your app needs only bot scope installations, the simpler way to implement would be: * save(installation) * find\_bot(enterprise\_id, team\_id, is\_enterprise\_install) * delete\_bot(enterprise\_id, team\_id) * delete\_all(enterprise\_id, team\_id) ### Subclasses * [AmazonS3InstallationStore](amazon_s3/index.html#slack_sdk.oauth.installation_store.amazon_s3.AmazonS3InstallationStore "slack_sdk.oauth.installation_store.amazon_s3.AmazonS3InstallationStore") * [CacheableInstallationStore](cacheable_installation_store.html#slack_sdk.oauth.installation_store.cacheable_installation_store.CacheableInstallationStore "slack_sdk.oauth.installation_store.cacheable_installation_store.CacheableInstallationStore") * [FileInstallationStore](file/index.html#slack_sdk.oauth.installation_store.file.FileInstallationStore "slack_sdk.oauth.installation_store.file.FileInstallationStore") * [SQLAlchemyInstallationStore](sqlalchemy/index.html#slack_sdk.oauth.installation_store.sqlalchemy.SQLAlchemyInstallationStore "slack_sdk.oauth.installation_store.sqlalchemy.SQLAlchemyInstallationStore") * [SQLite3InstallationStore](sqlite3/index.html#slack_sdk.oauth.installation_store.sqlite3.SQLite3InstallationStore "slack_sdk.oauth.installation_store.sqlite3.SQLite3InstallationStore") ### Instance variables `prop logger : logging.Logger` Expand source code ``` @property def logger(self) -> Logger: raise NotImplementedError() ``` ### Methods `def delete_all(self, *, enterprise_id: str | None, team_id: str | None)` Expand source code ``` def delete_all( self, *, enterprise_id: Optional[str], team_id: Optional[str], ): """Deletes all installation data for the given workspace / org""" self.delete_bot(enterprise_id=enterprise_id, team_id=team_id) self.delete_installation(enterprise_id=enterprise_id, team_id=team_id) ``` Deletes all installation data for the given workspace / org `def delete_bot(self, *, enterprise_id: str | None, team_id: str | None) ‑> None` Expand source code ``` def delete_bot( self, *, enterprise_id: Optional[str], team_id: Optional[str], ) -> None: """Deletes a bot scope installation per workspace / org""" raise NotImplementedError() ``` Deletes a bot scope installation per workspace / org `def delete_installation(self, *, enterprise_id: str | None, team_id: str | None, user_id: str | None = None) ‑> None` Expand source code ``` def delete_installation( self, *, enterprise_id: Optional[str], team_id: Optional[str], user_id: Optional[str] = None, ) -> None: """Deletes an installation that matches the given IDs""" raise NotImplementedError() ``` Deletes an installation that matches the given IDs `def find_bot(self, *, enterprise_id: str | None, team_id: str | None, is_enterprise_install: bool | None = False) ‑> [Bot](models/bot.html#slack_sdk.oauth.installation_store.models.bot.Bot "slack_sdk.oauth.installation_store.models.bot.Bot") | None` Expand source code ``` def find_bot( self, *, enterprise_id: Optional[str], team_id: Optional[str], is_enterprise_install: Optional[bool] = False, ) -> Optional[Bot]: """Finds a bot scope installation per workspace / org""" raise NotImplementedError() ``` Finds a bot scope installation per workspace / org `def find_installation(self, *, enterprise_id: str | None, team_id: str | None, user_id: str | None = None, is_enterprise_install: bool | None = False) ‑> [Installation](models/installation.html#slack_sdk.oauth.installation_store.models.installation.Installation "slack_sdk.oauth.installation_store.models.installation.Installation") | None` Expand source code ``` def find_installation( self, *, enterprise_id: Optional[str], team_id: Optional[str], user_id: Optional[str] = None, is_enterprise_install: Optional[bool] = False, ) -> Optional[Installation]: """Finds a relevant installation for the given IDs. If the user_id is absent, this method may return the latest installation in the workspace / org. """ raise NotImplementedError() ``` Finds a relevant installation for the given IDs. If the user\_id is absent, this method may return the latest installation in the workspace / org. `def save(self, installation: [Installation](models/installation.html#slack_sdk.oauth.installation_store.models.installation.Installation "slack_sdk.oauth.installation_store.models.installation.Installation"))` Expand source code ``` def save(self, installation: Installation): """Saves an installation data""" raise NotImplementedError() ``` Saves an installation data `def save_bot(self, bot: [Bot](models/bot.html#slack_sdk.oauth.installation_store.models.bot.Bot "slack_sdk.oauth.installation_store.models.bot.Bot"))` Expand source code ``` def save_bot(self, bot: Bot): """Saves a bot installation data""" raise NotImplementedError() ``` Saves a bot installation data --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/oauth/installation_store/amazon_s3 # Module slack_sdk.oauth.installation_store.amazon_s3 ## Classes `class AmazonS3InstallationStore (*, s3_client: botocore.client.BaseClient, bucket_name: str, client_id: str, historical_data_enabled: bool = True, logger: logging.Logger = )` Expand source code ``` class AmazonS3InstallationStore(InstallationStore, AsyncInstallationStore): def __init__( self, *, s3_client: BaseClient, bucket_name: str, client_id: str, historical_data_enabled: bool = True, logger: Logger = logging.getLogger(__name__), ): self.s3_client = s3_client self.bucket_name = bucket_name self.historical_data_enabled = historical_data_enabled self.client_id = client_id self._logger = logger @property def logger(self) -> Logger: if self._logger is None: self._logger = logging.getLogger(__name__) return self._logger async def async_save(self, installation: Installation): return self.save(installation) async def async_save_bot(self, bot: Bot): return self.save_bot(bot) def save(self, installation: Installation): none = "none" e_id = installation.enterprise_id or none t_id = installation.team_id or none workspace_path = f"{self.client_id}/{e_id}-{t_id}" self.save_bot(installation.to_bot()) if self.historical_data_enabled: history_version: str = str(installation.installed_at) # per workspace entity: str = json.dumps(installation.__dict__) response = self.s3_client.put_object( Bucket=self.bucket_name, Body=entity, Key=f"{workspace_path}/installer-latest", ) self.logger.debug(f"S3 put_object response: {response}") response = self.s3_client.put_object( Bucket=self.bucket_name, Body=entity, Key=f"{workspace_path}/installer-{history_version}", ) self.logger.debug(f"S3 put_object response: {response}") # per workspace per user u_id = installation.user_id or none entity = json.dumps(installation.__dict__) response = self.s3_client.put_object( Bucket=self.bucket_name, Body=entity, Key=f"{workspace_path}/installer-{u_id}-latest", ) self.logger.debug(f"S3 put_object response: {response}") response = self.s3_client.put_object( Bucket=self.bucket_name, Body=entity, Key=f"{workspace_path}/installer-{u_id}-{history_version}", ) self.logger.debug(f"S3 put_object response: {response}") else: # per workspace entity = json.dumps(installation.__dict__) response = self.s3_client.put_object( Bucket=self.bucket_name, Body=entity, Key=f"{workspace_path}/installer-latest", ) self.logger.debug(f"S3 put_object response: {response}") # per workspace per user u_id = installation.user_id or none entity = json.dumps(installation.__dict__) response = self.s3_client.put_object( Bucket=self.bucket_name, Body=entity, Key=f"{workspace_path}/installer-{u_id}-latest", ) self.logger.debug(f"S3 put_object response: {response}") def save_bot(self, bot: Bot): if bot.bot_token is None: self.logger.debug("Skipped saving a new row because of the absence of bot token in it") return none = "none" e_id = bot.enterprise_id or none t_id = bot.team_id or none workspace_path = f"{self.client_id}/{e_id}-{t_id}" if self.historical_data_enabled: history_version: str = str(bot.installed_at) entity: str = json.dumps(bot.__dict__) response = self.s3_client.put_object( Bucket=self.bucket_name, Body=entity, Key=f"{workspace_path}/bot-latest", ) self.logger.debug(f"S3 put_object response: {response}") response = self.s3_client.put_object( Bucket=self.bucket_name, Body=entity, Key=f"{workspace_path}/bot-{history_version}", ) self.logger.debug(f"S3 put_object response: {response}") else: entity = json.dumps(bot.__dict__) response = self.s3_client.put_object( Bucket=self.bucket_name, Body=entity, Key=f"{workspace_path}/bot-latest", ) self.logger.debug(f"S3 put_object response: {response}") async def async_find_bot( self, *, enterprise_id: Optional[str], team_id: Optional[str], is_enterprise_install: Optional[bool] = False, ) -> Optional[Bot]: return self.find_bot( enterprise_id=enterprise_id, team_id=team_id, is_enterprise_install=is_enterprise_install, ) def find_bot( self, *, enterprise_id: Optional[str], team_id: Optional[str], is_enterprise_install: Optional[bool] = False, ) -> Optional[Bot]: none = "none" e_id = enterprise_id or none t_id = team_id or none if is_enterprise_install: t_id = none workspace_path = f"{self.client_id}/{e_id}-{t_id}" try: fetch_response = self.s3_client.get_object( Bucket=self.bucket_name, Key=f"{workspace_path}/bot-latest", ) self.logger.debug(f"S3 get_object response: {fetch_response}") body = fetch_response["Body"].read().decode("utf-8") data = json.loads(body) return Bot(**data) except Exception as e: message = f"Failed to find bot installation data for enterprise: {e_id}, team: {t_id}: {e}" self.logger.warning(message) return None async def async_find_installation( self, *, enterprise_id: Optional[str], team_id: Optional[str], user_id: Optional[str] = None, is_enterprise_install: Optional[bool] = False, ) -> Optional[Installation]: return self.find_installation( enterprise_id=enterprise_id, team_id=team_id, user_id=user_id, is_enterprise_install=is_enterprise_install, ) def find_installation( self, *, enterprise_id: Optional[str], team_id: Optional[str], user_id: Optional[str] = None, is_enterprise_install: Optional[bool] = False, ) -> Optional[Installation]: none = "none" e_id = enterprise_id or none t_id = team_id or none if is_enterprise_install: t_id = none workspace_path = f"{self.client_id}/{e_id}-{t_id}" try: key = f"{workspace_path}/installer-{user_id}-latest" if user_id else f"{workspace_path}/installer-latest" fetch_response = self.s3_client.get_object( Bucket=self.bucket_name, Key=key, ) self.logger.debug(f"S3 get_object response: {fetch_response}") body = fetch_response["Body"].read().decode("utf-8") data = json.loads(body) installation = Installation(**data) has_user_installation = user_id is not None and installation is not None no_bot_token_installation = installation is not None and installation.bot_token is None should_find_bot_installation = has_user_installation or no_bot_token_installation if should_find_bot_installation: # Retrieve the latest bot token, just in case # See also: https://github.com/slackapi/bolt-python/issues/664 latest_bot_installation = self.find_bot( enterprise_id=enterprise_id, team_id=team_id, is_enterprise_install=is_enterprise_install, ) if latest_bot_installation is not None and installation.bot_token != latest_bot_installation.bot_token: # NOTE: this logic is based on the assumption that every single installation has bot scopes # If you need to installation patterns without bot scopes in the same S3 bucket, # please fork this code and implement your own logic. installation.bot_id = latest_bot_installation.bot_id installation.bot_user_id = latest_bot_installation.bot_user_id installation.bot_token = latest_bot_installation.bot_token installation.bot_scopes = latest_bot_installation.bot_scopes installation.bot_refresh_token = latest_bot_installation.bot_refresh_token installation.bot_token_expires_at = latest_bot_installation.bot_token_expires_at return installation except Exception as e: message = f"Failed to find an installation data for enterprise: {e_id}, team: {t_id}: {e}" self.logger.warning(message) return None async def async_delete_bot(self, *, enterprise_id: Optional[str], team_id: Optional[str]) -> None: return self.delete_bot( enterprise_id=enterprise_id, team_id=team_id, ) def delete_bot(self, *, enterprise_id: Optional[str], team_id: Optional[str]) -> None: none = "none" e_id = enterprise_id or none t_id = team_id or none workspace_path = f"{self.client_id}/{e_id}-{t_id}" objects = self.s3_client.list_objects( Bucket=self.bucket_name, Prefix=f"{workspace_path}/bot-", ) for content in objects.get("Contents", []): key = content.get("Key") if key is not None: self.logger.info(f"Going to delete bot installation ({key})") try: self.s3_client.delete_object( Bucket=self.bucket_name, Key=content.get("Key"), ) except Exception as e: message = f"Failed to delete bot installation data for enterprise: {e_id}, team: {t_id}: {e}" raise SlackClientConfigurationError(message) async def async_delete_installation( self, *, enterprise_id: Optional[str], team_id: Optional[str], user_id: Optional[str] = None, ) -> None: return self.delete_installation( enterprise_id=enterprise_id, team_id=team_id, user_id=user_id, ) def delete_installation( self, *, enterprise_id: Optional[str], team_id: Optional[str], user_id: Optional[str] = None, ) -> None: none = "none" e_id = enterprise_id or none t_id = team_id or none workspace_path = f"{self.client_id}/{e_id}-{t_id}" objects = self.s3_client.list_objects( Bucket=self.bucket_name, Prefix=f"{workspace_path}/installer-{user_id or ''}", ) deleted_keys = [] for content in objects.get("Contents", []): key = content.get("Key") if key is not None: self.logger.info(f"Going to delete installation ({key})") try: self.s3_client.delete_object( Bucket=self.bucket_name, Key=key, ) deleted_keys.append(key) except Exception as e: message = f"Failed to delete installation data for enterprise: {e_id}, team: {t_id}: {e}" raise SlackClientConfigurationError(message) try: no_user_id_key = key.replace(f"-{user_id}", "") if not no_user_id_key.endswith("installer-latest"): self.s3_client.delete_object( Bucket=self.bucket_name, Key=no_user_id_key, ) deleted_keys.append(no_user_id_key) except Exception as e: message = f"Failed to delete installation data for enterprise: {e_id}, team: {t_id}: {e}" raise SlackClientConfigurationError(message) # Check the remaining installation data objects = self.s3_client.list_objects( Bucket=self.bucket_name, Prefix=f"{workspace_path}/installer-", MaxKeys=10, # the small number would be enough for this purpose ) keys = [c.get("Key") for c in objects.get("Contents", []) if c.get("Key") not in deleted_keys] # If only installer-latest remains, we should delete the one as well if len(keys) == 1 and keys[0].endswith("installer-latest"): content = objects.get("Contents", [])[0] try: self.s3_client.delete_object( Bucket=self.bucket_name, Key=content.get("Key"), ) except Exception as e: message = f"Failed to delete installation data for enterprise: {e_id}, team: {t_id}: {e}" raise SlackClientConfigurationError(message) ``` The installation store interface. The minimum required methods are: * save(installation) * find\_installation(enterprise\_id, team\_id, user\_id, is\_enterprise\_install) If you would like to properly handle app uninstallations and token revocations, the following methods should be implemented. * delete\_installation(enterprise\_id, team\_id, user\_id) * delete\_all(enterprise\_id, team\_id) If your app needs only bot scope installations, the simpler way to implement would be: * save(installation) * find\_bot(enterprise\_id, team\_id, is\_enterprise\_install) * delete\_bot(enterprise\_id, team\_id) * delete\_all(enterprise\_id, team\_id) ### Ancestors * [InstallationStore](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore "slack_sdk.oauth.installation_store.installation_store.InstallationStore") * [AsyncInstallationStore](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore") ### Instance variables `prop logger : logging.Logger` Expand source code ``` @property def logger(self) -> Logger: if self._logger is None: self._logger = logging.getLogger(__name__) return self._logger ``` ### Inherited members * `**[InstallationStore](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore "slack_sdk.oauth.installation_store.installation_store.InstallationStore")**`: * `[save](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.save "slack_sdk.oauth.installation_store.installation_store.InstallationStore.save")` * `[save_bot](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.save_bot "slack_sdk.oauth.installation_store.installation_store.InstallationStore.save_bot")` * `**[InstallationStore](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore "slack_sdk.oauth.installation_store.installation_store.InstallationStore")**`: * `[find_bot](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.find_bot "slack_sdk.oauth.installation_store.installation_store.InstallationStore.find_bot")` * `**[InstallationStore](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore "slack_sdk.oauth.installation_store.installation_store.InstallationStore")**`: * `[find_installation](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.find_installation "slack_sdk.oauth.installation_store.installation_store.InstallationStore.find_installation")` * `**[InstallationStore](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore "slack_sdk.oauth.installation_store.installation_store.InstallationStore")**`: * `[delete_bot](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.delete_bot "slack_sdk.oauth.installation_store.installation_store.InstallationStore.delete_bot")` * `**[InstallationStore](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore "slack_sdk.oauth.installation_store.installation_store.InstallationStore")**`: * `[delete_all](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.delete_all "slack_sdk.oauth.installation_store.installation_store.InstallationStore.delete_all")` * `[delete_installation](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.delete_installation "slack_sdk.oauth.installation_store.installation_store.InstallationStore.delete_installation")` * `**[AsyncInstallationStore](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore")**`: * `[async_save](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_save "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_save")` * `[async_save_bot](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_save_bot "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_save_bot")` * `**[AsyncInstallationStore](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore")**`: * `[async_find_bot](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_find_bot "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_find_bot")` * `**[AsyncInstallationStore](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore")**`: * `[async_find_installation](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_find_installation "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_find_installation")` * `**[AsyncInstallationStore](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore")**`: * `[async_delete_bot](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_delete_bot "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_delete_bot")` * `**[AsyncInstallationStore](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore")**`: * `[async_delete_installation](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_delete_installation "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_delete_installation")` * `**[AsyncInstallationStore](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore")**`: * `[async_delete_all](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_delete_all "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_delete_all")` --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/oauth/installation_store/file # Module slack_sdk.oauth.installation_store.file ## Classes `class FileInstallationStore (*, base_dir: str = '$HOME/.bolt-app-installation', historical_data_enabled: bool = True, client_id: str | None = None, logger: logging.Logger = )` Expand source code ``` class FileInstallationStore(InstallationStore, AsyncInstallationStore): def __init__( self, *, base_dir: str = str(Path.home()) + "/.bolt-app-installation", historical_data_enabled: bool = True, client_id: Optional[str] = None, logger: Logger = logging.getLogger(__name__), ): self.base_dir = base_dir self.historical_data_enabled = historical_data_enabled self.client_id = client_id if self.client_id is not None: self.base_dir = f"{self.base_dir}/{self.client_id}" self._logger = logger @property def logger(self) -> Logger: if self._logger is None: self._logger = logging.getLogger(__name__) return self._logger async def async_save(self, installation: Installation): return self.save(installation) async def async_save_bot(self, bot: Bot): return self.save_bot(bot) def save(self, installation: Installation): none = "none" e_id = installation.enterprise_id or none t_id = installation.team_id or none team_installation_dir = f"{self.base_dir}/{e_id}-{t_id}" self._mkdir(team_installation_dir) self.save_bot(installation.to_bot()) if self.historical_data_enabled: history_version: str = str(installation.installed_at) # per workspace entity: str = json.dumps(installation.__dict__) with open(f"{team_installation_dir}/installer-latest", "w") as f: f.write(entity) with open(f"{team_installation_dir}/installer-{history_version}", "w") as f: f.write(entity) # per workspace per user u_id = installation.user_id or none entity = json.dumps(installation.__dict__) with open(f"{team_installation_dir}/installer-{u_id}-latest", "w") as f: f.write(entity) with open(f"{team_installation_dir}/installer-{u_id}-{history_version}", "w") as f: f.write(entity) else: u_id = installation.user_id or none installer_filepath = f"{team_installation_dir}/installer-{u_id}-latest" with open(installer_filepath, "w") as f: entity = json.dumps(installation.__dict__) f.write(entity) def save_bot(self, bot: Bot): if bot.bot_token is None: self.logger.debug("Skipped saving a new row because of the absence of bot token in it") return none = "none" e_id = bot.enterprise_id or none t_id = bot.team_id or none team_installation_dir = f"{self.base_dir}/{e_id}-{t_id}" self._mkdir(team_installation_dir) if self.historical_data_enabled: history_version: str = str(bot.installed_at) entity: str = json.dumps(bot.__dict__) with open(f"{team_installation_dir}/bot-latest", "w") as f: f.write(entity) with open(f"{team_installation_dir}/bot-{history_version}", "w") as f: f.write(entity) else: with open(f"{team_installation_dir}/bot-latest", "w") as f: entity = json.dumps(bot.__dict__) f.write(entity) async def async_find_bot( self, *, enterprise_id: Optional[str], team_id: Optional[str], is_enterprise_install: Optional[bool] = False, ) -> Optional[Bot]: return self.find_bot( enterprise_id=enterprise_id, team_id=team_id, is_enterprise_install=is_enterprise_install, ) def find_bot( self, *, enterprise_id: Optional[str], team_id: Optional[str], is_enterprise_install: Optional[bool] = False, ) -> Optional[Bot]: none = "none" e_id = enterprise_id or none t_id = team_id or none if is_enterprise_install: t_id = none bot_filepath = f"{self.base_dir}/{e_id}-{t_id}/bot-latest" try: with open(bot_filepath) as f: data = json.loads(f.read()) return Bot(**data) except FileNotFoundError as e: message = f"Installation data missing for enterprise: {e_id}, team: {t_id}: {e}" self.logger.debug(message) return None async def async_find_installation( self, *, enterprise_id: Optional[str], team_id: Optional[str], user_id: Optional[str] = None, is_enterprise_install: Optional[bool] = False, ) -> Optional[Installation]: return self.find_installation( enterprise_id=enterprise_id, team_id=team_id, user_id=user_id, is_enterprise_install=is_enterprise_install, ) def find_installation( self, *, enterprise_id: Optional[str], team_id: Optional[str], user_id: Optional[str] = None, is_enterprise_install: Optional[bool] = False, ) -> Optional[Installation]: none = "none" e_id = enterprise_id or none t_id = team_id or none if is_enterprise_install: t_id = none installation_filepath = f"{self.base_dir}/{e_id}-{t_id}/installer-latest" if user_id is not None: installation_filepath = f"{self.base_dir}/{e_id}-{t_id}/installer-{user_id}-latest" try: installation: Optional[Installation] = None with open(installation_filepath) as f: data = json.loads(f.read()) installation = Installation(**data) has_user_installation = user_id is not None and installation is not None no_bot_token_installation = installation is not None and installation.bot_token is None should_find_bot_installation = has_user_installation or no_bot_token_installation if should_find_bot_installation: # Retrieve the latest bot token, just in case # See also: https://github.com/slackapi/bolt-python/issues/664 latest_bot_installation = self.find_bot( enterprise_id=enterprise_id, team_id=team_id, is_enterprise_install=is_enterprise_install, ) if latest_bot_installation is not None and installation.bot_token != latest_bot_installation.bot_token: # NOTE: this logic is based on the assumption that every single installation has bot scopes # If you need to installation patterns without bot scopes in the same S3 bucket, # please fork this code and implement your own logic. installation.bot_id = latest_bot_installation.bot_id installation.bot_user_id = latest_bot_installation.bot_user_id installation.bot_token = latest_bot_installation.bot_token installation.bot_scopes = latest_bot_installation.bot_scopes installation.bot_refresh_token = latest_bot_installation.bot_refresh_token installation.bot_token_expires_at = latest_bot_installation.bot_token_expires_at return installation except FileNotFoundError as e: message = f"Installation data missing for enterprise: {e_id}, team: {t_id}: {e}" self.logger.debug(message) return None async def async_delete_bot(self, *, enterprise_id: Optional[str], team_id: Optional[str]) -> None: return self.delete_bot(enterprise_id=enterprise_id, team_id=team_id) def delete_bot(self, *, enterprise_id: Optional[str], team_id: Optional[str]) -> None: none = "none" e_id = enterprise_id or none t_id = team_id or none filepath_glob = f"{self.base_dir}/{e_id}-{t_id}/bot-*" self._delete_by_glob(e_id, t_id, filepath_glob) async def async_delete_installation( self, *, enterprise_id: Optional[str], team_id: Optional[str], user_id: Optional[str] = None, ) -> None: return self.delete_installation(enterprise_id=enterprise_id, team_id=team_id, user_id=user_id) def delete_installation( self, *, enterprise_id: Optional[str], team_id: Optional[str], user_id: Optional[str] = None, ) -> None: none = "none" e_id = enterprise_id or none t_id = team_id or none if user_id is not None: filepath_glob = f"{self.base_dir}/{e_id}-{t_id}/installer-{user_id}-*" else: filepath_glob = f"{self.base_dir}/{e_id}-{t_id}/installer-*" self._delete_by_glob(e_id, t_id, filepath_glob) def _delete_by_glob(self, e_id: str, t_id: str, filepath_glob: str): for filepath in glob.glob(filepath_glob): try: os.remove(filepath) except FileNotFoundError as e: message = f"Failed to delete installation data for enterprise: {e_id}, team: {t_id}: {e}" self.logger.warning(message) @staticmethod def _mkdir(path: Union[str, Path]): if isinstance(path, str): path = Path(path) path.mkdir(parents=True, exist_ok=True) ``` The installation store interface. The minimum required methods are: * save(installation) * find\_installation(enterprise\_id, team\_id, user\_id, is\_enterprise\_install) If you would like to properly handle app uninstallations and token revocations, the following methods should be implemented. * delete\_installation(enterprise\_id, team\_id, user\_id) * delete\_all(enterprise\_id, team\_id) If your app needs only bot scope installations, the simpler way to implement would be: * save(installation) * find\_bot(enterprise\_id, team\_id, is\_enterprise\_install) * delete\_bot(enterprise\_id, team\_id) * delete\_all(enterprise\_id, team\_id) ### Ancestors * [InstallationStore](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore "slack_sdk.oauth.installation_store.installation_store.InstallationStore") * [AsyncInstallationStore](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore") ### Instance variables `prop logger : logging.Logger` Expand source code ``` @property def logger(self) -> Logger: if self._logger is None: self._logger = logging.getLogger(__name__) return self._logger ``` ### Inherited members * `**[InstallationStore](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore "slack_sdk.oauth.installation_store.installation_store.InstallationStore")**`: * `[save](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.save "slack_sdk.oauth.installation_store.installation_store.InstallationStore.save")` * `[save_bot](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.save_bot "slack_sdk.oauth.installation_store.installation_store.InstallationStore.save_bot")` * `**[InstallationStore](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore "slack_sdk.oauth.installation_store.installation_store.InstallationStore")**`: * `[find_bot](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.find_bot "slack_sdk.oauth.installation_store.installation_store.InstallationStore.find_bot")` * `**[InstallationStore](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore "slack_sdk.oauth.installation_store.installation_store.InstallationStore")**`: * `[find_installation](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.find_installation "slack_sdk.oauth.installation_store.installation_store.InstallationStore.find_installation")` * `**[InstallationStore](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore "slack_sdk.oauth.installation_store.installation_store.InstallationStore")**`: * `[delete_bot](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.delete_bot "slack_sdk.oauth.installation_store.installation_store.InstallationStore.delete_bot")` * `**[InstallationStore](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore "slack_sdk.oauth.installation_store.installation_store.InstallationStore")**`: * `[delete_all](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.delete_all "slack_sdk.oauth.installation_store.installation_store.InstallationStore.delete_all")` * `[delete_installation](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.delete_installation "slack_sdk.oauth.installation_store.installation_store.InstallationStore.delete_installation")` * `**[AsyncInstallationStore](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore")**`: * `[async_save](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_save "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_save")` * `[async_save_bot](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_save_bot "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_save_bot")` * `**[AsyncInstallationStore](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore")**`: * `[async_find_bot](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_find_bot "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_find_bot")` * `**[AsyncInstallationStore](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore")**`: * `[async_find_installation](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_find_installation "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_find_installation")` * `**[AsyncInstallationStore](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore")**`: * `[async_delete_bot](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_delete_bot "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_delete_bot")` * `**[AsyncInstallationStore](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore")**`: * `[async_delete_installation](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_delete_installation "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_delete_installation")` * `**[AsyncInstallationStore](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore")**`: * `[async_delete_all](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_delete_all "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_delete_all")` --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/oauth/installation_store/models # Module slack_sdk.oauth.installation_store.models ## Sub-modules `[slack_sdk.oauth.installation_store.models.bot](bot.html "slack_sdk.oauth.installation_store.models.bot")` `[slack_sdk.oauth.installation_store.models.installation](installation.html "slack_sdk.oauth.installation_store.models.installation")` ## Classes `class Bot (*, app_id: str | None = None, enterprise_id: str | None = None, enterprise_name: str | None = None, team_id: str | None = None, team_name: str | None = None, bot_token: str, bot_id: str, bot_user_id: str, bot_scopes: str | Sequence[str] = '', bot_refresh_token: str | None = None, bot_token_expires_in: int | None = None, bot_token_expires_at: int | datetime.datetime | str | None = None, is_enterprise_install: bool | None = False, installed_at: float | datetime.datetime | str, custom_values: Dict[str, Any] | None = None)` Expand source code ``` class Bot: app_id: Optional[str] enterprise_id: Optional[str] enterprise_name: Optional[str] team_id: Optional[str] team_name: Optional[str] bot_token: str bot_id: str bot_user_id: str bot_scopes: Sequence[str] # only when token rotation is enabled bot_refresh_token: Optional[str] # only when token rotation is enabled bot_token_expires_at: Optional[int] is_enterprise_install: bool installed_at: float custom_values: Dict[str, Any] def __init__( self, *, app_id: Optional[str] = None, # org / workspace enterprise_id: Optional[str] = None, enterprise_name: Optional[str] = None, team_id: Optional[str] = None, team_name: Optional[str] = None, # bot bot_token: str, bot_id: str, bot_user_id: str, bot_scopes: Union[str, Sequence[str]] = "", # only when token rotation is enabled bot_refresh_token: Optional[str] = None, # only when token rotation is enabled bot_token_expires_in: Optional[int] = None, # only for duplicating this object # only when token rotation is enabled bot_token_expires_at: Optional[Union[int, datetime, str]] = None, is_enterprise_install: Optional[bool] = False, # timestamps # The expected value type is float but the internals handle other types too # for str values, we support only ISO datetime format. installed_at: Union[float, datetime, str], # custom values custom_values: Optional[Dict[str, Any]] = None, ): self.app_id = app_id self.enterprise_id = enterprise_id self.enterprise_name = enterprise_name self.team_id = team_id self.team_name = team_name self.bot_token = bot_token self.bot_id = bot_id self.bot_user_id = bot_user_id if isinstance(bot_scopes, str): self.bot_scopes = bot_scopes.split(",") if len(bot_scopes) > 0 else [] else: self.bot_scopes = bot_scopes self.bot_refresh_token = bot_refresh_token if bot_token_expires_at is not None: self.bot_token_expires_at = _timestamp_to_type(bot_token_expires_at, int) elif bot_token_expires_in is not None: self.bot_token_expires_at = int(time()) + bot_token_expires_in else: self.bot_token_expires_at = None self.is_enterprise_install = is_enterprise_install or False self.installed_at = _timestamp_to_type(installed_at, float) self.custom_values = custom_values if custom_values is not None else {} def set_custom_value(self, name: str, value: Any): self.custom_values[name] = value def get_custom_value(self, name: str) -> Optional[Any]: return self.custom_values.get(name) def _to_standard_value_dict(self) -> Dict[str, Any]: return { "app_id": self.app_id, "enterprise_id": self.enterprise_id, "enterprise_name": self.enterprise_name, "team_id": self.team_id, "team_name": self.team_name, "bot_token": self.bot_token, "bot_id": self.bot_id, "bot_user_id": self.bot_user_id, "bot_scopes": ",".join(self.bot_scopes) if self.bot_scopes else None, "bot_refresh_token": self.bot_refresh_token, "bot_token_expires_at": ( datetime.fromtimestamp(self.bot_token_expires_at, tz=timezone.utc) if self.bot_token_expires_at is not None else None ), "is_enterprise_install": self.is_enterprise_install, "installed_at": datetime.fromtimestamp(self.installed_at, tz=timezone.utc), } def to_dict_for_copying(self) -> Dict[str, Any]: return {"custom_values": self.custom_values, **self._to_standard_value_dict()} def to_dict(self) -> Dict[str, Any]: # prioritize standard_values over custom_values # when the same keys exist in both return {**self.custom_values, **self._to_standard_value_dict()} ``` ### Class variables `var app_id : str | None` The type of the None singleton. `var bot_id : str` The type of the None singleton. `var bot_refresh_token : str | None` The type of the None singleton. `var bot_scopes : Sequence[str]` The type of the None singleton. `var bot_token : str` The type of the None singleton. `var bot_token_expires_at : int | None` The type of the None singleton. `var bot_user_id : str` The type of the None singleton. `var custom_values : Dict[str, Any]` The type of the None singleton. `var enterprise_id : str | None` The type of the None singleton. `var enterprise_name : str | None` The type of the None singleton. `var installed_at : float` The type of the None singleton. `var is_enterprise_install : bool` The type of the None singleton. `var team_id : str | None` The type of the None singleton. `var team_name : str | None` The type of the None singleton. ### Methods `def get_custom_value(self, name: str) ‑> Any | None` Expand source code ``` def get_custom_value(self, name: str) -> Optional[Any]: return self.custom_values.get(name) ``` `def set_custom_value(self, name: str, value: Any)` Expand source code ``` def set_custom_value(self, name: str, value: Any): self.custom_values[name] = value ``` `def to_dict(self) ‑> Dict[str, Any]` Expand source code ``` def to_dict(self) -> Dict[str, Any]: # prioritize standard_values over custom_values # when the same keys exist in both return {**self.custom_values, **self._to_standard_value_dict()} ``` `def to_dict_for_copying(self) ‑> Dict[str, Any]` Expand source code ``` def to_dict_for_copying(self) -> Dict[str, Any]: return {"custom_values": self.custom_values, **self._to_standard_value_dict()} ``` `class Installation (*, app_id: str | None = None, enterprise_id: str | None = None, enterprise_name: str | None = None, enterprise_url: str | None = None, team_id: str | None = None, team_name: str | None = None, bot_token: str | None = None, bot_id: str | None = None, bot_user_id: str | None = None, bot_scopes: str | Sequence[str] = '', bot_refresh_token: str | None = None, bot_token_expires_in: int | None = None, bot_token_expires_at: int | datetime.datetime | str | None = None, user_id: str, user_token: str | None = None, user_scopes: str | Sequence[str] = '', user_refresh_token: str | None = None, user_token_expires_in: int | None = None, user_token_expires_at: int | datetime.datetime | str | None = None, incoming_webhook_url: str | None = None, incoming_webhook_channel: str | None = None, incoming_webhook_channel_id: str | None = None, incoming_webhook_configuration_url: str | None = None, is_enterprise_install: bool | None = False, token_type: str | None = None, installed_at: float | datetime.datetime | str | None = None, custom_values: Dict[str, Any] | None = None)` Expand source code ``` class Installation: app_id: Optional[str] enterprise_id: Optional[str] enterprise_name: Optional[str] enterprise_url: Optional[str] team_id: Optional[str] team_name: Optional[str] bot_token: Optional[str] bot_id: Optional[str] bot_user_id: Optional[str] bot_scopes: Optional[Sequence[str]] bot_refresh_token: Optional[str] # only when token rotation is enabled # only when token rotation is enabled # Unix time (seconds): only when token rotation is enabled bot_token_expires_at: Optional[int] user_id: str user_token: Optional[str] user_scopes: Optional[Sequence[str]] user_refresh_token: Optional[str] # only when token rotation is enabled # Unix time (seconds): only when token rotation is enabled user_token_expires_at: Optional[int] incoming_webhook_url: Optional[str] incoming_webhook_channel: Optional[str] incoming_webhook_channel_id: Optional[str] incoming_webhook_configuration_url: Optional[str] is_enterprise_install: bool token_type: Optional[str] installed_at: float custom_values: Dict[str, Any] def __init__( self, *, app_id: Optional[str] = None, # org / workspace enterprise_id: Optional[str] = None, enterprise_name: Optional[str] = None, enterprise_url: Optional[str] = None, team_id: Optional[str] = None, team_name: Optional[str] = None, # bot bot_token: Optional[str] = None, bot_id: Optional[str] = None, bot_user_id: Optional[str] = None, bot_scopes: Union[str, Sequence[str]] = "", bot_refresh_token: Optional[str] = None, # only when token rotation is enabled # only when token rotation is enabled bot_token_expires_in: Optional[int] = None, # only for duplicating this object # only when token rotation is enabled bot_token_expires_at: Optional[Union[int, datetime, str]] = None, # installer user_id: str, user_token: Optional[str] = None, user_scopes: Union[str, Sequence[str]] = "", user_refresh_token: Optional[str] = None, # only when token rotation is enabled # only when token rotation is enabled user_token_expires_in: Optional[int] = None, # only for duplicating this object # only when token rotation is enabled user_token_expires_at: Optional[Union[int, datetime, str]] = None, # incoming webhook incoming_webhook_url: Optional[str] = None, incoming_webhook_channel: Optional[str] = None, incoming_webhook_channel_id: Optional[str] = None, incoming_webhook_configuration_url: Optional[str] = None, # org app is_enterprise_install: Optional[bool] = False, token_type: Optional[str] = None, # timestamps # The expected value type is float but the internals handle other types too # for str values, we supports only ISO datetime format. installed_at: Optional[Union[float, datetime, str]] = None, # custom values custom_values: Optional[Dict[str, Any]] = None, ): self.app_id = app_id self.enterprise_id = enterprise_id self.enterprise_name = enterprise_name self.enterprise_url = enterprise_url self.team_id = team_id self.team_name = team_name self.bot_token = bot_token self.bot_id = bot_id self.bot_user_id = bot_user_id if isinstance(bot_scopes, str): self.bot_scopes = bot_scopes.split(",") if len(bot_scopes) > 0 else [] else: self.bot_scopes = bot_scopes self.bot_refresh_token = bot_refresh_token if bot_token_expires_at is not None: self.bot_token_expires_at = _timestamp_to_type(bot_token_expires_at, int) elif bot_token_expires_in is not None: self.bot_token_expires_at = int(time()) + bot_token_expires_in else: self.bot_token_expires_at = None self.user_id = user_id self.user_token = user_token if isinstance(user_scopes, str): self.user_scopes = user_scopes.split(",") if len(user_scopes) > 0 else [] else: self.user_scopes = user_scopes self.user_refresh_token = user_refresh_token if user_token_expires_at is not None: self.user_token_expires_at = _timestamp_to_type(user_token_expires_at, int) elif user_token_expires_in is not None: self.user_token_expires_at = int(time()) + user_token_expires_in else: self.user_token_expires_at = None self.incoming_webhook_url = incoming_webhook_url self.incoming_webhook_channel = incoming_webhook_channel self.incoming_webhook_channel_id = incoming_webhook_channel_id self.incoming_webhook_configuration_url = incoming_webhook_configuration_url self.is_enterprise_install = is_enterprise_install or False self.token_type = token_type if installed_at is None: self.installed_at = datetime.now().timestamp() else: self.installed_at = _timestamp_to_type(installed_at, float) self.custom_values = custom_values if custom_values is not None else {} def to_bot(self) -> Bot: return Bot( app_id=self.app_id, enterprise_id=self.enterprise_id, enterprise_name=self.enterprise_name, team_id=self.team_id, team_name=self.team_name, bot_token=self.bot_token, # type: ignore[arg-type] bot_id=self.bot_id, # type: ignore[arg-type] bot_user_id=self.bot_user_id, # type: ignore[arg-type] bot_scopes=self.bot_scopes, # type: ignore[arg-type] bot_refresh_token=self.bot_refresh_token, bot_token_expires_at=self.bot_token_expires_at, is_enterprise_install=self.is_enterprise_install, installed_at=self.installed_at, custom_values=self.custom_values, ) def set_custom_value(self, name: str, value: Any): self.custom_values[name] = value def get_custom_value(self, name: str) -> Optional[Any]: return self.custom_values.get(name) def _to_standard_value_dict(self) -> Dict[str, Any]: return { "app_id": self.app_id, "enterprise_id": self.enterprise_id, "enterprise_name": self.enterprise_name, "enterprise_url": self.enterprise_url, "team_id": self.team_id, "team_name": self.team_name, "bot_token": self.bot_token, "bot_id": self.bot_id, "bot_user_id": self.bot_user_id, "bot_scopes": ",".join(self.bot_scopes) if self.bot_scopes else None, "bot_refresh_token": self.bot_refresh_token, "bot_token_expires_at": ( datetime.fromtimestamp(self.bot_token_expires_at, tz=timezone.utc) if self.bot_token_expires_at is not None else None ), "user_id": self.user_id, "user_token": self.user_token, "user_scopes": ",".join(self.user_scopes) if self.user_scopes else None, "user_refresh_token": self.user_refresh_token, "user_token_expires_at": ( datetime.fromtimestamp(self.user_token_expires_at, tz=timezone.utc) if self.user_token_expires_at is not None else None ), "incoming_webhook_url": self.incoming_webhook_url, "incoming_webhook_channel": self.incoming_webhook_channel, "incoming_webhook_channel_id": self.incoming_webhook_channel_id, "incoming_webhook_configuration_url": self.incoming_webhook_configuration_url, "is_enterprise_install": self.is_enterprise_install, "token_type": self.token_type, "installed_at": datetime.fromtimestamp(self.installed_at, tz=timezone.utc), } def to_dict_for_copying(self) -> Dict[str, Any]: return {"custom_values": self.custom_values, **self._to_standard_value_dict()} def to_dict(self) -> Dict[str, Any]: # prioritize standard_values over custom_values # when the same keys exist in both return {**self.custom_values, **self._to_standard_value_dict()} ``` ### Class variables `var app_id : str | None` The type of the None singleton. `var bot_id : str | None` The type of the None singleton. `var bot_refresh_token : str | None` The type of the None singleton. `var bot_scopes : Sequence[str] | None` The type of the None singleton. `var bot_token : str | None` The type of the None singleton. `var bot_token_expires_at : int | None` The type of the None singleton. `var bot_user_id : str | None` The type of the None singleton. `var custom_values : Dict[str, Any]` The type of the None singleton. `var enterprise_id : str | None` The type of the None singleton. `var enterprise_name : str | None` The type of the None singleton. `var enterprise_url : str | None` The type of the None singleton. `var incoming_webhook_channel : str | None` The type of the None singleton. `var incoming_webhook_channel_id : str | None` The type of the None singleton. `var incoming_webhook_configuration_url : str | None` The type of the None singleton. `var incoming_webhook_url : str | None` The type of the None singleton. `var installed_at : float` The type of the None singleton. `var is_enterprise_install : bool` The type of the None singleton. `var team_id : str | None` The type of the None singleton. `var team_name : str | None` The type of the None singleton. `var token_type : str | None` The type of the None singleton. `var user_id : str` The type of the None singleton. `var user_refresh_token : str | None` The type of the None singleton. `var user_scopes : Sequence[str] | None` The type of the None singleton. `var user_token : str | None` The type of the None singleton. `var user_token_expires_at : int | None` The type of the None singleton. ### Methods `def get_custom_value(self, name: str) ‑> Any | None` Expand source code ``` def get_custom_value(self, name: str) -> Optional[Any]: return self.custom_values.get(name) ``` `def set_custom_value(self, name: str, value: Any)` Expand source code ``` def set_custom_value(self, name: str, value: Any): self.custom_values[name] = value ``` `def to_bot(self) ‑> [Bot](bot.html#slack_sdk.oauth.installation_store.models.bot.Bot "slack_sdk.oauth.installation_store.models.bot.Bot")` Expand source code ``` def to_bot(self) -> Bot: return Bot( app_id=self.app_id, enterprise_id=self.enterprise_id, enterprise_name=self.enterprise_name, team_id=self.team_id, team_name=self.team_name, bot_token=self.bot_token, # type: ignore[arg-type] bot_id=self.bot_id, # type: ignore[arg-type] bot_user_id=self.bot_user_id, # type: ignore[arg-type] bot_scopes=self.bot_scopes, # type: ignore[arg-type] bot_refresh_token=self.bot_refresh_token, bot_token_expires_at=self.bot_token_expires_at, is_enterprise_install=self.is_enterprise_install, installed_at=self.installed_at, custom_values=self.custom_values, ) ``` `def to_dict(self) ‑> Dict[str, Any]` Expand source code ``` def to_dict(self) -> Dict[str, Any]: # prioritize standard_values over custom_values # when the same keys exist in both return {**self.custom_values, **self._to_standard_value_dict()} ``` `def to_dict_for_copying(self) ‑> Dict[str, Any]` Expand source code ``` def to_dict_for_copying(self) -> Dict[str, Any]: return {"custom_values": self.custom_values, **self._to_standard_value_dict()} ``` --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/oauth/installation_store/sqlalchemy # Module slack_sdk.oauth.installation_store.sqlalchemy ## Classes `class AsyncSQLAlchemyInstallationStore (client_id: str, engine: sqlalchemy.ext.asyncio.engine.AsyncEngine, bots_table_name: str = 'slack_bots', installations_table_name: str = 'slack_installations', logger: logging.Logger = )` Expand source code ``` class AsyncSQLAlchemyInstallationStore(AsyncInstallationStore): default_bots_table_name: str = "slack_bots" default_installations_table_name: str = "slack_installations" client_id: str engine: AsyncEngine metadata: MetaData installations: Table def __init__( self, client_id: str, engine: AsyncEngine, bots_table_name: str = default_bots_table_name, installations_table_name: str = default_installations_table_name, logger: Logger = logging.getLogger(__name__), ): self.metadata = sqlalchemy.MetaData() self.bots = self.build_bots_table(metadata=self.metadata, table_name=bots_table_name) self.installations = self.build_installations_table(metadata=self.metadata, table_name=installations_table_name) self.client_id = client_id self._logger = logger self.engine = engine @classmethod def build_installations_table(cls, metadata: MetaData, table_name: str) -> Table: return SQLAlchemyInstallationStore.build_installations_table(metadata, table_name) @classmethod def build_bots_table(cls, metadata: MetaData, table_name: str) -> Table: return SQLAlchemyInstallationStore.build_bots_table(metadata, table_name) async def create_tables(self): async with self.engine.begin() as conn: await conn.run_sync(self.metadata.create_all) @property def logger(self) -> Logger: return self._logger async def async_save(self, installation: Installation): async with self.engine.begin() as conn: i = installation.to_dict() i["client_id"] = self.client_id i["installed_at"] = normalize_datetime_for_db(i.get("installed_at")) i["bot_token_expires_at"] = normalize_datetime_for_db(i.get("bot_token_expires_at")) i["user_token_expires_at"] = normalize_datetime_for_db(i.get("user_token_expires_at")) i_column = self.installations.c installations_rows = await conn.execute( sqlalchemy.select(i_column.id) .where( and_( i_column.client_id == self.client_id, i_column.enterprise_id == installation.enterprise_id, i_column.team_id == installation.team_id, i_column.installed_at == i.get("installed_at"), ) ) .limit(1) ) installations_row_id: Optional[str] = None for row in installations_rows.mappings(): installations_row_id = row["id"] if installations_row_id is None: await conn.execute(self.installations.insert(), i) else: update_statement = self.installations.update().where(i_column.id == installations_row_id).values(**i) await conn.execute(update_statement, i) # bots await self.async_save_bot(installation.to_bot()) async def async_save_bot(self, bot: Bot): async with self.engine.begin() as conn: # bots b = bot.to_dict() b["client_id"] = self.client_id b["installed_at"] = normalize_datetime_for_db(b.get("installed_at")) b["bot_token_expires_at"] = normalize_datetime_for_db(b.get("bot_token_expires_at")) b_column = self.bots.c bots_rows = await conn.execute( sqlalchemy.select(b_column.id) .where( and_( b_column.client_id == self.client_id, b_column.enterprise_id == bot.enterprise_id, b_column.team_id == bot.team_id, b_column.installed_at == b.get("installed_at"), ) ) .limit(1) ) bots_row_id: Optional[str] = None for row in bots_rows.mappings(): bots_row_id = row["id"] if bots_row_id is None: await conn.execute(self.bots.insert(), b) else: update_statement = self.bots.update().where(b_column.id == bots_row_id).values(**b) await conn.execute(update_statement, b) async def async_find_bot( self, *, enterprise_id: Optional[str], team_id: Optional[str], is_enterprise_install: Optional[bool] = False, ) -> Optional[Bot]: if is_enterprise_install or team_id is None: team_id = None c = self.bots.c query = ( self.bots.select() .where( and_( c.client_id == self.client_id, c.enterprise_id == enterprise_id, c.team_id == team_id, c.bot_token.is_not(None), # the latest one that has a bot token ) ) .order_by(desc(c.installed_at)) .limit(1) ) async with self.engine.connect() as conn: result: object = await conn.execute(query) for row in result.mappings(): # type: ignore[attr-defined] return SQLAlchemyInstallationStore.build_bot_entity(row) return None async def async_find_installation( self, *, enterprise_id: Optional[str], team_id: Optional[str], user_id: Optional[str] = None, is_enterprise_install: Optional[bool] = False, ) -> Optional[Installation]: if is_enterprise_install or team_id is None: team_id = None c = self.installations.c where_clause = and_( c.client_id == self.client_id, c.enterprise_id == enterprise_id, c.team_id == team_id, ) if user_id is not None: where_clause = and_( c.client_id == self.client_id, c.enterprise_id == enterprise_id, c.team_id == team_id, c.user_id == user_id, ) query = self.installations.select().where(where_clause).order_by(desc(c.installed_at)).limit(1) installation: Optional[Installation] = None async with self.engine.connect() as conn: result: object = await conn.execute(query) for row in result.mappings(): # type: ignore[attr-defined] installation = SQLAlchemyInstallationStore.build_installation_entity(row) has_user_installation = user_id is not None and installation is not None no_bot_token_installation = installation is not None and installation.bot_token is None should_find_bot_installation = has_user_installation or no_bot_token_installation if should_find_bot_installation: # Retrieve the latest bot token, just in case # See also: https://github.com/slackapi/bolt-python/issues/664 latest_bot_installation = await self.async_find_bot( enterprise_id=enterprise_id, team_id=team_id, is_enterprise_install=is_enterprise_install, ) if ( latest_bot_installation is not None and installation is not None and installation.bot_token != latest_bot_installation.bot_token ): installation.bot_id = latest_bot_installation.bot_id installation.bot_user_id = latest_bot_installation.bot_user_id installation.bot_token = latest_bot_installation.bot_token installation.bot_scopes = latest_bot_installation.bot_scopes installation.bot_refresh_token = latest_bot_installation.bot_refresh_token installation.bot_token_expires_at = latest_bot_installation.bot_token_expires_at return installation async def async_delete_bot( self, *, enterprise_id: Optional[str], team_id: Optional[str], ) -> None: table = self.bots c = table.c async with self.engine.begin() as conn: deletion = table.delete().where( and_( c.client_id == self.client_id, c.enterprise_id == enterprise_id, c.team_id == team_id, ) ) await conn.execute(deletion) async def async_delete_installation( self, *, enterprise_id: Optional[str], team_id: Optional[str], user_id: Optional[str] = None, ) -> None: table = self.installations c = table.c async with self.engine.begin() as conn: if user_id is not None: deletion = table.delete().where( and_( c.client_id == self.client_id, c.enterprise_id == enterprise_id, c.team_id == team_id, c.user_id == user_id, ) ) await conn.execute(deletion) else: deletion = table.delete().where( and_( c.client_id == self.client_id, c.enterprise_id == enterprise_id, c.team_id == team_id, ) ) await conn.execute(deletion) ``` The installation store interface for asyncio-based apps. The minimum required methods are: * async\_save(installation) * async\_find\_installation(enterprise\_id, team\_id, user\_id, is\_enterprise\_install) If you would like to properly handle app uninstallations and token revocations, the following methods should be implemented. * async\_delete\_installation(enterprise\_id, team\_id, user\_id) * async\_delete\_all(enterprise\_id, team\_id) If your app needs only bot scope installations, the simpler way to implement would be: * async\_save(installation) * async\_find\_bot(enterprise\_id, team\_id, is\_enterprise\_install) * async\_delete\_bot(enterprise\_id, team\_id) * async\_delete\_all(enterprise\_id, team\_id) ### Ancestors * [AsyncInstallationStore](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore") ### Class variables `var client_id : str` The type of the None singleton. `var default_bots_table_name : str` The type of the None singleton. `var default_installations_table_name : str` The type of the None singleton. `var engine : sqlalchemy.ext.asyncio.engine.AsyncEngine` The type of the None singleton. `var installations : sqlalchemy.sql.schema.Table` The type of the None singleton. `var metadata : sqlalchemy.sql.schema.MetaData` The type of the None singleton. ### Static methods `def build_bots_table(metadata: sqlalchemy.sql.schema.MetaData, table_name: str) ‑> sqlalchemy.sql.schema.Table` `def build_installations_table(metadata: sqlalchemy.sql.schema.MetaData, table_name: str) ‑> sqlalchemy.sql.schema.Table` ### Instance variables `prop logger : logging.Logger` Expand source code ``` @property def logger(self) -> Logger: return self._logger ``` ### Methods `async def create_tables(self)` Expand source code ``` async def create_tables(self): async with self.engine.begin() as conn: await conn.run_sync(self.metadata.create_all) ``` ### Inherited members * `**[AsyncInstallationStore](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore")**`: * `[async_delete_all](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_delete_all "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_delete_all")` * `[async_delete_bot](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_delete_bot "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_delete_bot")` * `[async_delete_installation](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_delete_installation "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_delete_installation")` * `[async_find_bot](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_find_bot "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_find_bot")` * `[async_find_installation](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_find_installation "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_find_installation")` * `[async_save](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_save "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_save")` * `[async_save_bot](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_save_bot "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_save_bot")` `class SQLAlchemyInstallationStore (client_id: str, engine: sqlalchemy.engine.base.Engine, bots_table_name: str = 'slack_bots', installations_table_name: str = 'slack_installations', logger: logging.Logger = )` Expand source code ``` class SQLAlchemyInstallationStore(InstallationStore): default_bots_table_name: str = "slack_bots" default_installations_table_name: str = "slack_installations" client_id: str engine: Engine metadata: MetaData installations: Table @classmethod def build_installations_table(cls, metadata: MetaData, table_name: str) -> Table: return sqlalchemy.Table( table_name, metadata, Column("id", Integer, primary_key=True, autoincrement=True), Column("client_id", String(32), nullable=False), Column("app_id", String(32), nullable=False), Column("enterprise_id", String(32)), Column("enterprise_name", String(200)), Column("enterprise_url", String(200)), Column("team_id", String(32)), Column("team_name", String(200)), Column("bot_token", String(200)), Column("bot_id", String(32)), Column("bot_user_id", String(32)), Column("bot_scopes", String(1000)), Column("bot_refresh_token", String(200)), # added in v3.8.0 Column("bot_token_expires_at", DateTime), # added in v3.8.0 Column("user_id", String(32), nullable=False), Column("user_token", String(200)), Column("user_scopes", String(1000)), Column("user_refresh_token", String(200)), # added in v3.8.0 Column("user_token_expires_at", DateTime), # added in v3.8.0 Column("incoming_webhook_url", String(200)), Column("incoming_webhook_channel", String(200)), Column("incoming_webhook_channel_id", String(200)), Column("incoming_webhook_configuration_url", String(200)), Column("is_enterprise_install", Boolean, default=False, nullable=False), Column("token_type", String(32)), Column( "installed_at", DateTime, nullable=False, default=sqlalchemy.sql.func.now(), ), Index( f"{table_name}_idx", "client_id", "enterprise_id", "team_id", "user_id", "installed_at", ), ) @classmethod def build_bots_table(cls, metadata: MetaData, table_name: str) -> Table: return Table( table_name, metadata, Column("id", Integer, primary_key=True, autoincrement=True), Column("client_id", String(32), nullable=False), Column("app_id", String(32), nullable=False), Column("enterprise_id", String(32)), Column("enterprise_name", String(200)), Column("team_id", String(32)), Column("team_name", String(200)), Column("bot_token", String(200)), Column("bot_id", String(32)), Column("bot_user_id", String(32)), Column("bot_scopes", String(1000)), Column("bot_refresh_token", String(200)), # added in v3.8.0 Column("bot_token_expires_at", DateTime), # added in v3.8.0 Column("is_enterprise_install", Boolean, default=False, nullable=False), Column( "installed_at", DateTime, nullable=False, default=sqlalchemy.sql.func.now(), ), Index( f"{table_name}_idx", "client_id", "enterprise_id", "team_id", "installed_at", ), ) def __init__( self, client_id: str, engine: Engine, bots_table_name: str = default_bots_table_name, installations_table_name: str = default_installations_table_name, logger: Logger = logging.getLogger(__name__), ): self.metadata = sqlalchemy.MetaData() self.bots = self.build_bots_table(metadata=self.metadata, table_name=bots_table_name) self.installations = self.build_installations_table(metadata=self.metadata, table_name=installations_table_name) self.client_id = client_id self._logger = logger self.engine = engine def create_tables(self): self.metadata.create_all(self.engine) @property def logger(self) -> Logger: return self._logger def save(self, installation: Installation): with self.engine.begin() as conn: i = installation.to_dict() i["client_id"] = self.client_id i["installed_at"] = normalize_datetime_for_db(i.get("installed_at")) i["bot_token_expires_at"] = normalize_datetime_for_db(i.get("bot_token_expires_at")) i["user_token_expires_at"] = normalize_datetime_for_db(i.get("user_token_expires_at")) i_column = self.installations.c installations_rows = conn.execute( sqlalchemy.select(i_column.id) .where( and_( i_column.client_id == self.client_id, i_column.enterprise_id == installation.enterprise_id, i_column.team_id == installation.team_id, i_column.installed_at == i.get("installed_at"), ) ) .limit(1) ) installations_row_id: Optional[str] = None for row in installations_rows.mappings(): installations_row_id = row["id"] if installations_row_id is None: conn.execute(self.installations.insert(), i) else: update_statement = self.installations.update().where(i_column.id == installations_row_id).values(**i) conn.execute(update_statement, i) # bots self.save_bot(installation.to_bot()) def save_bot(self, bot: Bot): with self.engine.begin() as conn: # bots b = bot.to_dict() b["client_id"] = self.client_id b["installed_at"] = normalize_datetime_for_db(b.get("installed_at")) b["bot_token_expires_at"] = normalize_datetime_for_db(b.get("bot_token_expires_at")) b_column = self.bots.c bots_rows = conn.execute( sqlalchemy.select(b_column.id) .where( and_( b_column.client_id == self.client_id, b_column.enterprise_id == bot.enterprise_id, b_column.team_id == bot.team_id, b_column.installed_at == b.get("installed_at"), ) ) .limit(1) ) bots_row_id: Optional[str] = None for row in bots_rows.mappings(): bots_row_id = row["id"] if bots_row_id is None: conn.execute(self.bots.insert(), b) else: update_statement = self.bots.update().where(b_column.id == bots_row_id).values(**b) conn.execute(update_statement, b) def find_bot( self, *, enterprise_id: Optional[str], team_id: Optional[str], is_enterprise_install: Optional[bool] = False, ) -> Optional[Bot]: if is_enterprise_install or team_id is None: team_id = None c = self.bots.c query = ( self.bots.select() .where( and_( c.client_id == self.client_id, c.enterprise_id == enterprise_id, c.team_id == team_id, c.bot_token.is_not(None), # the latest one that has a bot token ) ) .order_by(desc(c.installed_at)) .limit(1) ) with self.engine.connect() as conn: result: object = conn.execute(query) for row in result.mappings(): # type: ignore[attr-defined] return self.build_bot_entity(row) return None def find_installation( self, *, enterprise_id: Optional[str], team_id: Optional[str], user_id: Optional[str] = None, is_enterprise_install: Optional[bool] = False, ) -> Optional[Installation]: if is_enterprise_install or team_id is None: team_id = None c = self.installations.c where_clause = and_( c.client_id == self.client_id, c.enterprise_id == enterprise_id, c.team_id == team_id, ) if user_id is not None: where_clause = and_( c.client_id == self.client_id, c.enterprise_id == enterprise_id, c.team_id == team_id, c.user_id == user_id, ) query = self.installations.select().where(where_clause).order_by(desc(c.installed_at)).limit(1) installation: Optional[Installation] = None with self.engine.connect() as conn: result: object = conn.execute(query) for row in result.mappings(): # type: ignore[attr-defined] installation = self.build_installation_entity(row) has_user_installation = user_id is not None and installation is not None no_bot_token_installation = installation is not None and installation.bot_token is None should_find_bot_installation = has_user_installation or no_bot_token_installation if should_find_bot_installation: # Retrieve the latest bot token, just in case # See also: https://github.com/slackapi/bolt-python/issues/664 latest_bot_installation = self.find_bot( enterprise_id=enterprise_id, team_id=team_id, is_enterprise_install=is_enterprise_install, ) if ( latest_bot_installation is not None and installation is not None and installation.bot_token != latest_bot_installation.bot_token ): installation.bot_id = latest_bot_installation.bot_id installation.bot_user_id = latest_bot_installation.bot_user_id installation.bot_token = latest_bot_installation.bot_token installation.bot_scopes = latest_bot_installation.bot_scopes installation.bot_refresh_token = latest_bot_installation.bot_refresh_token installation.bot_token_expires_at = latest_bot_installation.bot_token_expires_at return installation def delete_bot(self, *, enterprise_id: Optional[str], team_id: Optional[str]) -> None: table = self.bots c = table.c with self.engine.begin() as conn: deletion = table.delete().where( and_( c.client_id == self.client_id, c.enterprise_id == enterprise_id, c.team_id == team_id, ) ) conn.execute(deletion) def delete_installation( self, *, enterprise_id: Optional[str], team_id: Optional[str], user_id: Optional[str] = None, ) -> None: table = self.installations c = table.c with self.engine.begin() as conn: if user_id is not None: deletion = table.delete().where( and_( c.client_id == self.client_id, c.enterprise_id == enterprise_id, c.team_id == team_id, c.user_id == user_id, ) ) conn.execute(deletion) else: deletion = table.delete().where( and_( c.client_id == self.client_id, c.enterprise_id == enterprise_id, c.team_id == team_id, ) ) conn.execute(deletion) @classmethod def build_installation_entity(cls, row) -> Installation: return Installation( app_id=row["app_id"], enterprise_id=row["enterprise_id"], enterprise_name=row["enterprise_name"], enterprise_url=row["enterprise_url"], team_id=row["team_id"], team_name=row["team_name"], bot_token=row["bot_token"], bot_id=row["bot_id"], bot_user_id=row["bot_user_id"], bot_scopes=row["bot_scopes"], bot_refresh_token=row["bot_refresh_token"], bot_token_expires_at=row["bot_token_expires_at"], user_id=row["user_id"], user_token=row["user_token"], user_scopes=row["user_scopes"], user_refresh_token=row["user_refresh_token"], user_token_expires_at=row["user_token_expires_at"], # Only the incoming webhook issued in the latest installation is set in this logic incoming_webhook_url=row["incoming_webhook_url"], incoming_webhook_channel=row["incoming_webhook_channel"], incoming_webhook_channel_id=row["incoming_webhook_channel_id"], incoming_webhook_configuration_url=row["incoming_webhook_configuration_url"], is_enterprise_install=row["is_enterprise_install"], token_type=row["token_type"], installed_at=row["installed_at"], ) @classmethod def build_bot_entity(cls, row) -> Bot: return Bot( app_id=row["app_id"], enterprise_id=row["enterprise_id"], enterprise_name=row["enterprise_name"], team_id=row["team_id"], team_name=row["team_name"], bot_token=row["bot_token"], bot_id=row["bot_id"], bot_user_id=row["bot_user_id"], bot_scopes=row["bot_scopes"], bot_refresh_token=row["bot_refresh_token"], bot_token_expires_at=row["bot_token_expires_at"], is_enterprise_install=row["is_enterprise_install"], installed_at=row["installed_at"], ) ``` The installation store interface. The minimum required methods are: * save(installation) * find\_installation(enterprise\_id, team\_id, user\_id, is\_enterprise\_install) If you would like to properly handle app uninstallations and token revocations, the following methods should be implemented. * delete\_installation(enterprise\_id, team\_id, user\_id) * delete\_all(enterprise\_id, team\_id) If your app needs only bot scope installations, the simpler way to implement would be: * save(installation) * find\_bot(enterprise\_id, team\_id, is\_enterprise\_install) * delete\_bot(enterprise\_id, team\_id) * delete\_all(enterprise\_id, team\_id) ### Ancestors * [InstallationStore](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore "slack_sdk.oauth.installation_store.installation_store.InstallationStore") ### Class variables `var client_id : str` The type of the None singleton. `var default_bots_table_name : str` The type of the None singleton. `var default_installations_table_name : str` The type of the None singleton. `var engine : sqlalchemy.engine.base.Engine` The type of the None singleton. `var installations : sqlalchemy.sql.schema.Table` The type of the None singleton. `var metadata : sqlalchemy.sql.schema.MetaData` The type of the None singleton. ### Static methods `def build_bot_entity(row) ‑> [Bot](../models/bot.html#slack_sdk.oauth.installation_store.models.bot.Bot "slack_sdk.oauth.installation_store.models.bot.Bot")` `def build_bots_table(metadata: sqlalchemy.sql.schema.MetaData, table_name: str) ‑> sqlalchemy.sql.schema.Table` `def build_installation_entity(row) ‑> [Installation](../models/installation.html#slack_sdk.oauth.installation_store.models.installation.Installation "slack_sdk.oauth.installation_store.models.installation.Installation")` `def build_installations_table(metadata: sqlalchemy.sql.schema.MetaData, table_name: str) ‑> sqlalchemy.sql.schema.Table` ### Instance variables `prop logger : logging.Logger` Expand source code ``` @property def logger(self) -> Logger: return self._logger ``` ### Methods `def create_tables(self)` Expand source code ``` def create_tables(self): self.metadata.create_all(self.engine) ``` ### Inherited members * `**[InstallationStore](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore "slack_sdk.oauth.installation_store.installation_store.InstallationStore")**`: * `[delete_all](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.delete_all "slack_sdk.oauth.installation_store.installation_store.InstallationStore.delete_all")` * `[delete_bot](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.delete_bot "slack_sdk.oauth.installation_store.installation_store.InstallationStore.delete_bot")` * `[delete_installation](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.delete_installation "slack_sdk.oauth.installation_store.installation_store.InstallationStore.delete_installation")` * `[find_bot](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.find_bot "slack_sdk.oauth.installation_store.installation_store.InstallationStore.find_bot")` * `[find_installation](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.find_installation "slack_sdk.oauth.installation_store.installation_store.InstallationStore.find_installation")` * `[save](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.save "slack_sdk.oauth.installation_store.installation_store.InstallationStore.save")` * `[save_bot](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.save_bot "slack_sdk.oauth.installation_store.installation_store.InstallationStore.save_bot")` --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/oauth/installation_store/sqlite3 # Module slack_sdk.oauth.installation_store.sqlite3 ## Classes `class SQLite3InstallationStore (*, database: str, client_id: str, logger: logging.Logger = )` Expand source code ``` class SQLite3InstallationStore(InstallationStore, AsyncInstallationStore): def __init__( self, *, database: str, client_id: str, logger: Logger = logging.getLogger(__name__), ): self.database = database self.client_id = client_id self.init_called = False self._logger = logger @property def logger(self) -> Logger: if self._logger is None: self._logger = logging.getLogger(__name__) return self._logger def init(self): try: with sqlite3.connect(database=self.database) as conn: cur = conn.execute("select count(1) from slack_installations;") row_num = cur.fetchone()[0] self.logger.debug(f"{row_num} installations are stored in {self.database}") except Exception: self.create_tables() self.init_called = True def connect(self) -> Connection: if not self.init_called: self.init() return sqlite3.connect(database=self.database) def create_tables(self): with sqlite3.connect(database=self.database) as conn: conn.execute(""" create table slack_installations ( id integer primary key autoincrement, client_id text not null, app_id text not null, enterprise_id text not null default '', enterprise_name text, enterprise_url text, team_id text not null default '', team_name text, bot_token text, bot_id text, bot_user_id text, bot_scopes text, bot_refresh_token text, -- since v3.8 bot_token_expires_at datetime, -- since v3.8 user_id text not null, user_token text, user_scopes text, user_refresh_token text, -- since v3.8 user_token_expires_at datetime, -- since v3.8 incoming_webhook_url text, incoming_webhook_channel text, incoming_webhook_channel_id text, incoming_webhook_configuration_url text, is_enterprise_install boolean not null default 0, token_type text, installed_at datetime not null default current_timestamp ); """) conn.execute(""" create index slack_installations_idx on slack_installations ( client_id, enterprise_id, team_id, user_id, installed_at ); """) conn.execute(""" create table slack_bots ( id integer primary key autoincrement, client_id text not null, app_id text not null, enterprise_id text not null default '', enterprise_name text, team_id text not null default '', team_name text, bot_token text not null, bot_id text not null, bot_user_id text not null, bot_scopes text, bot_refresh_token text, -- since v3.8 bot_token_expires_at datetime, -- since v3.8 is_enterprise_install boolean not null default 0, installed_at datetime not null default current_timestamp ); """) conn.execute(""" create index slack_bots_idx on slack_bots ( client_id, enterprise_id, team_id, installed_at ); """) self.logger.debug(f"Tables have been created (database: {self.database})") conn.commit() async def async_save(self, installation: Installation): return self.save(installation) async def async_save_bot(self, bot: Bot): return self.save_bot(bot) def save(self, installation: Installation): with self.connect() as conn: conn.execute( """ insert into slack_installations ( client_id, app_id, enterprise_id, enterprise_name, enterprise_url, team_id, team_name, bot_token, bot_id, bot_user_id, bot_scopes, bot_refresh_token, -- since v3.8 bot_token_expires_at, -- since v3.8 user_id, user_token, user_scopes, user_refresh_token, -- since v3.8 user_token_expires_at, -- since v3.8 incoming_webhook_url, incoming_webhook_channel, incoming_webhook_channel_id, incoming_webhook_configuration_url, is_enterprise_install, token_type ) values ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ); """, [ self.client_id, installation.app_id, installation.enterprise_id or "", installation.enterprise_name, installation.enterprise_url, installation.team_id or "", installation.team_name, installation.bot_token, installation.bot_id, installation.bot_user_id, ",".join(installation.bot_scopes), # type: ignore[arg-type] installation.bot_refresh_token, installation.bot_token_expires_at, installation.user_id, installation.user_token, ",".join(installation.user_scopes) if installation.user_scopes else None, installation.user_refresh_token, installation.user_token_expires_at, installation.incoming_webhook_url, installation.incoming_webhook_channel, installation.incoming_webhook_channel_id, installation.incoming_webhook_configuration_url, 1 if installation.is_enterprise_install else 0, installation.token_type, ], ) self.logger.debug( f"New rows in slack_bots and slack_installations have been created (database: {self.database})" ) conn.commit() self.save_bot(installation.to_bot()) def save_bot(self, bot: Bot): if bot.bot_token is None: self.logger.debug("Skipped saving a new row because of the absence of bot token in it") return with self.connect() as conn: conn.execute( """ insert into slack_bots ( client_id, app_id, enterprise_id, enterprise_name, team_id, team_name, bot_token, bot_id, bot_user_id, bot_scopes, bot_refresh_token, -- since v3.8 bot_token_expires_at, -- since v3.8 is_enterprise_install ) values ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ); """, [ self.client_id, bot.app_id, bot.enterprise_id or "", bot.enterprise_name, bot.team_id or "", bot.team_name, bot.bot_token, bot.bot_id, bot.bot_user_id, ",".join(bot.bot_scopes), bot.bot_refresh_token, bot.bot_token_expires_at, bot.is_enterprise_install, ], ) conn.commit() async def async_find_bot( self, *, enterprise_id: Optional[str], team_id: Optional[str], is_enterprise_install: Optional[bool] = False, ) -> Optional[Bot]: return self.find_bot( enterprise_id=enterprise_id, team_id=team_id, is_enterprise_install=is_enterprise_install, ) def find_bot( self, *, enterprise_id: Optional[str], team_id: Optional[str], is_enterprise_install: Optional[bool] = False, ) -> Optional[Bot]: if is_enterprise_install or team_id is None: team_id = "" try: with self.connect() as conn: cur = conn.execute( """ select app_id, enterprise_id, enterprise_name, team_id, team_name, bot_token, bot_id, bot_user_id, bot_scopes, bot_refresh_token, -- since v3.8 bot_token_expires_at, -- since v3.8 is_enterprise_install, installed_at from slack_bots where client_id = ? and enterprise_id = ? and team_id = ? order by installed_at desc limit 1 """, [self.client_id, enterprise_id or "", team_id or ""], ) row = cur.fetchone() result = "found" if row and len(row) > 0 else "not found" self.logger.debug(f"find_bot's query result: {result} (database: {self.database})") if row and len(row) > 0: bot = Bot( app_id=row[0], enterprise_id=row[1], enterprise_name=row[2], team_id=row[3], team_name=row[4], bot_token=row[5], bot_id=row[6], bot_user_id=row[7], bot_scopes=row[8], bot_refresh_token=row[9], bot_token_expires_at=row[10], is_enterprise_install=row[11], installed_at=row[12], ) return bot return None except Exception as e: message = f"Failed to find bot installation data for enterprise: {enterprise_id}, team: {team_id}: {e}" if self.logger.level <= logging.DEBUG: self.logger.exception(message) else: self.logger.warning(message) return None async def async_find_installation( self, *, enterprise_id: Optional[str], team_id: Optional[str], user_id: Optional[str] = None, is_enterprise_install: Optional[bool] = False, ) -> Optional[Installation]: return self.find_installation( enterprise_id=enterprise_id, team_id=team_id, user_id=user_id, is_enterprise_install=is_enterprise_install, ) def find_installation( self, *, enterprise_id: Optional[str], team_id: Optional[str], user_id: Optional[str] = None, is_enterprise_install: Optional[bool] = False, ) -> Optional[Installation]: if is_enterprise_install or team_id is None: team_id = "" try: with self.connect() as conn: row = None columns = """ app_id, enterprise_id, enterprise_name, enterprise_url, team_id, team_name, bot_token, bot_id, bot_user_id, bot_scopes, bot_refresh_token, -- since v3.8 bot_token_expires_at, -- since v3.8 user_id, user_token, user_scopes, user_refresh_token, -- since v3.8 user_token_expires_at, -- since v3.8 incoming_webhook_url, incoming_webhook_channel, incoming_webhook_channel_id, incoming_webhook_configuration_url, is_enterprise_install, token_type, installed_at """ if user_id is None: cur = conn.execute( f""" select {columns} from slack_installations where client_id = ? and enterprise_id = ? and team_id = ? order by installed_at desc limit 1 """, [self.client_id, enterprise_id or "", team_id], ) row = cur.fetchone() else: cur = conn.execute( f""" select {columns} from slack_installations where client_id = ? and enterprise_id = ? and team_id = ? and user_id = ? order by installed_at desc limit 1 """, [self.client_id, enterprise_id or "", team_id, user_id], ) row = cur.fetchone() if row is None: return None result = "found" if row and len(row) > 0 else "not found" self.logger.debug(f"find_installation's query result: {result} (database: {self.database})") if row and len(row) > 0: installation = Installation( app_id=row[0], enterprise_id=row[1], enterprise_name=row[2], enterprise_url=row[3], team_id=row[4], team_name=row[5], bot_token=row[6], bot_id=row[7], bot_user_id=row[8], bot_scopes=row[9], bot_refresh_token=row[10], bot_token_expires_at=row[11], user_id=row[12], user_token=row[13], user_scopes=row[14], user_refresh_token=row[15], user_token_expires_at=row[16], incoming_webhook_url=row[17], incoming_webhook_channel=row[18], incoming_webhook_channel_id=row[19], incoming_webhook_configuration_url=row[20], is_enterprise_install=row[21], token_type=row[22], installed_at=row[23], ) if user_id is not None: # Retrieve the latest bot token, just in case # See also: https://github.com/slackapi/bolt-python/issues/664 cur = conn.execute( """ select bot_token, bot_id, bot_user_id, bot_scopes, bot_refresh_token, bot_token_expires_at from slack_installations where client_id = ? and enterprise_id = ? and team_id = ? and bot_token is not null order by installed_at desc limit 1 """, [self.client_id, enterprise_id or "", team_id], ) row = cur.fetchone() installation.bot_token = row[0] installation.bot_id = row[1] installation.bot_user_id = row[2] installation.bot_scopes = row[3] installation.bot_refresh_token = row[4] installation.bot_token_expires_at = row[5] return installation return None except Exception as e: message = f"Failed to find an installation data for enterprise: {enterprise_id}, team: {team_id}: {e}" if self.logger.level <= logging.DEBUG: self.logger.exception(message) else: self.logger.warning(message) return None def delete_bot(self, *, enterprise_id: Optional[str], team_id: Optional[str]) -> None: try: with self.connect() as conn: conn.execute( """ delete from slack_bots where client_id = ? and enterprise_id = ? and team_id = ? """, [self.client_id, enterprise_id or "", team_id or ""], ) conn.commit() except Exception as e: message = f"Failed to delete bot installation data for enterprise: {enterprise_id}, team: {team_id}: {e}" if self.logger.level <= logging.DEBUG: self.logger.exception(message) else: self.logger.warning(message) def delete_installation( self, *, enterprise_id: Optional[str], team_id: Optional[str], user_id: Optional[str] = None, ) -> None: try: with self.connect() as conn: if user_id is None: conn.execute( """ delete from slack_installations where client_id = ? and enterprise_id = ? and team_id = ? """, [self.client_id, enterprise_id or "", team_id], ) else: conn.execute( """ delete from slack_installations where client_id = ? and enterprise_id = ? and team_id = ? and user_id = ? """, [self.client_id, enterprise_id or "", team_id, user_id], ) conn.commit() except Exception as e: message = f"Failed to delete installation data for enterprise: {enterprise_id}, team: {team_id}: {e}" if self.logger.level <= logging.DEBUG: self.logger.exception(message) else: self.logger.warning(message) ``` The installation store interface. The minimum required methods are: * save(installation) * find\_installation(enterprise\_id, team\_id, user\_id, is\_enterprise\_install) If you would like to properly handle app uninstallations and token revocations, the following methods should be implemented. * delete\_installation(enterprise\_id, team\_id, user\_id) * delete\_all(enterprise\_id, team\_id) If your app needs only bot scope installations, the simpler way to implement would be: * save(installation) * find\_bot(enterprise\_id, team\_id, is\_enterprise\_install) * delete\_bot(enterprise\_id, team\_id) * delete\_all(enterprise\_id, team\_id) ### Ancestors * [InstallationStore](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore "slack_sdk.oauth.installation_store.installation_store.InstallationStore") * [AsyncInstallationStore](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore") ### Instance variables `prop logger : logging.Logger` Expand source code ``` @property def logger(self) -> Logger: if self._logger is None: self._logger = logging.getLogger(__name__) return self._logger ``` ### Methods `def connect(self) ‑> sqlite3.Connection` Expand source code ``` def connect(self) -> Connection: if not self.init_called: self.init() return sqlite3.connect(database=self.database) ``` `def create_tables(self)` Expand source code ``` def create_tables(self): with sqlite3.connect(database=self.database) as conn: conn.execute(""" create table slack_installations ( id integer primary key autoincrement, client_id text not null, app_id text not null, enterprise_id text not null default '', enterprise_name text, enterprise_url text, team_id text not null default '', team_name text, bot_token text, bot_id text, bot_user_id text, bot_scopes text, bot_refresh_token text, -- since v3.8 bot_token_expires_at datetime, -- since v3.8 user_id text not null, user_token text, user_scopes text, user_refresh_token text, -- since v3.8 user_token_expires_at datetime, -- since v3.8 incoming_webhook_url text, incoming_webhook_channel text, incoming_webhook_channel_id text, incoming_webhook_configuration_url text, is_enterprise_install boolean not null default 0, token_type text, installed_at datetime not null default current_timestamp ); """) conn.execute(""" create index slack_installations_idx on slack_installations ( client_id, enterprise_id, team_id, user_id, installed_at ); """) conn.execute(""" create table slack_bots ( id integer primary key autoincrement, client_id text not null, app_id text not null, enterprise_id text not null default '', enterprise_name text, team_id text not null default '', team_name text, bot_token text not null, bot_id text not null, bot_user_id text not null, bot_scopes text, bot_refresh_token text, -- since v3.8 bot_token_expires_at datetime, -- since v3.8 is_enterprise_install boolean not null default 0, installed_at datetime not null default current_timestamp ); """) conn.execute(""" create index slack_bots_idx on slack_bots ( client_id, enterprise_id, team_id, installed_at ); """) self.logger.debug(f"Tables have been created (database: {self.database})") conn.commit() ``` `def init(self)` Expand source code ``` def init(self): try: with sqlite3.connect(database=self.database) as conn: cur = conn.execute("select count(1) from slack_installations;") row_num = cur.fetchone()[0] self.logger.debug(f"{row_num} installations are stored in {self.database}") except Exception: self.create_tables() self.init_called = True ``` ### Inherited members * `**[InstallationStore](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore "slack_sdk.oauth.installation_store.installation_store.InstallationStore")**`: * `[save](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.save "slack_sdk.oauth.installation_store.installation_store.InstallationStore.save")` * `[save_bot](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.save_bot "slack_sdk.oauth.installation_store.installation_store.InstallationStore.save_bot")` * `**[InstallationStore](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore "slack_sdk.oauth.installation_store.installation_store.InstallationStore")**`: * `[find_bot](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.find_bot "slack_sdk.oauth.installation_store.installation_store.InstallationStore.find_bot")` * `**[InstallationStore](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore "slack_sdk.oauth.installation_store.installation_store.InstallationStore")**`: * `[delete_all](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.delete_all "slack_sdk.oauth.installation_store.installation_store.InstallationStore.delete_all")` * `[delete_bot](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.delete_bot "slack_sdk.oauth.installation_store.installation_store.InstallationStore.delete_bot")` * `[delete_installation](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.delete_installation "slack_sdk.oauth.installation_store.installation_store.InstallationStore.delete_installation")` * `[find_installation](../installation_store.html#slack_sdk.oauth.installation_store.installation_store.InstallationStore.find_installation "slack_sdk.oauth.installation_store.installation_store.InstallationStore.find_installation")` * `**[AsyncInstallationStore](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore")**`: * `[async_save](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_save "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_save")` * `[async_save_bot](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_save_bot "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_save_bot")` * `**[AsyncInstallationStore](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore")**`: * `[async_find_bot](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_find_bot "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_find_bot")` * `**[AsyncInstallationStore](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore")**`: * `[async_find_installation](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_find_installation "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_find_installation")` * `**[AsyncInstallationStore](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore")**`: * `[async_delete_all](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_delete_all "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_delete_all")` * `[async_delete_bot](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_delete_bot "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_delete_bot")` * `[async_delete_installation](../async_installation_store.html#slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_delete_installation "slack_sdk.oauth.installation_store.async_installation_store.AsyncInstallationStore.async_delete_installation")` --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/oauth/redirect_uri_page_renderer # Module slack_sdk.oauth.redirect_uri_page_renderer ## Classes `class RedirectUriPageRenderer (*, install_path: str, redirect_uri_path: str, success_url: str | None = None, failure_url: str | None = None)` Expand source code ``` class RedirectUriPageRenderer: def __init__( self, *, install_path: str, redirect_uri_path: str, success_url: Optional[str] = None, failure_url: Optional[str] = None, ): self.install_path = install_path self.redirect_uri_path = redirect_uri_path self.success_url = success_url self.failure_url = failure_url def render_success_page( self, app_id: str, team_id: Optional[str], is_enterprise_install: Optional[bool] = None, enterprise_url: Optional[str] = None, ) -> str: url = self.success_url if url is None: if is_enterprise_install is True and enterprise_url is not None and app_id is not None: url = f"{enterprise_url}manage/organization/apps/profile/{app_id}/workspaces/add" elif team_id is None or app_id is None: url = "slack://open" else: url = f"slack://app?team={team_id}&id={app_id}" browser_url = f"https://app.slack.com/client/{team_id}" return f"""

Thank you!

Redirecting to the Slack App... click here. If you use the browser version of Slack, click this link instead.

""" # noqa: E501 def render_failure_page(self, reason: str) -> str: return f"""

Oops, Something Went Wrong!

Please try again from here or contact the app owner (reason: {html.escape(reason)})

""" # noqa: E501 ``` ### Methods `def render_failure_page(self, reason: str) ‑> str` Expand source code ``` def render_failure_page(self, reason: str) -> str: return f"""

Oops, Something Went Wrong!

Please try again from here or contact the app owner (reason: {html.escape(reason)})

""" # noqa: E501 ``` `def render_success_page(self, app_id: str, team_id: str | None, is_enterprise_install: bool | None = None, enterprise_url: str | None = None) ‑> str` Expand source code ``` def render_success_page( self, app_id: str, team_id: Optional[str], is_enterprise_install: Optional[bool] = None, enterprise_url: Optional[str] = None, ) -> str: url = self.success_url if url is None: if is_enterprise_install is True and enterprise_url is not None and app_id is not None: url = f"{enterprise_url}manage/organization/apps/profile/{app_id}/workspaces/add" elif team_id is None or app_id is None: url = "slack://open" else: url = f"slack://app?team={team_id}&id={app_id}" browser_url = f"https://app.slack.com/client/{team_id}" return f"""

Thank you!

Redirecting to the Slack App... click here. If you use the browser version of Slack, click this link instead.

""" # noqa: E501 ``` --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/oauth/sqlalchemy_utils # Module slack_sdk.oauth.sqlalchemy_utils ## Functions `def normalize_datetime_for_db(dt: datetime.datetime | None) ‑> datetime.datetime | None` Expand source code ``` def normalize_datetime_for_db(dt: Optional[datetime]) -> Optional[datetime]: """ Normalize timezone-aware datetime to naive UTC datetime for database storage. Ensures compatibility with existing databases using TIMESTAMP WITHOUT TIME ZONE. SQLAlchemy DateTime columns without timezone=True create naive timestamp columns in databases like PostgreSQL. This function strips timezone information from timezone-aware datetimes (which are already in UTC) to enable safe comparisons. Args: dt: A timezone-aware or naive datetime object, or None Returns: A naive datetime in UTC, or None if input is None Example: >>> from datetime import datetime, timezone >>> aware_dt = datetime(2024, 1, 1, 12, 0, 0, tzinfo=timezone.utc) >>> naive_dt = normalize_datetime_for_db(aware_dt) >>> naive_dt.tzinfo is None True """ if dt is None: return None if dt.tzinfo is not None: return dt.replace(tzinfo=None) return dt ``` Normalize timezone-aware datetime to naive UTC datetime for database storage. Ensures compatibility with existing databases using TIMESTAMP WITHOUT TIME ZONE. SQLAlchemy DateTime columns without timezone=True create naive timestamp columns in databases like PostgreSQL. This function strips timezone information from timezone-aware datetimes (which are already in UTC) to enable safe comparisons. ## Args **`dt`** A timezone-aware or naive datetime object, or None ## Returns A naive datetime in UTC, or None if input is None ## Example ```python-repl >>> from datetime import datetime, timezone >>> aware_dt = datetime(2024, 1, 1, 12, 0, 0, tzinfo=timezone.utc) >>> naive_dt = normalize_datetime_for_db(aware_dt) >>> naive_dt.tzinfo is None True ``` --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/oauth/state_store # Module slack_sdk.oauth.state_store OAuth state parameter data store Refer to [https://docs.slack.dev/tools/python-slack-sdk/oauth](https://docs.slack.dev/tools/python-slack-sdk/oauth) for details. ## Sub-modules `[slack_sdk.oauth.state_store.amazon_s3](amazon_s3/index.html "slack_sdk.oauth.state_store.amazon_s3")` `[slack_sdk.oauth.state_store.async_state_store](async_state_store.html "slack_sdk.oauth.state_store.async_state_store")` `[slack_sdk.oauth.state_store.file](file/index.html "slack_sdk.oauth.state_store.file")` `[slack_sdk.oauth.state_store.sqlalchemy](sqlalchemy/index.html "slack_sdk.oauth.state_store.sqlalchemy")` `[slack_sdk.oauth.state_store.sqlite3](sqlite3/index.html "slack_sdk.oauth.state_store.sqlite3")` `[slack_sdk.oauth.state_store.state_store](state_store.html "slack_sdk.oauth.state_store.state_store")` ## Classes `class FileOAuthStateStore (*, expiration_seconds: int, base_dir: str = '$HOME/.bolt-app-oauth-state', client_id: str | None = None, logger: logging.Logger = )` Expand source code ``` class FileOAuthStateStore(OAuthStateStore, AsyncOAuthStateStore): def __init__( self, *, expiration_seconds: int, base_dir: str = str(Path.home()) + "/.bolt-app-oauth-state", client_id: Optional[str] = None, logger: Logger = logging.getLogger(__name__), ): self.expiration_seconds = expiration_seconds self.base_dir = base_dir self.client_id = client_id if self.client_id is not None: self.base_dir = f"{self.base_dir}/{self.client_id}" self._logger = logger @property def logger(self) -> Logger: if self._logger is None: self._logger = logging.getLogger(__name__) return self._logger async def async_issue(self, *args, **kwargs) -> str: return self.issue(*args, **kwargs) async def async_consume(self, state: str) -> bool: return self.consume(state) def issue(self, *args, **kwargs) -> str: state = str(uuid4()) self._mkdir(self.base_dir) filepath = f"{self.base_dir}/{state}" with open(filepath, "w") as f: content = str(time.time()) f.write(content) return state def consume(self, state: str) -> bool: filepath = f"{self.base_dir}/{state}" try: with open(filepath) as f: created = float(f.read()) expiration = created + self.expiration_seconds still_valid: bool = time.time() < expiration os.remove(filepath) # consume the file by deleting it return still_valid except FileNotFoundError as e: message = f"Failed to find any persistent data for state: {state} - {e}" self.logger.warning(message) return False @staticmethod def _mkdir(path: Union[str, Path]): if isinstance(path, str): path = Path(path) path.mkdir(parents=True, exist_ok=True) ``` ### Ancestors * [OAuthStateStore](state_store.html#slack_sdk.oauth.state_store.state_store.OAuthStateStore "slack_sdk.oauth.state_store.state_store.OAuthStateStore") * [AsyncOAuthStateStore](async_state_store.html#slack_sdk.oauth.state_store.async_state_store.AsyncOAuthStateStore "slack_sdk.oauth.state_store.async_state_store.AsyncOAuthStateStore") ### Instance variables `prop logger : logging.Logger` Expand source code ``` @property def logger(self) -> Logger: if self._logger is None: self._logger = logging.getLogger(__name__) return self._logger ``` ### Methods `async def async_consume(self, state: str) ‑> bool` Expand source code ``` async def async_consume(self, state: str) -> bool: return self.consume(state) ``` `async def async_issue(self, *args, **kwargs) ‑> str` Expand source code ``` async def async_issue(self, *args, **kwargs) -> str: return self.issue(*args, **kwargs) ``` `def consume(self, state: str) ‑> bool` Expand source code ``` def consume(self, state: str) -> bool: filepath = f"{self.base_dir}/{state}" try: with open(filepath) as f: created = float(f.read()) expiration = created + self.expiration_seconds still_valid: bool = time.time() < expiration os.remove(filepath) # consume the file by deleting it return still_valid except FileNotFoundError as e: message = f"Failed to find any persistent data for state: {state} - {e}" self.logger.warning(message) return False ``` `def issue(self, *args, **kwargs) ‑> str` Expand source code ``` def issue(self, *args, **kwargs) -> str: state = str(uuid4()) self._mkdir(self.base_dir) filepath = f"{self.base_dir}/{state}" with open(filepath, "w") as f: content = str(time.time()) f.write(content) return state ``` `class OAuthStateStore` Expand source code ``` class OAuthStateStore: @property def logger(self) -> Logger: raise NotImplementedError() def issue(self, *args, **kwargs) -> str: raise NotImplementedError() def consume(self, state: str) -> bool: raise NotImplementedError() ``` ### Subclasses * [AmazonS3OAuthStateStore](amazon_s3/index.html#slack_sdk.oauth.state_store.amazon_s3.AmazonS3OAuthStateStore "slack_sdk.oauth.state_store.amazon_s3.AmazonS3OAuthStateStore") * [FileOAuthStateStore](file/index.html#slack_sdk.oauth.state_store.file.FileOAuthStateStore "slack_sdk.oauth.state_store.file.FileOAuthStateStore") * [SQLAlchemyOAuthStateStore](sqlalchemy/index.html#slack_sdk.oauth.state_store.sqlalchemy.SQLAlchemyOAuthStateStore "slack_sdk.oauth.state_store.sqlalchemy.SQLAlchemyOAuthStateStore") * [SQLite3OAuthStateStore](sqlite3/index.html#slack_sdk.oauth.state_store.sqlite3.SQLite3OAuthStateStore "slack_sdk.oauth.state_store.sqlite3.SQLite3OAuthStateStore") ### Instance variables `prop logger : logging.Logger` Expand source code ``` @property def logger(self) -> Logger: raise NotImplementedError() ``` ### Methods `def consume(self, state: str) ‑> bool` Expand source code ``` def consume(self, state: str) -> bool: raise NotImplementedError() ``` `def issue(self, *args, **kwargs) ‑> str` Expand source code ``` def issue(self, *args, **kwargs) -> str: raise NotImplementedError() ``` --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/oauth/state_store/amazon_s3 # Module slack_sdk.oauth.state_store.amazon_s3 ## Classes `class AmazonS3OAuthStateStore (*, s3_client: botocore.client.BaseClient, bucket_name: str, expiration_seconds: int, logger: logging.Logger = )` Expand source code ``` class AmazonS3OAuthStateStore(OAuthStateStore, AsyncOAuthStateStore): def __init__( self, *, s3_client: BaseClient, bucket_name: str, expiration_seconds: int, logger: Logger = logging.getLogger(__name__), ): self.s3_client = s3_client self.bucket_name = bucket_name self.expiration_seconds = expiration_seconds self._logger = logger @property def logger(self) -> Logger: if self._logger is None: self._logger = logging.getLogger(__name__) return self._logger async def async_issue(self, *args, **kwargs) -> str: return self.issue(*args, **kwargs) async def async_consume(self, state: str) -> bool: return self.consume(state) def issue(self, *args, **kwargs) -> str: state = str(uuid4()) response = self.s3_client.put_object( Bucket=self.bucket_name, Body=str(time.time()), Key=state, ) self.logger.debug(f"S3 put_object response: {response}") return state def consume(self, state: str) -> bool: try: fetch_response = self.s3_client.get_object( Bucket=self.bucket_name, Key=state, ) self.logger.debug(f"S3 get_object response: {fetch_response}") body = fetch_response["Body"].read().decode("utf-8") created = float(body) expiration = created + self.expiration_seconds still_valid: bool = time.time() < expiration deletion_response = self.s3_client.delete_object( Bucket=self.bucket_name, Key=state, ) self.logger.debug(f"S3 delete_object response: {deletion_response}") return still_valid except Exception as e: message = f"Failed to find any persistent data for state: {state} - {e}" self.logger.warning(message) return False ``` ### Ancestors * [OAuthStateStore](../state_store.html#slack_sdk.oauth.state_store.state_store.OAuthStateStore "slack_sdk.oauth.state_store.state_store.OAuthStateStore") * [AsyncOAuthStateStore](../async_state_store.html#slack_sdk.oauth.state_store.async_state_store.AsyncOAuthStateStore "slack_sdk.oauth.state_store.async_state_store.AsyncOAuthStateStore") ### Instance variables `prop logger : logging.Logger` Expand source code ``` @property def logger(self) -> Logger: if self._logger is None: self._logger = logging.getLogger(__name__) return self._logger ``` ### Methods `async def async_consume(self, state: str) ‑> bool` Expand source code ``` async def async_consume(self, state: str) -> bool: return self.consume(state) ``` `async def async_issue(self, *args, **kwargs) ‑> str` Expand source code ``` async def async_issue(self, *args, **kwargs) -> str: return self.issue(*args, **kwargs) ``` `def consume(self, state: str) ‑> bool` Expand source code ``` def consume(self, state: str) -> bool: try: fetch_response = self.s3_client.get_object( Bucket=self.bucket_name, Key=state, ) self.logger.debug(f"S3 get_object response: {fetch_response}") body = fetch_response["Body"].read().decode("utf-8") created = float(body) expiration = created + self.expiration_seconds still_valid: bool = time.time() < expiration deletion_response = self.s3_client.delete_object( Bucket=self.bucket_name, Key=state, ) self.logger.debug(f"S3 delete_object response: {deletion_response}") return still_valid except Exception as e: message = f"Failed to find any persistent data for state: {state} - {e}" self.logger.warning(message) return False ``` `def issue(self, *args, **kwargs) ‑> str` Expand source code ``` def issue(self, *args, **kwargs) -> str: state = str(uuid4()) response = self.s3_client.put_object( Bucket=self.bucket_name, Body=str(time.time()), Key=state, ) self.logger.debug(f"S3 put_object response: {response}") return state ``` --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/oauth/state_store/file # Module slack_sdk.oauth.state_store.file ## Classes `class FileOAuthStateStore (*, expiration_seconds: int, base_dir: str = '$HOME/.bolt-app-oauth-state', client_id: str | None = None, logger: logging.Logger = )` Expand source code ``` class FileOAuthStateStore(OAuthStateStore, AsyncOAuthStateStore): def __init__( self, *, expiration_seconds: int, base_dir: str = str(Path.home()) + "/.bolt-app-oauth-state", client_id: Optional[str] = None, logger: Logger = logging.getLogger(__name__), ): self.expiration_seconds = expiration_seconds self.base_dir = base_dir self.client_id = client_id if self.client_id is not None: self.base_dir = f"{self.base_dir}/{self.client_id}" self._logger = logger @property def logger(self) -> Logger: if self._logger is None: self._logger = logging.getLogger(__name__) return self._logger async def async_issue(self, *args, **kwargs) -> str: return self.issue(*args, **kwargs) async def async_consume(self, state: str) -> bool: return self.consume(state) def issue(self, *args, **kwargs) -> str: state = str(uuid4()) self._mkdir(self.base_dir) filepath = f"{self.base_dir}/{state}" with open(filepath, "w") as f: content = str(time.time()) f.write(content) return state def consume(self, state: str) -> bool: filepath = f"{self.base_dir}/{state}" try: with open(filepath) as f: created = float(f.read()) expiration = created + self.expiration_seconds still_valid: bool = time.time() < expiration os.remove(filepath) # consume the file by deleting it return still_valid except FileNotFoundError as e: message = f"Failed to find any persistent data for state: {state} - {e}" self.logger.warning(message) return False @staticmethod def _mkdir(path: Union[str, Path]): if isinstance(path, str): path = Path(path) path.mkdir(parents=True, exist_ok=True) ``` ### Ancestors * [OAuthStateStore](../state_store.html#slack_sdk.oauth.state_store.state_store.OAuthStateStore "slack_sdk.oauth.state_store.state_store.OAuthStateStore") * [AsyncOAuthStateStore](../async_state_store.html#slack_sdk.oauth.state_store.async_state_store.AsyncOAuthStateStore "slack_sdk.oauth.state_store.async_state_store.AsyncOAuthStateStore") ### Instance variables `prop logger : logging.Logger` Expand source code ``` @property def logger(self) -> Logger: if self._logger is None: self._logger = logging.getLogger(__name__) return self._logger ``` ### Methods `async def async_consume(self, state: str) ‑> bool` Expand source code ``` async def async_consume(self, state: str) -> bool: return self.consume(state) ``` `async def async_issue(self, *args, **kwargs) ‑> str` Expand source code ``` async def async_issue(self, *args, **kwargs) -> str: return self.issue(*args, **kwargs) ``` `def consume(self, state: str) ‑> bool` Expand source code ``` def consume(self, state: str) -> bool: filepath = f"{self.base_dir}/{state}" try: with open(filepath) as f: created = float(f.read()) expiration = created + self.expiration_seconds still_valid: bool = time.time() < expiration os.remove(filepath) # consume the file by deleting it return still_valid except FileNotFoundError as e: message = f"Failed to find any persistent data for state: {state} - {e}" self.logger.warning(message) return False ``` `def issue(self, *args, **kwargs) ‑> str` Expand source code ``` def issue(self, *args, **kwargs) -> str: state = str(uuid4()) self._mkdir(self.base_dir) filepath = f"{self.base_dir}/{state}" with open(filepath, "w") as f: content = str(time.time()) f.write(content) return state ``` --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/oauth/state_store/sqlalchemy # Module slack_sdk.oauth.state_store.sqlalchemy ## Classes `class AsyncSQLAlchemyOAuthStateStore (expiration_seconds: int, engine: sqlalchemy.ext.asyncio.engine.AsyncEngine, logger: logging.Logger = , table_name: str = 'slack_oauth_states')` Expand source code ``` class AsyncSQLAlchemyOAuthStateStore(AsyncOAuthStateStore): default_table_name: str = "slack_oauth_states" expiration_seconds: int engine: AsyncEngine metadata: MetaData oauth_states: Table @classmethod def build_oauth_states_table(cls, metadata: MetaData, table_name: str) -> Table: return sqlalchemy.Table( table_name, metadata, metadata, Column("id", Integer, primary_key=True, autoincrement=True), Column("state", String(200), nullable=False), Column("expire_at", DateTime, nullable=False), ) def __init__( self, expiration_seconds: int, engine: AsyncEngine, logger: Logger = logging.getLogger(__name__), table_name: str = default_table_name, ): self.expiration_seconds = expiration_seconds self._logger = logger self.engine = engine self.metadata = MetaData() self.oauth_states = self.build_oauth_states_table(self.metadata, table_name) async def create_tables(self): async with self.engine.begin() as conn: await conn.run_sync(self.metadata.create_all) @property def logger(self) -> Logger: if self._logger is None: self._logger = logging.getLogger(__name__) return self._logger async def async_issue(self, *args, **kwargs) -> str: state: str = str(uuid4()) now = normalize_datetime_for_db(datetime.fromtimestamp(time.time() + self.expiration_seconds, tz=timezone.utc)) async with self.engine.begin() as conn: await conn.execute( self.oauth_states.insert(), {"state": state, "expire_at": now}, ) return state async def async_consume(self, state: str) -> bool: try: now = normalize_datetime_for_db(datetime.now(tz=timezone.utc)) async with self.engine.begin() as conn: c = self.oauth_states.c query = self.oauth_states.select().where(and_(c.state == state, c.expire_at > now)) result = await conn.execute(query) for row in result.mappings(): self.logger.debug(f"consume's query result: {row}") await conn.execute(self.oauth_states.delete().where(c.id == row["id"])) return True return False except Exception as e: message = f"Failed to find any persistent data for state: {state} - {e}" self.logger.warning(message) return False ``` ### Ancestors * [AsyncOAuthStateStore](../async_state_store.html#slack_sdk.oauth.state_store.async_state_store.AsyncOAuthStateStore "slack_sdk.oauth.state_store.async_state_store.AsyncOAuthStateStore") ### Class variables `var default_table_name : str` The type of the None singleton. `var engine : sqlalchemy.ext.asyncio.engine.AsyncEngine` The type of the None singleton. `var expiration_seconds : int` The type of the None singleton. `var metadata : sqlalchemy.sql.schema.MetaData` The type of the None singleton. `var oauth_states : sqlalchemy.sql.schema.Table` The type of the None singleton. ### Static methods `def build_oauth_states_table(metadata: sqlalchemy.sql.schema.MetaData, table_name: str) ‑> sqlalchemy.sql.schema.Table` ### Instance variables `prop logger : logging.Logger` Expand source code ``` @property def logger(self) -> Logger: if self._logger is None: self._logger = logging.getLogger(__name__) return self._logger ``` ### Methods `async def async_consume(self, state: str) ‑> bool` Expand source code ``` async def async_consume(self, state: str) -> bool: try: now = normalize_datetime_for_db(datetime.now(tz=timezone.utc)) async with self.engine.begin() as conn: c = self.oauth_states.c query = self.oauth_states.select().where(and_(c.state == state, c.expire_at > now)) result = await conn.execute(query) for row in result.mappings(): self.logger.debug(f"consume's query result: {row}") await conn.execute(self.oauth_states.delete().where(c.id == row["id"])) return True return False except Exception as e: message = f"Failed to find any persistent data for state: {state} - {e}" self.logger.warning(message) return False ``` `async def async_issue(self, *args, **kwargs) ‑> str` Expand source code ``` async def async_issue(self, *args, **kwargs) -> str: state: str = str(uuid4()) now = normalize_datetime_for_db(datetime.fromtimestamp(time.time() + self.expiration_seconds, tz=timezone.utc)) async with self.engine.begin() as conn: await conn.execute( self.oauth_states.insert(), {"state": state, "expire_at": now}, ) return state ``` `async def create_tables(self)` Expand source code ``` async def create_tables(self): async with self.engine.begin() as conn: await conn.run_sync(self.metadata.create_all) ``` `class SQLAlchemyOAuthStateStore (expiration_seconds: int, engine: sqlalchemy.engine.base.Engine, logger: logging.Logger = , table_name: str = 'slack_oauth_states')` Expand source code ``` class SQLAlchemyOAuthStateStore(OAuthStateStore): default_table_name: str = "slack_oauth_states" expiration_seconds: int engine: Engine metadata: MetaData oauth_states: Table @classmethod def build_oauth_states_table(cls, metadata: MetaData, table_name: str) -> Table: return sqlalchemy.Table( table_name, metadata, metadata, Column("id", Integer, primary_key=True, autoincrement=True), Column("state", String(200), nullable=False), Column("expire_at", DateTime, nullable=False), ) def __init__( self, expiration_seconds: int, engine: Engine, logger: Logger = logging.getLogger(__name__), table_name: str = default_table_name, ): self.expiration_seconds = expiration_seconds self._logger = logger self.engine = engine self.metadata = MetaData() self.oauth_states = self.build_oauth_states_table(self.metadata, table_name) def create_tables(self): self.metadata.create_all(self.engine) @property def logger(self) -> Logger: if self._logger is None: self._logger = logging.getLogger(__name__) return self._logger def issue(self, *args, **kwargs) -> str: state: str = str(uuid4()) now = normalize_datetime_for_db(datetime.fromtimestamp(time.time() + self.expiration_seconds, tz=timezone.utc)) with self.engine.begin() as conn: conn.execute( self.oauth_states.insert(), {"state": state, "expire_at": now}, ) return state def consume(self, state: str) -> bool: try: now = normalize_datetime_for_db(datetime.now(tz=timezone.utc)) with self.engine.begin() as conn: c = self.oauth_states.c query = self.oauth_states.select().where(and_(c.state == state, c.expire_at > now)) result = conn.execute(query) for row in result.mappings(): self.logger.debug(f"consume's query result: {row}") conn.execute(self.oauth_states.delete().where(c.id == row["id"])) return True return False except Exception as e: message = f"Failed to find any persistent data for state: {state} - {e}" self.logger.warning(message) return False ``` ### Ancestors * [OAuthStateStore](../state_store.html#slack_sdk.oauth.state_store.state_store.OAuthStateStore "slack_sdk.oauth.state_store.state_store.OAuthStateStore") ### Class variables `var default_table_name : str` The type of the None singleton. `var engine : sqlalchemy.engine.base.Engine` The type of the None singleton. `var expiration_seconds : int` The type of the None singleton. `var metadata : sqlalchemy.sql.schema.MetaData` The type of the None singleton. `var oauth_states : sqlalchemy.sql.schema.Table` The type of the None singleton. ### Static methods `def build_oauth_states_table(metadata: sqlalchemy.sql.schema.MetaData, table_name: str) ‑> sqlalchemy.sql.schema.Table` ### Instance variables `prop logger : logging.Logger` Expand source code ``` @property def logger(self) -> Logger: if self._logger is None: self._logger = logging.getLogger(__name__) return self._logger ``` ### Methods `def consume(self, state: str) ‑> bool` Expand source code ``` def consume(self, state: str) -> bool: try: now = normalize_datetime_for_db(datetime.now(tz=timezone.utc)) with self.engine.begin() as conn: c = self.oauth_states.c query = self.oauth_states.select().where(and_(c.state == state, c.expire_at > now)) result = conn.execute(query) for row in result.mappings(): self.logger.debug(f"consume's query result: {row}") conn.execute(self.oauth_states.delete().where(c.id == row["id"])) return True return False except Exception as e: message = f"Failed to find any persistent data for state: {state} - {e}" self.logger.warning(message) return False ``` `def create_tables(self)` Expand source code ``` def create_tables(self): self.metadata.create_all(self.engine) ``` `def issue(self, *args, **kwargs) ‑> str` Expand source code ``` def issue(self, *args, **kwargs) -> str: state: str = str(uuid4()) now = normalize_datetime_for_db(datetime.fromtimestamp(time.time() + self.expiration_seconds, tz=timezone.utc)) with self.engine.begin() as conn: conn.execute( self.oauth_states.insert(), {"state": state, "expire_at": now}, ) return state ``` --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/oauth/state_store/sqlite3 # Module slack_sdk.oauth.state_store.sqlite3 ## Classes `class SQLite3OAuthStateStore (*, database: str, expiration_seconds: int, logger: logging.Logger = )` Expand source code ``` class SQLite3OAuthStateStore(OAuthStateStore, AsyncOAuthStateStore): def __init__( self, *, database: str, expiration_seconds: int, logger: Logger = logging.getLogger(__name__), ): self.database = database self.expiration_seconds = expiration_seconds self.init_called = False self._logger = logger @property def logger(self) -> Logger: if self._logger is None: self._logger = logging.getLogger(__name__) return self._logger def init(self): try: with sqlite3.connect(database=self.database) as conn: cur = conn.execute("select count(1) from oauth_states;") row_num = cur.fetchone()[0] self.logger.debug(f"{row_num} oauth states are stored in {self.database}") except Exception: self.create_tables() self.init_called = True def connect(self) -> Connection: if not self.init_called: self.init() return sqlite3.connect(database=self.database) def create_tables(self): with sqlite3.connect(database=self.database) as conn: conn.execute(""" create table oauth_states ( id integer primary key autoincrement, state text not null, expire_at datetime not null ); """) self.logger.debug(f"Tables have been created (database: {self.database})") conn.commit() async def async_issue(self, *args, **kwargs) -> str: return self.issue(*args, **kwargs) async def async_consume(self, state: str) -> bool: return self.consume(state) def issue(self, *args, **kwargs) -> str: state: str = str(uuid4()) with self.connect() as conn: parameters = [ state, time.time() + self.expiration_seconds, ] conn.execute("insert into oauth_states (state, expire_at) values (?, ?);", parameters) self.logger.debug(f"issue's insertion result: {parameters} (database: {self.database})") conn.commit() return state def consume(self, state: str) -> bool: try: with self.connect() as conn: cur = conn.execute( "select id, state from oauth_states where state = ? and expire_at > ?;", [state, time.time()], ) row = cur.fetchone() self.logger.debug(f"consume's query result: {row} (database: {self.database})") if row and len(row) > 0: id = row[0] conn.execute("delete from oauth_states where id = ?;", [id]) conn.commit() return True return False except Exception as e: message = f"Failed to find any persistent data for state: {state} - {e}" self.logger.warning(message) return False ``` ### Ancestors * [OAuthStateStore](../state_store.html#slack_sdk.oauth.state_store.state_store.OAuthStateStore "slack_sdk.oauth.state_store.state_store.OAuthStateStore") * [AsyncOAuthStateStore](../async_state_store.html#slack_sdk.oauth.state_store.async_state_store.AsyncOAuthStateStore "slack_sdk.oauth.state_store.async_state_store.AsyncOAuthStateStore") ### Instance variables `prop logger : logging.Logger` Expand source code ``` @property def logger(self) -> Logger: if self._logger is None: self._logger = logging.getLogger(__name__) return self._logger ``` ### Methods `async def async_consume(self, state: str) ‑> bool` Expand source code ``` async def async_consume(self, state: str) -> bool: return self.consume(state) ``` `async def async_issue(self, *args, **kwargs) ‑> str` Expand source code ``` async def async_issue(self, *args, **kwargs) -> str: return self.issue(*args, **kwargs) ``` `def connect(self) ‑> sqlite3.Connection` Expand source code ``` def connect(self) -> Connection: if not self.init_called: self.init() return sqlite3.connect(database=self.database) ``` `def consume(self, state: str) ‑> bool` Expand source code ``` def consume(self, state: str) -> bool: try: with self.connect() as conn: cur = conn.execute( "select id, state from oauth_states where state = ? and expire_at > ?;", [state, time.time()], ) row = cur.fetchone() self.logger.debug(f"consume's query result: {row} (database: {self.database})") if row and len(row) > 0: id = row[0] conn.execute("delete from oauth_states where id = ?;", [id]) conn.commit() return True return False except Exception as e: message = f"Failed to find any persistent data for state: {state} - {e}" self.logger.warning(message) return False ``` `def create_tables(self)` Expand source code ``` def create_tables(self): with sqlite3.connect(database=self.database) as conn: conn.execute(""" create table oauth_states ( id integer primary key autoincrement, state text not null, expire_at datetime not null ); """) self.logger.debug(f"Tables have been created (database: {self.database})") conn.commit() ``` `def init(self)` Expand source code ``` def init(self): try: with sqlite3.connect(database=self.database) as conn: cur = conn.execute("select count(1) from oauth_states;") row_num = cur.fetchone()[0] self.logger.debug(f"{row_num} oauth states are stored in {self.database}") except Exception: self.create_tables() self.init_called = True ``` `def issue(self, *args, **kwargs) ‑> str` Expand source code ``` def issue(self, *args, **kwargs) -> str: state: str = str(uuid4()) with self.connect() as conn: parameters = [ state, time.time() + self.expiration_seconds, ] conn.execute("insert into oauth_states (state, expire_at) values (?, ?);", parameters) self.logger.debug(f"issue's insertion result: {parameters} (database: {self.database})") conn.commit() return state ``` --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/oauth/state_utils # Module slack_sdk.oauth.state_utils ## Classes `class OAuthStateUtils (*, cookie_name: str = 'slack-app-oauth-state', expiration_seconds: int = 600)` Expand source code ``` class OAuthStateUtils: cookie_name: str expiration_seconds: int default_cookie_name: str = "slack-app-oauth-state" default_expiration_seconds: int = 60 * 10 # 10 minutes def __init__( self, *, cookie_name: str = default_cookie_name, expiration_seconds: int = default_expiration_seconds, ): self.cookie_name = cookie_name self.expiration_seconds = expiration_seconds def build_set_cookie_for_new_state(self, state: str) -> str: return f"{self.cookie_name}={state}; " "Secure; " "HttpOnly; " "Path=/; " f"Max-Age={self.expiration_seconds}" def build_set_cookie_for_deletion(self) -> str: return f"{self.cookie_name}=deleted; " "Secure; " "HttpOnly; " "Path=/; " "Expires=Thu, 01 Jan 1970 00:00:00 GMT" def is_valid_browser( self, state: Optional[str], request_headers: Dict[str, Union[str, Sequence[str]]], ) -> bool: if state is None or request_headers is None or request_headers.get("cookie", None) is None: return False cookies = request_headers["cookie"] if isinstance(cookies, str): cookies = [cookies] for cookie in cookies: values = cookie.split(";") for value in values: # handle quoted cookie values (e.g. due to base64 encoding) if value.strip().replace('"', "").replace("'", "") == f"{self.cookie_name}={state}": return True return False ``` ### Class variables `var cookie_name : str` The type of the None singleton. `var default_cookie_name : str` The type of the None singleton. `var default_expiration_seconds : int` The type of the None singleton. `var expiration_seconds : int` The type of the None singleton. ### Methods `def build_set_cookie_for_deletion(self) ‑> str` Expand source code ``` def build_set_cookie_for_deletion(self) -> str: return f"{self.cookie_name}=deleted; " "Secure; " "HttpOnly; " "Path=/; " "Expires=Thu, 01 Jan 1970 00:00:00 GMT" ``` `def build_set_cookie_for_new_state(self, state: str) ‑> str` Expand source code ``` def build_set_cookie_for_new_state(self, state: str) -> str: return f"{self.cookie_name}={state}; " "Secure; " "HttpOnly; " "Path=/; " f"Max-Age={self.expiration_seconds}" ``` `def is_valid_browser(self, state: str | None, request_headers: Dict[str, str | Sequence[str]]) ‑> bool` Expand source code ``` def is_valid_browser( self, state: Optional[str], request_headers: Dict[str, Union[str, Sequence[str]]], ) -> bool: if state is None or request_headers is None or request_headers.get("cookie", None) is None: return False cookies = request_headers["cookie"] if isinstance(cookies, str): cookies = [cookies] for cookie in cookies: values = cookie.split(";") for value in values: # handle quoted cookie values (e.g. due to base64 encoding) if value.strip().replace('"', "").replace("'", "") == f"{self.cookie_name}={state}": return True return False ``` --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/oauth/token_rotation # Module slack_sdk.oauth.token_rotation ## Sub-modules `[slack_sdk.oauth.token_rotation.async_rotator](async_rotator.html "slack_sdk.oauth.token_rotation.async_rotator")` `[slack_sdk.oauth.token_rotation.rotator](rotator.html "slack_sdk.oauth.token_rotation.rotator")` ## Classes `class TokenRotator (*, client_id: str, client_secret: str, client: [WebClient](../../web/client.html#slack_sdk.web.client.WebClient "slack_sdk.web.client.WebClient") | None = None)` Expand source code ``` class TokenRotator: client: WebClient client_id: str client_secret: str def __init__(self, *, client_id: str, client_secret: str, client: Optional[WebClient] = None): self.client = client if client is not None else WebClient(token=None) self.client_id = client_id self.client_secret = client_secret def perform_token_rotation( self, *, installation: Installation, minutes_before_expiration: int = 120, # 2 hours by default ) -> Optional[Installation]: """Performs token rotation if the underlying tokens (bot / user) are expired / expiring. Args: installation: the current installation data minutes_before_expiration: the minutes before the token expiration Returns: None if no rotation is necessary for now. """ # TODO: make the following two calls in parallel for better performance # bot rotated_bot: Optional[Bot] = self.perform_bot_token_rotation( bot=installation.to_bot(), minutes_before_expiration=minutes_before_expiration, ) # user rotated_installation: Optional[Installation] = self.perform_user_token_rotation( installation=installation, minutes_before_expiration=minutes_before_expiration, ) if rotated_bot is not None: if rotated_installation is None: rotated_installation = Installation(**installation.to_dict_for_copying()) rotated_installation.bot_token = rotated_bot.bot_token rotated_installation.bot_refresh_token = rotated_bot.bot_refresh_token rotated_installation.bot_token_expires_at = rotated_bot.bot_token_expires_at return rotated_installation def perform_bot_token_rotation( self, *, bot: Bot, minutes_before_expiration: int = 120, # 2 hours by default ) -> Optional[Bot]: """Performs bot token rotation if the underlying bot token is expired / expiring. Args: bot: the current bot installation data minutes_before_expiration: the minutes before the token expiration Returns: None if no rotation is necessary for now. """ if bot.bot_token_expires_at is None: return None if bot.bot_token_expires_at > time() + minutes_before_expiration * 60: return None try: refresh_response = self.client.oauth_v2_access( client_id=self.client_id, client_secret=self.client_secret, grant_type="refresh_token", refresh_token=bot.bot_refresh_token, ) if refresh_response.get("token_type") != "bot": return None refreshed_bot = Bot(**bot.to_dict_for_copying()) refreshed_bot.bot_token = refresh_response["access_token"] refreshed_bot.bot_refresh_token = refresh_response.get("refresh_token") refreshed_bot.bot_token_expires_at = int(time()) + int(refresh_response["expires_in"]) return refreshed_bot except SlackApiError as e: raise SlackTokenRotationError(e) def perform_user_token_rotation( self, *, installation: Installation, minutes_before_expiration: int = 120, # 2 hours by default ) -> Optional[Installation]: """Performs user token rotation if the underlying user token is expired / expiring. Args: installation: the current installation data minutes_before_expiration: the minutes before the token expiration Returns: None if no rotation is necessary for now. """ if installation.user_token_expires_at is None: return None if installation.user_token_expires_at > time() + minutes_before_expiration * 60: return None try: refresh_response = self.client.oauth_v2_access( client_id=self.client_id, client_secret=self.client_secret, grant_type="refresh_token", refresh_token=installation.user_refresh_token, ) if refresh_response.get("token_type") != "user": return None refreshed_installation = Installation(**installation.to_dict_for_copying()) refreshed_installation.user_token = refresh_response.get("access_token") refreshed_installation.user_refresh_token = refresh_response.get("refresh_token") refreshed_installation.user_token_expires_at = int(time()) + int(refresh_response["expires_in"]) return refreshed_installation except SlackApiError as e: raise SlackTokenRotationError(e) ``` ### Class variables `var client : [WebClient](../../web/client.html#slack_sdk.web.client.WebClient "slack_sdk.web.client.WebClient")` The type of the None singleton. `var client_id : str` The type of the None singleton. `var client_secret : str` The type of the None singleton. ### Methods `def perform_bot_token_rotation(self, *, bot: [Bot](../installation_store/models/bot.html#slack_sdk.oauth.installation_store.models.bot.Bot "slack_sdk.oauth.installation_store.models.bot.Bot"), minutes_before_expiration: int = 120) ‑> [Bot](../installation_store/models/bot.html#slack_sdk.oauth.installation_store.models.bot.Bot "slack_sdk.oauth.installation_store.models.bot.Bot") | None` Expand source code ``` def perform_bot_token_rotation( self, *, bot: Bot, minutes_before_expiration: int = 120, # 2 hours by default ) -> Optional[Bot]: """Performs bot token rotation if the underlying bot token is expired / expiring. Args: bot: the current bot installation data minutes_before_expiration: the minutes before the token expiration Returns: None if no rotation is necessary for now. """ if bot.bot_token_expires_at is None: return None if bot.bot_token_expires_at > time() + minutes_before_expiration * 60: return None try: refresh_response = self.client.oauth_v2_access( client_id=self.client_id, client_secret=self.client_secret, grant_type="refresh_token", refresh_token=bot.bot_refresh_token, ) if refresh_response.get("token_type") != "bot": return None refreshed_bot = Bot(**bot.to_dict_for_copying()) refreshed_bot.bot_token = refresh_response["access_token"] refreshed_bot.bot_refresh_token = refresh_response.get("refresh_token") refreshed_bot.bot_token_expires_at = int(time()) + int(refresh_response["expires_in"]) return refreshed_bot except SlackApiError as e: raise SlackTokenRotationError(e) ``` Performs bot token rotation if the underlying bot token is expired / expiring. ## Args **`bot`** the current bot installation data **`minutes_before_expiration`** the minutes before the token expiration ## Returns None if no rotation is necessary for now. `def perform_token_rotation(self, *, installation: [Installation](../installation_store/models/installation.html#slack_sdk.oauth.installation_store.models.installation.Installation "slack_sdk.oauth.installation_store.models.installation.Installation"), minutes_before_expiration: int = 120) ‑> [Installation](../installation_store/models/installation.html#slack_sdk.oauth.installation_store.models.installation.Installation "slack_sdk.oauth.installation_store.models.installation.Installation") | None` Expand source code ``` def perform_token_rotation( self, *, installation: Installation, minutes_before_expiration: int = 120, # 2 hours by default ) -> Optional[Installation]: """Performs token rotation if the underlying tokens (bot / user) are expired / expiring. Args: installation: the current installation data minutes_before_expiration: the minutes before the token expiration Returns: None if no rotation is necessary for now. """ # TODO: make the following two calls in parallel for better performance # bot rotated_bot: Optional[Bot] = self.perform_bot_token_rotation( bot=installation.to_bot(), minutes_before_expiration=minutes_before_expiration, ) # user rotated_installation: Optional[Installation] = self.perform_user_token_rotation( installation=installation, minutes_before_expiration=minutes_before_expiration, ) if rotated_bot is not None: if rotated_installation is None: rotated_installation = Installation(**installation.to_dict_for_copying()) rotated_installation.bot_token = rotated_bot.bot_token rotated_installation.bot_refresh_token = rotated_bot.bot_refresh_token rotated_installation.bot_token_expires_at = rotated_bot.bot_token_expires_at return rotated_installation ``` Performs token rotation if the underlying tokens (bot / user) are expired / expiring. ## Args **`installation`** the current installation data **`minutes_before_expiration`** the minutes before the token expiration ## Returns None if no rotation is necessary for now. `def perform_user_token_rotation(self, *, installation: [Installation](../installation_store/models/installation.html#slack_sdk.oauth.installation_store.models.installation.Installation "slack_sdk.oauth.installation_store.models.installation.Installation"), minutes_before_expiration: int = 120) ‑> [Installation](../installation_store/models/installation.html#slack_sdk.oauth.installation_store.models.installation.Installation "slack_sdk.oauth.installation_store.models.installation.Installation") | None` Expand source code ``` def perform_user_token_rotation( self, *, installation: Installation, minutes_before_expiration: int = 120, # 2 hours by default ) -> Optional[Installation]: """Performs user token rotation if the underlying user token is expired / expiring. Args: installation: the current installation data minutes_before_expiration: the minutes before the token expiration Returns: None if no rotation is necessary for now. """ if installation.user_token_expires_at is None: return None if installation.user_token_expires_at > time() + minutes_before_expiration * 60: return None try: refresh_response = self.client.oauth_v2_access( client_id=self.client_id, client_secret=self.client_secret, grant_type="refresh_token", refresh_token=installation.user_refresh_token, ) if refresh_response.get("token_type") != "user": return None refreshed_installation = Installation(**installation.to_dict_for_copying()) refreshed_installation.user_token = refresh_response.get("access_token") refreshed_installation.user_refresh_token = refresh_response.get("refresh_token") refreshed_installation.user_token_expires_at = int(time()) + int(refresh_response["expires_in"]) return refreshed_installation except SlackApiError as e: raise SlackTokenRotationError(e) ``` Performs user token rotation if the underlying user token is expired / expiring. ## Args **`installation`** the current installation data **`minutes_before_expiration`** the minutes before the token expiration ## Returns None if no rotation is necessary for now. --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/rtm # Module slack_sdk.rtm A Python module for interacting with Slack's RTM API. ## Sub-modules `[slack_sdk.rtm.v2](v2/index.html "slack_sdk.rtm.v2")` ## Classes `class RTMClient (*, token: str, run_async: bool | None = False, auto_reconnect: bool | None = True, ssl: ssl.SSLContext | None = None, proxy: str | None = None, timeout: int | None = 30, base_url: str | None = 'https://slack.com/api/', connect_method: str | None = None, ping_interval: int | None = 30, loop: asyncio.events.AbstractEventLoop | None = None, headers: dict | None = {})` Expand source code ``` class RTMClient(object): """An RTMClient allows apps to communicate with the Slack Platform's RTM API. The event-driven architecture of this client allows you to simply link callbacks to their corresponding events. When an event occurs this client executes your callback while passing along any information it receives. Attributes: token (str): A string specifying an xoxp or xoxb token. run_async (bool): A boolean specifying if the client should be run in async mode. Default is False. auto_reconnect (bool): When true the client will automatically reconnect when (not manually) disconnected. Default is True. ssl (SSLContext): To use SSL support, pass an SSLContext object here. Default is None. proxy (str): To use proxy support, pass the string of the proxy server. e.g. "http://proxy.com" Authentication credentials can be passed in proxy URL. e.g. "http://user:pass@some.proxy.com" Default is None. timeout (int): The amount of seconds the session should wait before timing out. Default is 30. base_url (str): The base url for all HTTP requests. Note: This is only used in the WebClient. Default is "https://slack.com/api/". connect_method (str): An string specifying if the client will connect with `rtm.connect` or `rtm.start`. Default is `rtm.connect`. ping_interval (int): automatically send "ping" command every specified period of seconds. If set to 0, do not send automatically. Default is 30. loop (AbstractEventLoop): An event loop provided by asyncio. If None is specified we attempt to use the current loop with `get_event_loop`. Default is None. Methods: ping: Sends a ping message over the websocket to Slack. typing: Sends a typing indicator to the specified channel. on: Stores and links callbacks to websocket and Slack events. run_on: Decorator that stores and links callbacks to websocket and Slack events. start: Starts an RTM Session with Slack. stop: Closes the websocket connection and ensures it won't reconnect. Example: ```python import os from slack import RTMClient @RTMClient.run_on(event="message") def say_hello(**payload): data = payload['data'] web_client = payload['web_client'] if 'Hello' in data['text']: channel_id = data['channel'] thread_ts = data['ts'] user = data['user'] web_client.chat_postMessage( channel=channel_id, text=f"Hi <@{user}>!", thread_ts=thread_ts ) slack_token = os.environ["SLACK_API_TOKEN"] rtm_client = RTMClient(token=slack_token) rtm_client.start() ``` Note: The initial state returned when establishing an RTM connection will be available as the data in payload for the 'open' event. This data is not and will not be stored on the RTM Client. Any attributes or methods prefixed with _underscores are intended to be "private" internal use only. They may be changed or removed at anytime. """ _callbacks: DefaultDict = collections.defaultdict(list) def __init__( self, *, token: str, run_async: Optional[bool] = False, auto_reconnect: Optional[bool] = True, ssl: Optional[SSLContext] = None, proxy: Optional[str] = None, timeout: Optional[int] = 30, base_url: Optional[str] = WebClient.BASE_URL, connect_method: Optional[str] = None, ping_interval: Optional[int] = 30, loop: Optional[asyncio.AbstractEventLoop] = None, headers: Optional[dict] = {}, ): self.token = token.strip() self.run_async = run_async self.auto_reconnect = auto_reconnect self.ssl = ssl self.proxy = proxy self.timeout = timeout self.base_url = base_url self.connect_method = connect_method self.ping_interval = ping_interval self.headers = headers self._event_loop = loop or asyncio.get_event_loop() self._web_client = None self._websocket = None self._session = None self._logger = logging.getLogger(__name__) self._last_message_id = 0 self._connection_attempts = 0 self._stopped = False self._web_client = WebClient( token=self.token, base_url=self.base_url, # type: ignore[arg-type] timeout=self.timeout, # type: ignore[arg-type] ssl=self.ssl, proxy=self.proxy, run_async=self.run_async, # type: ignore[arg-type] loop=self._event_loop, session=self._session, headers=self.headers, ) @staticmethod def run_on(*, event: str): """A decorator to store and link a callback to an event.""" def decorator(callback): RTMClient.on(event=event, callback=callback) return callback return decorator @classmethod def on(cls, *, event: str, callback: Callable): """Stores and links the callback(s) to the event. Args: event (str): A string that specifies a Slack or websocket event. e.g. 'channel_joined' or 'open' callback (Callable): Any object or a list of objects that can be called. e.g. or [,] Raises: SlackClientError: The specified callback is not callable. SlackClientError: The callback must accept keyword arguments (**kwargs). """ if isinstance(callback, list): for cb in callback: cls._validate_callback(cb) previous_callbacks = cls._callbacks[event] cls._callbacks[event] = list(set(previous_callbacks + callback)) else: cls._validate_callback(callback) cls._callbacks[event].append(callback) def start(self) -> Union[asyncio.Future, Any]: """Starts an RTM Session with Slack. Makes an authenticated call to Slack's RTM API to retrieve a websocket URL and then connects to the message server. As events stream-in we run any associated callbacks stored on the client. If 'auto_reconnect' is specified we retrieve a new url and reconnect any time the connection is lost unintentionally or an exception is thrown. Raises: SlackApiError: Unable to retrieve RTM URL from Slack. """ # Not yet implemented: Add Windows support for graceful shutdowns. if os.name != "nt" and current_thread() == main_thread(): signals = (signal.SIGHUP, signal.SIGTERM, signal.SIGINT) for s in signals: self._event_loop.add_signal_handler(s, self.stop) future: Future[Any] = asyncio.ensure_future(self._connect_and_read(), loop=self._event_loop) if self.run_async: return future return self._event_loop.run_until_complete(future) def stop(self): """Closes the websocket connection and ensures it won't reconnect. If your application outputs the following errors, call #async_stop() instead and await for the completion on your application side. asyncio/base_events.py:641: RuntimeWarning: coroutine 'ClientWebSocketResponse.close' was never awaited self._ready.clear() """ self._logger.debug("The Slack RTMClient is shutting down.") self._stopped = True self._close_websocket() async def async_stop(self): """Closes the websocket connection and ensures it won't reconnect.""" self._logger.debug("The Slack RTMClient is shutting down.") remaining_futures = self._close_websocket() for future in remaining_futures: await future self._stopped = True def send_over_websocket(self, *, payload: dict): """Sends a message to Slack over the WebSocket connection. Note: The RTM API only supports posting simple messages formatted using our default message formatting mode. It does not support attachments or other message formatting modes. For this reason we recommend users send messages via the Web API methods. e.g. web_client.chat_postMessage() If the message "id" is not specified in the payload, it'll be added. Args: payload (dict): The message to send over the wesocket. e.g. { "id": 1, "type": "typing", "channel": "C024BE91L" } Raises: SlackClientNotConnectedError: Websocket connection is closed. """ return asyncio.ensure_future(self._send_json(payload), loop=self._event_loop) async def _send_json(self, payload): if self._websocket is None or self._event_loop is None: raise client_err.SlackClientNotConnectedError("Websocket connection is closed.") if "id" not in payload: payload["id"] = self._next_msg_id() return await self._websocket.send_json(payload) async def ping(self): """Sends a ping message over the websocket to Slack. Not all web browsers support the WebSocket ping spec, so the RTM protocol also supports ping/pong messages. Raises: SlackClientNotConnectedError: Websocket connection is closed. """ payload = {"id": self._next_msg_id(), "type": "ping"} await self._send_json(payload=payload) async def typing(self, *, channel: str): """Sends a typing indicator to the specified channel. This indicates that this app is currently writing a message to send to a channel. Args: channel (str): The channel id. e.g. 'C024BE91L' Raises: SlackClientNotConnectedError: Websocket connection is closed. """ payload = {"id": self._next_msg_id(), "type": "typing", "channel": channel} await self._send_json(payload=payload) @staticmethod def _validate_callback(callback): """Checks if the specified callback is callable and accepts a kwargs param. Args: callback (obj): Any object or a list of objects that can be called. e.g. Raises: SlackClientError: The specified callback is not callable. SlackClientError: The callback must accept keyword arguments (**kwargs). """ cb_name = callback.__name__ if hasattr(callback, "__name__") else callback if not callable(callback): msg = "The specified callback '{}' is not callable.".format(cb_name) raise client_err.SlackClientError(msg) callback_params = inspect.signature(callback).parameters.values() if not any(param for param in callback_params if param.kind == param.VAR_KEYWORD): msg = "The callback '{}' must accept keyword arguments (**kwargs).".format(cb_name) raise client_err.SlackClientError(msg) def _next_msg_id(self): """Retrieves the next message id. When sending messages to Slack every event should have a unique (for that connection) positive integer ID. Returns: An integer representing the message id. e.g. 98 """ self._last_message_id += 1 return self._last_message_id async def _connect_and_read(self): """Retrieves the WS url and connects to Slack's RTM API. Makes an authenticated call to Slack's Web API to retrieve a websocket URL. Then connects to the message server and reads event messages as they come in. If 'auto_reconnect' is specified we retrieve a new url and reconnect any time the connection is lost unintentionally or an exception is thrown. Raises: SlackApiError: Unable to retrieve RTM URL from Slack. websockets.exceptions: Errors thrown by the 'websockets' library. """ while not self._stopped: try: self._connection_attempts += 1 async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=self.timeout)) as session: self._session = session url, data = await self._retrieve_websocket_info() async with session.ws_connect( url, heartbeat=self.ping_interval, ssl=self.ssl, proxy=self.proxy, ) as websocket: self._logger.debug("The Websocket connection has been opened.") self._websocket = websocket await self._dispatch_event(event="open", data=data) await self._read_messages() # The websocket has been disconnected, or self._stopped is True if not self._stopped and not self.auto_reconnect: self._logger.warning("Not reconnecting the Websocket because auto_reconnect is False") return # No need to wait exponentially here, since the connection was # established OK, but timed out, or was closed remotely except ( client_err.SlackClientNotConnectedError, client_err.SlackApiError, # Not yet implemented: Catch websocket exceptions thrown by aiohttp. ) as exception: await self._dispatch_event(event="error", data=exception) error_code = exception.response.get("error", None) if hasattr(exception, "response") else None if ( self.auto_reconnect and not self._stopped and error_code != "invalid_auth" # "invalid_auth" is unrecoverable ): await self._wait_exponentially(exception) continue self._logger.exception("The Websocket encountered an error. Closing the connection...") self._close_websocket() raise async def _read_messages(self): """Process messages received on the WebSocket connection.""" while not self._stopped and self._websocket is not None: try: # Wait for a message to be received, but timeout after a second so that # we can check if the socket has been closed, or if self._stopped is # True message = await self._websocket.receive(timeout=1) except asyncio.TimeoutError: if not self._websocket.closed: # We didn't receive a message within the timeout interval, but # aiohttp hasn't closed the socket, so ping responses must still be # returning continue self._logger.warning( "Websocket was closed (%s).", self._websocket.close_code if self._websocket else "", ) await self._dispatch_event( event="error", data=self._websocket.exception() if self._websocket else "", ) self._websocket = None await self._dispatch_event(event="close") return if message.type == aiohttp.WSMsgType.TEXT: try: payload = message.json() event = payload.pop("type", "Unknown") await self._dispatch_event(event, data=payload) except Exception as err: data = message.data if message else message self._logger.info(f"Caught a raised exception ({err}) while dispatching a TEXT message ({data})") # Raised exceptions here happen in users' code and were just unhandled. # As they're not intended for closing current WebSocket connection, # this exception should not be propagated to higher level (#_connect_and_read()). continue elif message.type == aiohttp.WSMsgType.ERROR: self._logger.error("Received an error on the websocket: %r", message) await self._dispatch_event(event="error", data=message) elif message.type in ( aiohttp.WSMsgType.CLOSE, aiohttp.WSMsgType.CLOSING, aiohttp.WSMsgType.CLOSED, ): self._logger.warning("Websocket was closed.") self._websocket = None await self._dispatch_event(event="close") else: self._logger.debug("Received unhandled message type: %r", message) async def _dispatch_event(self, event, data=None): """Dispatches the event and executes any associated callbacks. Note: To prevent the app from crashing due to callback errors. We catch all exceptions and send all data to the logger. Args: event (str): The type of event. e.g. 'bot_added' data (dict): The data Slack sent. e.g. { "type": "bot_added", "bot": { "id": "B024BE7LH", "app_id": "A4H1JB4AZ", "name": "hugbot" } } """ if self._logger.level <= logging.DEBUG: self._logger.debug("Received an event: '%s' - %s", event, data) for callback in self._callbacks[event]: self._logger.debug( "Running %s callbacks for event: '%s'", len(self._callbacks[event]), event, ) try: if self._stopped and event not in ["close", "error"]: # Don't run callbacks if client was stopped unless they're # close/error callbacks. break if inspect.iscoroutinefunction(callback): await callback(rtm_client=self, web_client=self._web_client, data=data) else: if self.run_async is True: raise client_err.SlackRequestError( f'The callback "{callback.__name__}" is NOT a coroutine. ' "Running such with run_async=True is unsupported. " "Consider adding async/await to the method " "or going with run_async=False if your app is not really non-blocking." ) payload = { "rtm_client": self, "web_client": self._web_client, "data": data, } callback(**payload) except Exception as err: name = callback.__name__ module = callback.__module__ msg = f"When calling '#{name}()' in the '{module}' module the following error was raised: {err}" self._logger.error(msg) raise async def _retrieve_websocket_info(self): """Retrieves the WebSocket info from Slack. Returns: A tuple of websocket information. e.g. ( "wss://...", { "self": {"id": "U01234ABC","name": "robotoverlord"}, "team": { "domain": "exampledomain", "id": "T123450FP", "name": "ExampleName" } } ) Raises: SlackApiError: Unable to retrieve RTM URL from Slack. """ if self._web_client is None: self._web_client = WebClient( token=self.token, base_url=self.base_url, timeout=self.timeout, ssl=self.ssl, proxy=self.proxy, run_async=True, loop=self._event_loop, session=self._session, headers=self.headers, ) self._logger.debug("Retrieving websocket info.") use_rtm_start = self.connect_method in ["rtm.start", "rtm_start"] if self.run_async: if use_rtm_start: resp = await self._web_client.rtm_start() else: resp = await self._web_client.rtm_connect() else: if use_rtm_start: resp = self._web_client.rtm_start() else: resp = self._web_client.rtm_connect() url = resp.get("url") if url is None: msg = "Unable to retrieve RTM URL from Slack." raise client_err.SlackApiError(message=msg, response=resp) return url, resp.data async def _wait_exponentially(self, exception, max_wait_time=300): """Wait exponentially longer for each connection attempt. Calculate the number of seconds to wait and then add a random number of milliseconds to avoid coincidental synchronized client retries. Wait up to the maximum amount of wait time specified via 'max_wait_time'. However, if Slack returned how long to wait use that. """ if hasattr(exception, "response"): wait_time = exception.response.get("headers", {}).get( "Retry-After", min((2**self._connection_attempts) + random.random(), max_wait_time), ) self._logger.debug("Waiting %s seconds before reconnecting.", wait_time) await asyncio.sleep(float(wait_time)) def _close_websocket(self) -> Sequence[Future]: """Closes the websocket connection.""" futures = [] close_method = getattr(self._websocket, "close", None) if callable(close_method): future = asyncio.ensure_future(close_method(), loop=self._event_loop) futures.append(future) self._websocket = None event_f = asyncio.ensure_future(self._dispatch_event(event="close"), loop=self._event_loop) futures.append(event_f) return futures ``` An RTMClient allows apps to communicate with the Slack Platform's RTM API. The event-driven architecture of this client allows you to simply link callbacks to their corresponding events. When an event occurs this client executes your callback while passing along any information it receives. ## Attributes **`token`** : `str` A string specifying an xoxp or xoxb token. **`run_async`** : `bool` A boolean specifying if the client should be run in async mode. Default is False. **`auto_reconnect`** : `bool` When true the client will automatically reconnect when (not manually) disconnected. Default is True. **`ssl`** : `SSLContext` To use SSL support, pass an SSLContext object here. Default is None. **`proxy`** : `str` To use proxy support, pass the string of the proxy server. e.g. "http://proxy.com" Authentication credentials can be passed in proxy URL. e.g. "http://user:pass@some.proxy.com" Default is None. **`timeout`** : `int` The amount of seconds the session should wait before timing out. Default is 30. **`base_url`** : `str` The base url for all HTTP requests. Note: This is only used in the WebClient. Default is "https://slack.com/api/". **`connect_method`** : `str` An string specifying if the client will connect with `rtm.connect` or `rtm.start`. Default is `rtm.connect`. **`ping_interval`** : `int` automatically send "ping" command every specified period of seconds. If set to 0, do not send automatically. Default is 30. **`loop`** : `AbstractEventLoop` An event loop provided by asyncio. If None is specified we attempt to use the current loop with `get_event_loop`. Default is None. ## Methods ping: Sends a ping message over the websocket to Slack. typing: Sends a typing indicator to the specified channel. on: Stores and links callbacks to websocket and Slack events. run\_on: Decorator that stores and links callbacks to websocket and Slack events. start: Starts an RTM Session with Slack. stop: Closes the websocket connection and ensures it won't reconnect. Example: ```python import os from slack import RTMClient @RTMClient.run_on(event="message") def say_hello(**payload): data = payload['data'] web_client = payload['web_client'] if 'Hello' in data['text']: channel_id = data['channel'] thread_ts = data['ts'] user = data['user'] web_client.chat_postMessage( channel=channel_id, text=f"Hi <@{user}>!", thread_ts=thread_ts ) slack_token = os.environ["SLACK_API_TOKEN"] rtm_client = RTMClient(token=slack_token) rtm_client.start() ``` ## Note The initial state returned when establishing an RTM connection will be available as the data in payload for the 'open' event. This data is not and will not be stored on the RTM Client. Any attributes or methods prefixed with \_underscores are intended to be "private" internal use only. They may be changed or removed at anytime. ### Static methods `def on(*, event: str, callback: Callable)` Stores and links the callback(s) to the event. ## Args **`event`** : `str` A string that specifies a Slack or websocket event. e.g. 'channel\_joined' or 'open' **`callback`** : `Callable` Any object or a list of objects that can be called. e.g. or \[,\] ## Raises `SlackClientError` The specified callback is not callable. `SlackClientError` The callback must accept keyword arguments (\*\*kwargs). `def run_on(*, event: str)` Expand source code ``` @staticmethod def run_on(*, event: str): """A decorator to store and link a callback to an event.""" def decorator(callback): RTMClient.on(event=event, callback=callback) return callback return decorator ``` A decorator to store and link a callback to an event. ### Methods `async def async_stop(self)` Expand source code ``` async def async_stop(self): """Closes the websocket connection and ensures it won't reconnect.""" self._logger.debug("The Slack RTMClient is shutting down.") remaining_futures = self._close_websocket() for future in remaining_futures: await future self._stopped = True ``` Closes the websocket connection and ensures it won't reconnect. `async def ping(self)` Expand source code ``` async def ping(self): """Sends a ping message over the websocket to Slack. Not all web browsers support the WebSocket ping spec, so the RTM protocol also supports ping/pong messages. Raises: SlackClientNotConnectedError: Websocket connection is closed. """ payload = {"id": self._next_msg_id(), "type": "ping"} await self._send_json(payload=payload) ``` Sends a ping message over the websocket to Slack. Not all web browsers support the WebSocket ping spec, so the RTM protocol also supports ping/pong messages. ## Raises `SlackClientNotConnectedError` Websocket connection is closed. `def send_over_websocket(self, *, payload: dict)` Expand source code ``` def send_over_websocket(self, *, payload: dict): """Sends a message to Slack over the WebSocket connection. Note: The RTM API only supports posting simple messages formatted using our default message formatting mode. It does not support attachments or other message formatting modes. For this reason we recommend users send messages via the Web API methods. e.g. web_client.chat_postMessage() If the message "id" is not specified in the payload, it'll be added. Args: payload (dict): The message to send over the wesocket. e.g. { "id": 1, "type": "typing", "channel": "C024BE91L" } Raises: SlackClientNotConnectedError: Websocket connection is closed. """ return asyncio.ensure_future(self._send_json(payload), loop=self._event_loop) ``` Sends a message to Slack over the WebSocket connection. ## Note The RTM API only supports posting simple messages formatted using our default message formatting mode. It does not support attachments or other message formatting modes. For this reason we recommend users send messages via the Web API methods. e.g. web\_client.chat\_postMessage() If the message "id" is not specified in the payload, it'll be added. ## Args **`payload`** : `dict` The message to send over the wesocket. e.g. { "id": 1, "type": "typing", "channel": "C024BE91L" } ## Raises `SlackClientNotConnectedError` Websocket connection is closed. `def start(self) ‑> _asyncio.Future | Any` Expand source code ``` def start(self) -> Union[asyncio.Future, Any]: """Starts an RTM Session with Slack. Makes an authenticated call to Slack's RTM API to retrieve a websocket URL and then connects to the message server. As events stream-in we run any associated callbacks stored on the client. If 'auto_reconnect' is specified we retrieve a new url and reconnect any time the connection is lost unintentionally or an exception is thrown. Raises: SlackApiError: Unable to retrieve RTM URL from Slack. """ # Not yet implemented: Add Windows support for graceful shutdowns. if os.name != "nt" and current_thread() == main_thread(): signals = (signal.SIGHUP, signal.SIGTERM, signal.SIGINT) for s in signals: self._event_loop.add_signal_handler(s, self.stop) future: Future[Any] = asyncio.ensure_future(self._connect_and_read(), loop=self._event_loop) if self.run_async: return future return self._event_loop.run_until_complete(future) ``` Starts an RTM Session with Slack. Makes an authenticated call to Slack's RTM API to retrieve a websocket URL and then connects to the message server. As events stream-in we run any associated callbacks stored on the client. If 'auto\_reconnect' is specified we retrieve a new url and reconnect any time the connection is lost unintentionally or an exception is thrown. ## Raises `SlackApiError` Unable to retrieve RTM URL from Slack. `def stop(self)` Expand source code ``` def stop(self): """Closes the websocket connection and ensures it won't reconnect. If your application outputs the following errors, call #async_stop() instead and await for the completion on your application side. asyncio/base_events.py:641: RuntimeWarning: coroutine 'ClientWebSocketResponse.close' was never awaited self._ready.clear() """ self._logger.debug("The Slack RTMClient is shutting down.") self._stopped = True self._close_websocket() ``` Closes the websocket connection and ensures it won't reconnect. If your application outputs the following errors, call #async\_stop() instead and await for the completion on your application side. asyncio/base\_events.py:641: RuntimeWarning: coroutine 'ClientWebSocketResponse.close' was never awaited self.\_ready.clear() `async def typing(self, *, channel: str)` Expand source code ``` async def typing(self, *, channel: str): """Sends a typing indicator to the specified channel. This indicates that this app is currently writing a message to send to a channel. Args: channel (str): The channel id. e.g. 'C024BE91L' Raises: SlackClientNotConnectedError: Websocket connection is closed. """ payload = {"id": self._next_msg_id(), "type": "typing", "channel": channel} await self._send_json(payload=payload) ``` Sends a typing indicator to the specified channel. This indicates that this app is currently writing a message to send to a channel. ## Args **`channel`** : `str` The channel id. e.g. 'C024BE91L' ## Raises `SlackClientNotConnectedError` Websocket connection is closed. --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/rtm/v2 # Module slack_sdk.rtm.v2 ## Classes `class RTMClient (*, token: str | None = None, web_client: [WebClient](../../web/client.html#slack_sdk.web.client.WebClient "slack_sdk.web.client.WebClient") | None = None, auto_reconnect_enabled: bool = True, ssl: ssl.SSLContext | None = None, proxy: str | None = None, timeout: int = 30, base_url: str = 'https://slack.com/api/', headers: dict | None = None, ping_interval: int = 5, concurrency: int = 10, logger: logging.Logger | None = None, on_message_listeners: List[Callable[[str], None]] | None = None, on_error_listeners: List[Callable[[Exception], None]] | None = None, on_close_listeners: List[Callable[[int, str | None], None]] | None = None, trace_enabled: bool = False, all_message_trace_enabled: bool = False, ping_pong_trace_enabled: bool = False)` Expand source code ``` class RTMClient: token: Optional[str] bot_id: Optional[str] default_auto_reconnect_enabled: bool auto_reconnect_enabled: bool ssl: Optional[SSLContext] proxy: Optional[str] timeout: int base_url: str ping_interval: int logger: Logger web_client: WebClient current_session: Optional[Connection] current_session_state: Optional[ConnectionState] wss_uri: Optional[str] message_queue: Queue message_listeners: List[Callable[["RTMClient", dict], None]] message_processor: IntervalRunner message_workers: ThreadPoolExecutor closed: bool connect_operation_lock: Lock on_message_listeners: List[Callable[[str], None]] on_error_listeners: List[Callable[[Exception], None]] on_close_listeners: List[Callable[[int, Optional[str]], None]] def __init__( self, *, token: Optional[str] = None, web_client: Optional[WebClient] = None, auto_reconnect_enabled: bool = True, ssl: Optional[SSLContext] = None, proxy: Optional[str] = None, timeout: int = 30, base_url: str = WebClient.BASE_URL, headers: Optional[dict] = None, ping_interval: int = 5, concurrency: int = 10, logger: Optional[logging.Logger] = None, on_message_listeners: Optional[List[Callable[[str], None]]] = None, on_error_listeners: Optional[List[Callable[[Exception], None]]] = None, on_close_listeners: Optional[List[Callable[[int, Optional[str]], None]]] = None, trace_enabled: bool = False, all_message_trace_enabled: bool = False, ping_pong_trace_enabled: bool = False, ): self.token = token.strip() if token is not None else None self.bot_id = None self.default_auto_reconnect_enabled = auto_reconnect_enabled # You may want temporarily turn off the auto_reconnect as necessary self.auto_reconnect_enabled = self.default_auto_reconnect_enabled self.ssl = ssl self.proxy = proxy self.timeout = timeout self.base_url = base_url self.headers = headers self.ping_interval = ping_interval self.logger = logger or logging.getLogger(__name__) if self.proxy is None or len(self.proxy.strip()) == 0: env_variable = load_http_proxy_from_env(self.logger) if env_variable is not None: self.proxy = env_variable self.web_client = web_client or WebClient( token=self.token, base_url=self.base_url, timeout=self.timeout, ssl=self.ssl, proxy=self.proxy, headers=self.headers, logger=logger, ) self.on_message_listeners = on_message_listeners or [] self.on_error_listeners = on_error_listeners or [] self.on_close_listeners = on_close_listeners or [] self.trace_enabled = trace_enabled self.all_message_trace_enabled = all_message_trace_enabled self.ping_pong_trace_enabled = ping_pong_trace_enabled self.message_queue = Queue() def goodbye_listener(_self, event: dict): if event.get("type") == "goodbye": message = "Got a goodbye message. Reconnecting to the server ..." self.logger.info(message) self.connect_to_new_endpoint(force=True) self.message_listeners = [goodbye_listener] self.socket_mode_request_listeners = [] self.current_session = None self.current_session_state = ConnectionState() self.current_session_runner = IntervalRunner(self._run_current_session, 0.1).start() self.wss_uri = None self.current_app_monitor_started = False self.current_app_monitor = IntervalRunner( self._monitor_current_session, self.ping_interval, ) self.closed = False self.connect_operation_lock = Lock() self.message_processor = IntervalRunner(self.process_messages, 0.001).start() self.message_workers = ThreadPoolExecutor(max_workers=concurrency) # -------------------------------------------------------------- # Decorator to register listeners # -------------------------------------------------------------- def on(self, event_type: str) -> Callable: """Registers a new event listener. Args: event_type: str representing an event's type (e.g., message, reaction_added) """ def __call__(*args, **kwargs): func = args[0] if func is not None: if isinstance(func, Callable): name = ( func.__name__ if hasattr(func, "__name__") else f"{func.__class__.__module__}.{func.__class__.__name__}" ) inspect_result: inspect.FullArgSpec = inspect.getfullargspec(func) if inspect_result is not None and len(inspect_result.args) != 2: actual_args = ", ".join(inspect_result.args) error = f"The listener '{name}' must accept two args: client, event (actual: {actual_args})" raise SlackClientError(error) def new_message_listener(_self, event: dict): actual_event_type = event.get("type") if event.get("bot_id") == self.bot_id: # SKip the events generated by this bot user return # https://github.com/slackapi/python-slack-sdk/issues/533 if event_type == "*" or (actual_event_type is not None and actual_event_type == event_type): func(_self, event) self.message_listeners.append(new_message_listener) else: error = f"The listener '{func}' is not a Callable (actual: {type(func).__name__})" raise SlackClientError(error) # Not to cause modification to the decorated method return func return __call__ # -------------------------------------------------------------- # Connections # -------------------------------------------------------------- def is_connected(self) -> bool: """Returns True if this client is connected.""" return self.current_session is not None and self.current_session.is_active() def issue_new_wss_url(self) -> str: """Acquires a new WSS URL using rtm.connect API method""" try: api_response = self.web_client.rtm_connect() return api_response["url"] except SlackApiError as e: if e.response["error"] == "ratelimited": delay = int(e.response.headers.get("Retry-After", "30")) # Tier1 self.logger.info(f"Rate limited. Retrying in {delay} seconds...") time.sleep(delay) # Retry to issue a new WSS URL return self.issue_new_wss_url() else: # other errors self.logger.error(f"Failed to retrieve WSS URL: {e}") raise e def connect_to_new_endpoint(self, force: bool = False): """Acquires a new WSS URL and tries to connect to the endpoint.""" with self.connect_operation_lock: if force or not self.is_connected(): self.logger.info("Connecting to a new endpoint...") self.wss_uri = self.issue_new_wss_url() self.connect() self.logger.info("Connected to a new endpoint...") def connect(self): """Starts talking to the RTM server through a WebSocket connection""" if self.bot_id is None: self.bot_id = self.web_client.auth_test()["bot_id"] old_session: Optional[Connection] = self.current_session old_current_session_state: ConnectionState = self.current_session_state if self.wss_uri is None: self.wss_uri = self.issue_new_wss_url() current_session = Connection( url=self.wss_uri, logger=self.logger, ping_interval=self.ping_interval, trace_enabled=self.trace_enabled, all_message_trace_enabled=self.all_message_trace_enabled, ping_pong_trace_enabled=self.ping_pong_trace_enabled, receive_buffer_size=1024, proxy=self.proxy, on_message_listener=self.run_all_message_listeners, on_error_listener=self.run_all_error_listeners, on_close_listener=self.run_all_close_listeners, connection_type_name="RTM", ) current_session.connect() if old_current_session_state is not None: old_current_session_state.terminated = True if old_session is not None: old_session.close() self.current_session = current_session self.current_session_state = ConnectionState() self.auto_reconnect_enabled = self.default_auto_reconnect_enabled if not self.current_app_monitor_started: self.current_app_monitor_started = True self.current_app_monitor.start() self.logger.info(f"A new session has been established (session id: {self.session_id()})") def disconnect(self): """Disconnects the current session.""" self.current_session.disconnect() def close(self) -> None: """ Closes this instance and cleans up underlying resources. After calling this method, this instance is no longer usable. """ self.closed = True self.disconnect() self.current_session.close() def start(self) -> None: """Establishes an RTM connection and blocks the current thread.""" self.connect() Event().wait() def send(self, payload: Union[dict, str]) -> None: if payload is None: return if self.current_session is None or not self.current_session.is_active(): raise SlackClientError("The RTM client is not connected to the Slack servers") if isinstance(payload, str): self.current_session.send(payload) else: self.current_session.send(json.dumps(payload)) # -------------------------------------------------------------- # WS Message Processor # -------------------------------------------------------------- def enqueue_message(self, message: str): self.message_queue.put(message) if self.logger.level <= logging.DEBUG: self.logger.debug(f"A new message enqueued (current queue size: {self.message_queue.qsize()})") def process_message(self): try: raw_message = self.message_queue.get(timeout=1) if self.logger.level <= logging.DEBUG: self.logger.debug(f"A message dequeued (current queue size: {self.message_queue.qsize()})") if raw_message is not None: message: dict = {} if raw_message.startswith("{"): message = json.loads(raw_message) def _run_message_listeners(): self.run_message_listeners(message) self.message_workers.submit(_run_message_listeners) except Empty: pass def process_messages(self) -> None: while not self.closed: try: self.process_message() except Exception as e: self.logger.exception(f"Failed to process a message: {e}") def run_message_listeners(self, message: dict) -> None: type = message.get("type") if self.logger.level <= logging.DEBUG: self.logger.debug(f"Message processing started (type: {type})") try: for listener in self.message_listeners: try: listener(self, message) except Exception as e: self.logger.exception(f"Failed to run a message listener: {e}") except Exception as e: self.logger.exception(f"Failed to run message listeners: {e}") finally: if self.logger.level <= logging.DEBUG: self.logger.debug(f"Message processing completed (type: {type})") # -------------------------------------------------------------- # Internals # -------------------------------------------------------------- def session_id(self) -> Optional[str]: if self.current_session is not None: return self.current_session.session_id return None def run_all_message_listeners(self, message: str): if self.logger.level <= logging.DEBUG: self.logger.debug(f"on_message invoked: (message: {message})") self.enqueue_message(message) for listener in self.on_message_listeners: listener(message) def run_all_error_listeners(self, error: Exception): self.logger.exception( f"on_error invoked (session id: {self.session_id()}, " f"error: {type(error).__name__}, message: {error})" ) for listener in self.on_error_listeners: listener(error) def run_all_close_listeners(self, code: int, reason: Optional[str] = None): if self.logger.level <= logging.DEBUG: self.logger.debug(f"on_close invoked (session id: {self.session_id()})") if self.auto_reconnect_enabled: self.logger.info("Received CLOSE event. Going to reconnect... " f"(session id: {self.session_id()})") self.connect_to_new_endpoint() for listener in self.on_close_listeners: listener(code, reason) def _run_current_session(self): if self.current_session is not None and self.current_session.is_active(): session_id = self.session_id() try: self.logger.info("Starting to receive messages from a new connection" f" (session id: {session_id})") self.current_session_state.terminated = False self.current_session.run_until_completion(self.current_session_state) self.logger.info("Stopped receiving messages from a connection" f" (session id: {session_id})") except Exception as e: self.logger.exception( "Failed to start or stop the current session" f" (session id: {session_id}, error: {e})" ) def _monitor_current_session(self): if self.current_app_monitor_started: try: self.current_session.check_state() if self.auto_reconnect_enabled and (self.current_session is None or not self.current_session.is_active()): self.logger.info( "The session seems to be already closed. Going to reconnect... " f"(session id: {self.session_id()})" ) self.connect_to_new_endpoint() except Exception as e: self.logger.error( "Failed to check the current session or reconnect to the server " f"(session id: {self.session_id()}, error: {type(e).__name__}, message: {e})" ) ``` ### Class variables `var auto_reconnect_enabled : bool` The type of the None singleton. `var base_url : str` The type of the None singleton. `var bot_id : str | None` The type of the None singleton. `var closed : bool` The type of the None singleton. `var connect_operation_lock : _thread.lock` The type of the None singleton. `var current_session : [Connection](../../socket_mode/builtin/connection.html#slack_sdk.socket_mode.builtin.connection.Connection "slack_sdk.socket_mode.builtin.connection.Connection") | None` The type of the None singleton. `var current_session_state : [ConnectionState](../../socket_mode/builtin/connection.html#slack_sdk.socket_mode.builtin.connection.ConnectionState "slack_sdk.socket_mode.builtin.connection.ConnectionState") | None` The type of the None singleton. `var default_auto_reconnect_enabled : bool` The type of the None singleton. `var logger : logging.Logger` The type of the None singleton. `var message_listeners : List[Callable[[[RTMClient](../../rtm_v2/index.html#slack_sdk.rtm_v2.RTMClient "slack_sdk.rtm_v2.RTMClient"), dict], None]]` The type of the None singleton. `var message_processor : [IntervalRunner](../../socket_mode/interval_runner.html#slack_sdk.socket_mode.interval_runner.IntervalRunner "slack_sdk.socket_mode.interval_runner.IntervalRunner")` The type of the None singleton. `var message_queue : queue.Queue` The type of the None singleton. `var message_workers : concurrent.futures.thread.ThreadPoolExecutor` The type of the None singleton. `var on_close_listeners : List[Callable[[int, str | None], None]]` The type of the None singleton. `var on_error_listeners : List[Callable[[Exception], None]]` The type of the None singleton. `var on_message_listeners : List[Callable[[str], None]]` The type of the None singleton. `var ping_interval : int` The type of the None singleton. `var proxy : str | None` The type of the None singleton. `var ssl : ssl.SSLContext | None` The type of the None singleton. `var timeout : int` The type of the None singleton. `var token : str | None` The type of the None singleton. `var web_client : [WebClient](../../web/client.html#slack_sdk.web.client.WebClient "slack_sdk.web.client.WebClient")` The type of the None singleton. `var wss_uri : str | None` The type of the None singleton. ### Methods `def close(self) ‑> None` Expand source code ``` def close(self) -> None: """ Closes this instance and cleans up underlying resources. After calling this method, this instance is no longer usable. """ self.closed = True self.disconnect() self.current_session.close() ``` Closes this instance and cleans up underlying resources. After calling this method, this instance is no longer usable. `def connect(self)` Expand source code ``` def connect(self): """Starts talking to the RTM server through a WebSocket connection""" if self.bot_id is None: self.bot_id = self.web_client.auth_test()["bot_id"] old_session: Optional[Connection] = self.current_session old_current_session_state: ConnectionState = self.current_session_state if self.wss_uri is None: self.wss_uri = self.issue_new_wss_url() current_session = Connection( url=self.wss_uri, logger=self.logger, ping_interval=self.ping_interval, trace_enabled=self.trace_enabled, all_message_trace_enabled=self.all_message_trace_enabled, ping_pong_trace_enabled=self.ping_pong_trace_enabled, receive_buffer_size=1024, proxy=self.proxy, on_message_listener=self.run_all_message_listeners, on_error_listener=self.run_all_error_listeners, on_close_listener=self.run_all_close_listeners, connection_type_name="RTM", ) current_session.connect() if old_current_session_state is not None: old_current_session_state.terminated = True if old_session is not None: old_session.close() self.current_session = current_session self.current_session_state = ConnectionState() self.auto_reconnect_enabled = self.default_auto_reconnect_enabled if not self.current_app_monitor_started: self.current_app_monitor_started = True self.current_app_monitor.start() self.logger.info(f"A new session has been established (session id: {self.session_id()})") ``` Starts talking to the RTM server through a WebSocket connection `def connect_to_new_endpoint(self, force: bool = False)` Expand source code ``` def connect_to_new_endpoint(self, force: bool = False): """Acquires a new WSS URL and tries to connect to the endpoint.""" with self.connect_operation_lock: if force or not self.is_connected(): self.logger.info("Connecting to a new endpoint...") self.wss_uri = self.issue_new_wss_url() self.connect() self.logger.info("Connected to a new endpoint...") ``` Acquires a new WSS URL and tries to connect to the endpoint. `def disconnect(self)` Expand source code ``` def disconnect(self): """Disconnects the current session.""" self.current_session.disconnect() ``` Disconnects the current session. `def enqueue_message(self, message: str)` Expand source code ``` def enqueue_message(self, message: str): self.message_queue.put(message) if self.logger.level <= logging.DEBUG: self.logger.debug(f"A new message enqueued (current queue size: {self.message_queue.qsize()})") ``` `def is_connected(self) ‑> bool` Expand source code ``` def is_connected(self) -> bool: """Returns True if this client is connected.""" return self.current_session is not None and self.current_session.is_active() ``` Returns True if this client is connected. `def issue_new_wss_url(self) ‑> str` Expand source code ``` def issue_new_wss_url(self) -> str: """Acquires a new WSS URL using rtm.connect API method""" try: api_response = self.web_client.rtm_connect() return api_response["url"] except SlackApiError as e: if e.response["error"] == "ratelimited": delay = int(e.response.headers.get("Retry-After", "30")) # Tier1 self.logger.info(f"Rate limited. Retrying in {delay} seconds...") time.sleep(delay) # Retry to issue a new WSS URL return self.issue_new_wss_url() else: # other errors self.logger.error(f"Failed to retrieve WSS URL: {e}") raise e ``` Acquires a new WSS URL using rtm.connect API method `def on(self, event_type: str) ‑> Callable` Expand source code ``` def on(self, event_type: str) -> Callable: """Registers a new event listener. Args: event_type: str representing an event's type (e.g., message, reaction_added) """ def __call__(*args, **kwargs): func = args[0] if func is not None: if isinstance(func, Callable): name = ( func.__name__ if hasattr(func, "__name__") else f"{func.__class__.__module__}.{func.__class__.__name__}" ) inspect_result: inspect.FullArgSpec = inspect.getfullargspec(func) if inspect_result is not None and len(inspect_result.args) != 2: actual_args = ", ".join(inspect_result.args) error = f"The listener '{name}' must accept two args: client, event (actual: {actual_args})" raise SlackClientError(error) def new_message_listener(_self, event: dict): actual_event_type = event.get("type") if event.get("bot_id") == self.bot_id: # SKip the events generated by this bot user return # https://github.com/slackapi/python-slack-sdk/issues/533 if event_type == "*" or (actual_event_type is not None and actual_event_type == event_type): func(_self, event) self.message_listeners.append(new_message_listener) else: error = f"The listener '{func}' is not a Callable (actual: {type(func).__name__})" raise SlackClientError(error) # Not to cause modification to the decorated method return func return __call__ ``` Registers a new event listener. ## Args **`event_type`** str representing an event's type (e.g., message, reaction\_added) `def process_message(self)` Expand source code ``` def process_message(self): try: raw_message = self.message_queue.get(timeout=1) if self.logger.level <= logging.DEBUG: self.logger.debug(f"A message dequeued (current queue size: {self.message_queue.qsize()})") if raw_message is not None: message: dict = {} if raw_message.startswith("{"): message = json.loads(raw_message) def _run_message_listeners(): self.run_message_listeners(message) self.message_workers.submit(_run_message_listeners) except Empty: pass ``` `def process_messages(self) ‑> None` Expand source code ``` def process_messages(self) -> None: while not self.closed: try: self.process_message() except Exception as e: self.logger.exception(f"Failed to process a message: {e}") ``` `def run_all_close_listeners(self, code: int, reason: str | None = None)` Expand source code ``` def run_all_close_listeners(self, code: int, reason: Optional[str] = None): if self.logger.level <= logging.DEBUG: self.logger.debug(f"on_close invoked (session id: {self.session_id()})") if self.auto_reconnect_enabled: self.logger.info("Received CLOSE event. Going to reconnect... " f"(session id: {self.session_id()})") self.connect_to_new_endpoint() for listener in self.on_close_listeners: listener(code, reason) ``` `def run_all_error_listeners(self, error: Exception)` Expand source code ``` def run_all_error_listeners(self, error: Exception): self.logger.exception( f"on_error invoked (session id: {self.session_id()}, " f"error: {type(error).__name__}, message: {error})" ) for listener in self.on_error_listeners: listener(error) ``` `def run_all_message_listeners(self, message: str)` Expand source code ``` def run_all_message_listeners(self, message: str): if self.logger.level <= logging.DEBUG: self.logger.debug(f"on_message invoked: (message: {message})") self.enqueue_message(message) for listener in self.on_message_listeners: listener(message) ``` `def run_message_listeners(self, message: dict) ‑> None` Expand source code ``` def run_message_listeners(self, message: dict) -> None: type = message.get("type") if self.logger.level <= logging.DEBUG: self.logger.debug(f"Message processing started (type: {type})") try: for listener in self.message_listeners: try: listener(self, message) except Exception as e: self.logger.exception(f"Failed to run a message listener: {e}") except Exception as e: self.logger.exception(f"Failed to run message listeners: {e}") finally: if self.logger.level <= logging.DEBUG: self.logger.debug(f"Message processing completed (type: {type})") ``` `def send(self, payload: dict | str) ‑> None` Expand source code ``` def send(self, payload: Union[dict, str]) -> None: if payload is None: return if self.current_session is None or not self.current_session.is_active(): raise SlackClientError("The RTM client is not connected to the Slack servers") if isinstance(payload, str): self.current_session.send(payload) else: self.current_session.send(json.dumps(payload)) ``` `def session_id(self) ‑> str | None` Expand source code ``` def session_id(self) -> Optional[str]: if self.current_session is not None: return self.current_session.session_id return None ``` `def start(self) ‑> None` Expand source code ``` def start(self) -> None: """Establishes an RTM connection and blocks the current thread.""" self.connect() Event().wait() ``` Establishes an RTM connection and blocks the current thread. --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/rtm_v2 # Module slack_sdk.rtm_v2 A Python module for interacting with Slack's RTM API. ## Classes `class RTMClient (*, token: str | None = None, web_client: [WebClient](../web/client.html#slack_sdk.web.client.WebClient "slack_sdk.web.client.WebClient") | None = None, auto_reconnect_enabled: bool = True, ssl: ssl.SSLContext | None = None, proxy: str | None = None, timeout: int = 30, base_url: str = 'https://slack.com/api/', headers: dict | None = None, ping_interval: int = 5, concurrency: int = 10, logger: logging.Logger | None = None, on_message_listeners: List[Callable[[str], None]] | None = None, on_error_listeners: List[Callable[[Exception], None]] | None = None, on_close_listeners: List[Callable[[int, str | None], None]] | None = None, trace_enabled: bool = False, all_message_trace_enabled: bool = False, ping_pong_trace_enabled: bool = False)` Expand source code ``` class RTMClient: token: Optional[str] bot_id: Optional[str] default_auto_reconnect_enabled: bool auto_reconnect_enabled: bool ssl: Optional[SSLContext] proxy: Optional[str] timeout: int base_url: str ping_interval: int logger: Logger web_client: WebClient current_session: Optional[Connection] current_session_state: Optional[ConnectionState] wss_uri: Optional[str] message_queue: Queue message_listeners: List[Callable[["RTMClient", dict], None]] message_processor: IntervalRunner message_workers: ThreadPoolExecutor closed: bool connect_operation_lock: Lock on_message_listeners: List[Callable[[str], None]] on_error_listeners: List[Callable[[Exception], None]] on_close_listeners: List[Callable[[int, Optional[str]], None]] def __init__( self, *, token: Optional[str] = None, web_client: Optional[WebClient] = None, auto_reconnect_enabled: bool = True, ssl: Optional[SSLContext] = None, proxy: Optional[str] = None, timeout: int = 30, base_url: str = WebClient.BASE_URL, headers: Optional[dict] = None, ping_interval: int = 5, concurrency: int = 10, logger: Optional[logging.Logger] = None, on_message_listeners: Optional[List[Callable[[str], None]]] = None, on_error_listeners: Optional[List[Callable[[Exception], None]]] = None, on_close_listeners: Optional[List[Callable[[int, Optional[str]], None]]] = None, trace_enabled: bool = False, all_message_trace_enabled: bool = False, ping_pong_trace_enabled: bool = False, ): self.token = token.strip() if token is not None else None self.bot_id = None self.default_auto_reconnect_enabled = auto_reconnect_enabled # You may want temporarily turn off the auto_reconnect as necessary self.auto_reconnect_enabled = self.default_auto_reconnect_enabled self.ssl = ssl self.proxy = proxy self.timeout = timeout self.base_url = base_url self.headers = headers self.ping_interval = ping_interval self.logger = logger or logging.getLogger(__name__) if self.proxy is None or len(self.proxy.strip()) == 0: env_variable = load_http_proxy_from_env(self.logger) if env_variable is not None: self.proxy = env_variable self.web_client = web_client or WebClient( token=self.token, base_url=self.base_url, timeout=self.timeout, ssl=self.ssl, proxy=self.proxy, headers=self.headers, logger=logger, ) self.on_message_listeners = on_message_listeners or [] self.on_error_listeners = on_error_listeners or [] self.on_close_listeners = on_close_listeners or [] self.trace_enabled = trace_enabled self.all_message_trace_enabled = all_message_trace_enabled self.ping_pong_trace_enabled = ping_pong_trace_enabled self.message_queue = Queue() def goodbye_listener(_self, event: dict): if event.get("type") == "goodbye": message = "Got a goodbye message. Reconnecting to the server ..." self.logger.info(message) self.connect_to_new_endpoint(force=True) self.message_listeners = [goodbye_listener] self.socket_mode_request_listeners = [] self.current_session = None self.current_session_state = ConnectionState() self.current_session_runner = IntervalRunner(self._run_current_session, 0.1).start() self.wss_uri = None self.current_app_monitor_started = False self.current_app_monitor = IntervalRunner( self._monitor_current_session, self.ping_interval, ) self.closed = False self.connect_operation_lock = Lock() self.message_processor = IntervalRunner(self.process_messages, 0.001).start() self.message_workers = ThreadPoolExecutor(max_workers=concurrency) # -------------------------------------------------------------- # Decorator to register listeners # -------------------------------------------------------------- def on(self, event_type: str) -> Callable: """Registers a new event listener. Args: event_type: str representing an event's type (e.g., message, reaction_added) """ def __call__(*args, **kwargs): func = args[0] if func is not None: if isinstance(func, Callable): name = ( func.__name__ if hasattr(func, "__name__") else f"{func.__class__.__module__}.{func.__class__.__name__}" ) inspect_result: inspect.FullArgSpec = inspect.getfullargspec(func) if inspect_result is not None and len(inspect_result.args) != 2: actual_args = ", ".join(inspect_result.args) error = f"The listener '{name}' must accept two args: client, event (actual: {actual_args})" raise SlackClientError(error) def new_message_listener(_self, event: dict): actual_event_type = event.get("type") if event.get("bot_id") == self.bot_id: # SKip the events generated by this bot user return # https://github.com/slackapi/python-slack-sdk/issues/533 if event_type == "*" or (actual_event_type is not None and actual_event_type == event_type): func(_self, event) self.message_listeners.append(new_message_listener) else: error = f"The listener '{func}' is not a Callable (actual: {type(func).__name__})" raise SlackClientError(error) # Not to cause modification to the decorated method return func return __call__ # -------------------------------------------------------------- # Connections # -------------------------------------------------------------- def is_connected(self) -> bool: """Returns True if this client is connected.""" return self.current_session is not None and self.current_session.is_active() def issue_new_wss_url(self) -> str: """Acquires a new WSS URL using rtm.connect API method""" try: api_response = self.web_client.rtm_connect() return api_response["url"] except SlackApiError as e: if e.response["error"] == "ratelimited": delay = int(e.response.headers.get("Retry-After", "30")) # Tier1 self.logger.info(f"Rate limited. Retrying in {delay} seconds...") time.sleep(delay) # Retry to issue a new WSS URL return self.issue_new_wss_url() else: # other errors self.logger.error(f"Failed to retrieve WSS URL: {e}") raise e def connect_to_new_endpoint(self, force: bool = False): """Acquires a new WSS URL and tries to connect to the endpoint.""" with self.connect_operation_lock: if force or not self.is_connected(): self.logger.info("Connecting to a new endpoint...") self.wss_uri = self.issue_new_wss_url() self.connect() self.logger.info("Connected to a new endpoint...") def connect(self): """Starts talking to the RTM server through a WebSocket connection""" if self.bot_id is None: self.bot_id = self.web_client.auth_test()["bot_id"] old_session: Optional[Connection] = self.current_session old_current_session_state: ConnectionState = self.current_session_state if self.wss_uri is None: self.wss_uri = self.issue_new_wss_url() current_session = Connection( url=self.wss_uri, logger=self.logger, ping_interval=self.ping_interval, trace_enabled=self.trace_enabled, all_message_trace_enabled=self.all_message_trace_enabled, ping_pong_trace_enabled=self.ping_pong_trace_enabled, receive_buffer_size=1024, proxy=self.proxy, on_message_listener=self.run_all_message_listeners, on_error_listener=self.run_all_error_listeners, on_close_listener=self.run_all_close_listeners, connection_type_name="RTM", ) current_session.connect() if old_current_session_state is not None: old_current_session_state.terminated = True if old_session is not None: old_session.close() self.current_session = current_session self.current_session_state = ConnectionState() self.auto_reconnect_enabled = self.default_auto_reconnect_enabled if not self.current_app_monitor_started: self.current_app_monitor_started = True self.current_app_monitor.start() self.logger.info(f"A new session has been established (session id: {self.session_id()})") def disconnect(self): """Disconnects the current session.""" self.current_session.disconnect() def close(self) -> None: """ Closes this instance and cleans up underlying resources. After calling this method, this instance is no longer usable. """ self.closed = True self.disconnect() self.current_session.close() def start(self) -> None: """Establishes an RTM connection and blocks the current thread.""" self.connect() Event().wait() def send(self, payload: Union[dict, str]) -> None: if payload is None: return if self.current_session is None or not self.current_session.is_active(): raise SlackClientError("The RTM client is not connected to the Slack servers") if isinstance(payload, str): self.current_session.send(payload) else: self.current_session.send(json.dumps(payload)) # -------------------------------------------------------------- # WS Message Processor # -------------------------------------------------------------- def enqueue_message(self, message: str): self.message_queue.put(message) if self.logger.level <= logging.DEBUG: self.logger.debug(f"A new message enqueued (current queue size: {self.message_queue.qsize()})") def process_message(self): try: raw_message = self.message_queue.get(timeout=1) if self.logger.level <= logging.DEBUG: self.logger.debug(f"A message dequeued (current queue size: {self.message_queue.qsize()})") if raw_message is not None: message: dict = {} if raw_message.startswith("{"): message = json.loads(raw_message) def _run_message_listeners(): self.run_message_listeners(message) self.message_workers.submit(_run_message_listeners) except Empty: pass def process_messages(self) -> None: while not self.closed: try: self.process_message() except Exception as e: self.logger.exception(f"Failed to process a message: {e}") def run_message_listeners(self, message: dict) -> None: type = message.get("type") if self.logger.level <= logging.DEBUG: self.logger.debug(f"Message processing started (type: {type})") try: for listener in self.message_listeners: try: listener(self, message) except Exception as e: self.logger.exception(f"Failed to run a message listener: {e}") except Exception as e: self.logger.exception(f"Failed to run message listeners: {e}") finally: if self.logger.level <= logging.DEBUG: self.logger.debug(f"Message processing completed (type: {type})") # -------------------------------------------------------------- # Internals # -------------------------------------------------------------- def session_id(self) -> Optional[str]: if self.current_session is not None: return self.current_session.session_id return None def run_all_message_listeners(self, message: str): if self.logger.level <= logging.DEBUG: self.logger.debug(f"on_message invoked: (message: {message})") self.enqueue_message(message) for listener in self.on_message_listeners: listener(message) def run_all_error_listeners(self, error: Exception): self.logger.exception( f"on_error invoked (session id: {self.session_id()}, " f"error: {type(error).__name__}, message: {error})" ) for listener in self.on_error_listeners: listener(error) def run_all_close_listeners(self, code: int, reason: Optional[str] = None): if self.logger.level <= logging.DEBUG: self.logger.debug(f"on_close invoked (session id: {self.session_id()})") if self.auto_reconnect_enabled: self.logger.info("Received CLOSE event. Going to reconnect... " f"(session id: {self.session_id()})") self.connect_to_new_endpoint() for listener in self.on_close_listeners: listener(code, reason) def _run_current_session(self): if self.current_session is not None and self.current_session.is_active(): session_id = self.session_id() try: self.logger.info("Starting to receive messages from a new connection" f" (session id: {session_id})") self.current_session_state.terminated = False self.current_session.run_until_completion(self.current_session_state) self.logger.info("Stopped receiving messages from a connection" f" (session id: {session_id})") except Exception as e: self.logger.exception( "Failed to start or stop the current session" f" (session id: {session_id}, error: {e})" ) def _monitor_current_session(self): if self.current_app_monitor_started: try: self.current_session.check_state() if self.auto_reconnect_enabled and (self.current_session is None or not self.current_session.is_active()): self.logger.info( "The session seems to be already closed. Going to reconnect... " f"(session id: {self.session_id()})" ) self.connect_to_new_endpoint() except Exception as e: self.logger.error( "Failed to check the current session or reconnect to the server " f"(session id: {self.session_id()}, error: {type(e).__name__}, message: {e})" ) ``` ### Class variables `var auto_reconnect_enabled : bool` The type of the None singleton. `var base_url : str` The type of the None singleton. `var bot_id : str | None` The type of the None singleton. `var closed : bool` The type of the None singleton. `var connect_operation_lock : _thread.lock` The type of the None singleton. `var current_session : [Connection](../socket_mode/builtin/connection.html#slack_sdk.socket_mode.builtin.connection.Connection "slack_sdk.socket_mode.builtin.connection.Connection") | None` The type of the None singleton. `var current_session_state : [ConnectionState](../socket_mode/builtin/connection.html#slack_sdk.socket_mode.builtin.connection.ConnectionState "slack_sdk.socket_mode.builtin.connection.ConnectionState") | None` The type of the None singleton. `var default_auto_reconnect_enabled : bool` The type of the None singleton. `var logger : logging.Logger` The type of the None singleton. `var message_listeners : List[Callable[[[RTMClient](#slack_sdk.rtm_v2.RTMClient "slack_sdk.rtm_v2.RTMClient"), dict], None]]` The type of the None singleton. `var message_processor : [IntervalRunner](../socket_mode/interval_runner.html#slack_sdk.socket_mode.interval_runner.IntervalRunner "slack_sdk.socket_mode.interval_runner.IntervalRunner")` The type of the None singleton. `var message_queue : queue.Queue` The type of the None singleton. `var message_workers : concurrent.futures.thread.ThreadPoolExecutor` The type of the None singleton. `var on_close_listeners : List[Callable[[int, str | None], None]]` The type of the None singleton. `var on_error_listeners : List[Callable[[Exception], None]]` The type of the None singleton. `var on_message_listeners : List[Callable[[str], None]]` The type of the None singleton. `var ping_interval : int` The type of the None singleton. `var proxy : str | None` The type of the None singleton. `var ssl : ssl.SSLContext | None` The type of the None singleton. `var timeout : int` The type of the None singleton. `var token : str | None` The type of the None singleton. `var web_client : [WebClient](../web/client.html#slack_sdk.web.client.WebClient "slack_sdk.web.client.WebClient")` The type of the None singleton. `var wss_uri : str | None` The type of the None singleton. ### Methods `def close(self) ‑> None` Expand source code ``` def close(self) -> None: """ Closes this instance and cleans up underlying resources. After calling this method, this instance is no longer usable. """ self.closed = True self.disconnect() self.current_session.close() ``` Closes this instance and cleans up underlying resources. After calling this method, this instance is no longer usable. `def connect(self)` Expand source code ``` def connect(self): """Starts talking to the RTM server through a WebSocket connection""" if self.bot_id is None: self.bot_id = self.web_client.auth_test()["bot_id"] old_session: Optional[Connection] = self.current_session old_current_session_state: ConnectionState = self.current_session_state if self.wss_uri is None: self.wss_uri = self.issue_new_wss_url() current_session = Connection( url=self.wss_uri, logger=self.logger, ping_interval=self.ping_interval, trace_enabled=self.trace_enabled, all_message_trace_enabled=self.all_message_trace_enabled, ping_pong_trace_enabled=self.ping_pong_trace_enabled, receive_buffer_size=1024, proxy=self.proxy, on_message_listener=self.run_all_message_listeners, on_error_listener=self.run_all_error_listeners, on_close_listener=self.run_all_close_listeners, connection_type_name="RTM", ) current_session.connect() if old_current_session_state is not None: old_current_session_state.terminated = True if old_session is not None: old_session.close() self.current_session = current_session self.current_session_state = ConnectionState() self.auto_reconnect_enabled = self.default_auto_reconnect_enabled if not self.current_app_monitor_started: self.current_app_monitor_started = True self.current_app_monitor.start() self.logger.info(f"A new session has been established (session id: {self.session_id()})") ``` Starts talking to the RTM server through a WebSocket connection `def connect_to_new_endpoint(self, force: bool = False)` Expand source code ``` def connect_to_new_endpoint(self, force: bool = False): """Acquires a new WSS URL and tries to connect to the endpoint.""" with self.connect_operation_lock: if force or not self.is_connected(): self.logger.info("Connecting to a new endpoint...") self.wss_uri = self.issue_new_wss_url() self.connect() self.logger.info("Connected to a new endpoint...") ``` Acquires a new WSS URL and tries to connect to the endpoint. `def disconnect(self)` Expand source code ``` def disconnect(self): """Disconnects the current session.""" self.current_session.disconnect() ``` Disconnects the current session. `def enqueue_message(self, message: str)` Expand source code ``` def enqueue_message(self, message: str): self.message_queue.put(message) if self.logger.level <= logging.DEBUG: self.logger.debug(f"A new message enqueued (current queue size: {self.message_queue.qsize()})") ``` `def is_connected(self) ‑> bool` Expand source code ``` def is_connected(self) -> bool: """Returns True if this client is connected.""" return self.current_session is not None and self.current_session.is_active() ``` Returns True if this client is connected. `def issue_new_wss_url(self) ‑> str` Expand source code ``` def issue_new_wss_url(self) -> str: """Acquires a new WSS URL using rtm.connect API method""" try: api_response = self.web_client.rtm_connect() return api_response["url"] except SlackApiError as e: if e.response["error"] == "ratelimited": delay = int(e.response.headers.get("Retry-After", "30")) # Tier1 self.logger.info(f"Rate limited. Retrying in {delay} seconds...") time.sleep(delay) # Retry to issue a new WSS URL return self.issue_new_wss_url() else: # other errors self.logger.error(f"Failed to retrieve WSS URL: {e}") raise e ``` Acquires a new WSS URL using rtm.connect API method `def on(self, event_type: str) ‑> Callable` Expand source code ``` def on(self, event_type: str) -> Callable: """Registers a new event listener. Args: event_type: str representing an event's type (e.g., message, reaction_added) """ def __call__(*args, **kwargs): func = args[0] if func is not None: if isinstance(func, Callable): name = ( func.__name__ if hasattr(func, "__name__") else f"{func.__class__.__module__}.{func.__class__.__name__}" ) inspect_result: inspect.FullArgSpec = inspect.getfullargspec(func) if inspect_result is not None and len(inspect_result.args) != 2: actual_args = ", ".join(inspect_result.args) error = f"The listener '{name}' must accept two args: client, event (actual: {actual_args})" raise SlackClientError(error) def new_message_listener(_self, event: dict): actual_event_type = event.get("type") if event.get("bot_id") == self.bot_id: # SKip the events generated by this bot user return # https://github.com/slackapi/python-slack-sdk/issues/533 if event_type == "*" or (actual_event_type is not None and actual_event_type == event_type): func(_self, event) self.message_listeners.append(new_message_listener) else: error = f"The listener '{func}' is not a Callable (actual: {type(func).__name__})" raise SlackClientError(error) # Not to cause modification to the decorated method return func return __call__ ``` Registers a new event listener. ## Args **`event_type`** str representing an event's type (e.g., message, reaction\_added) `def process_message(self)` Expand source code ``` def process_message(self): try: raw_message = self.message_queue.get(timeout=1) if self.logger.level <= logging.DEBUG: self.logger.debug(f"A message dequeued (current queue size: {self.message_queue.qsize()})") if raw_message is not None: message: dict = {} if raw_message.startswith("{"): message = json.loads(raw_message) def _run_message_listeners(): self.run_message_listeners(message) self.message_workers.submit(_run_message_listeners) except Empty: pass ``` `def process_messages(self) ‑> None` Expand source code ``` def process_messages(self) -> None: while not self.closed: try: self.process_message() except Exception as e: self.logger.exception(f"Failed to process a message: {e}") ``` `def run_all_close_listeners(self, code: int, reason: str | None = None)` Expand source code ``` def run_all_close_listeners(self, code: int, reason: Optional[str] = None): if self.logger.level <= logging.DEBUG: self.logger.debug(f"on_close invoked (session id: {self.session_id()})") if self.auto_reconnect_enabled: self.logger.info("Received CLOSE event. Going to reconnect... " f"(session id: {self.session_id()})") self.connect_to_new_endpoint() for listener in self.on_close_listeners: listener(code, reason) ``` `def run_all_error_listeners(self, error: Exception)` Expand source code ``` def run_all_error_listeners(self, error: Exception): self.logger.exception( f"on_error invoked (session id: {self.session_id()}, " f"error: {type(error).__name__}, message: {error})" ) for listener in self.on_error_listeners: listener(error) ``` `def run_all_message_listeners(self, message: str)` Expand source code ``` def run_all_message_listeners(self, message: str): if self.logger.level <= logging.DEBUG: self.logger.debug(f"on_message invoked: (message: {message})") self.enqueue_message(message) for listener in self.on_message_listeners: listener(message) ``` `def run_message_listeners(self, message: dict) ‑> None` Expand source code ``` def run_message_listeners(self, message: dict) -> None: type = message.get("type") if self.logger.level <= logging.DEBUG: self.logger.debug(f"Message processing started (type: {type})") try: for listener in self.message_listeners: try: listener(self, message) except Exception as e: self.logger.exception(f"Failed to run a message listener: {e}") except Exception as e: self.logger.exception(f"Failed to run message listeners: {e}") finally: if self.logger.level <= logging.DEBUG: self.logger.debug(f"Message processing completed (type: {type})") ``` `def send(self, payload: dict | str) ‑> None` Expand source code ``` def send(self, payload: Union[dict, str]) -> None: if payload is None: return if self.current_session is None or not self.current_session.is_active(): raise SlackClientError("The RTM client is not connected to the Slack servers") if isinstance(payload, str): self.current_session.send(payload) else: self.current_session.send(json.dumps(payload)) ``` `def session_id(self) ‑> str | None` Expand source code ``` def session_id(self) -> Optional[str]: if self.current_session is not None: return self.current_session.session_id return None ``` `def start(self) ‑> None` Expand source code ``` def start(self) -> None: """Establishes an RTM connection and blocks the current thread.""" self.connect() Event().wait() ``` Establishes an RTM connection and blocks the current thread. --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/scim # Module slack_sdk.scim SCIM API is a set of APIs for provisioning and managing user accounts and groups. SCIM is used by Single Sign-On (SSO) services and identity providers to manage people across a variety of tools, including Slack. Refer to [https://docs.slack.dev/tools/python-slack-sdk/scim](https://docs.slack.dev/tools/python-slack-sdk/scim) for details. ## Sub-modules `[slack_sdk.scim.async_client](async_client.html "slack_sdk.scim.async_client")` `[slack_sdk.scim.v1](v1/index.html "slack_sdk.scim.v1")` SCIM API is a set of APIs for provisioning and managing user accounts and groups. SCIM is used by Single Sign-On (SSO) services and identity providers … ## Classes `class Group (*, display_name: str | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None = , id: str | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None = , members: List[[GroupMember](v1/group.html#slack_sdk.scim.v1.group.GroupMember "slack_sdk.scim.v1.group.GroupMember")] | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None = , meta: [GroupMeta](v1/group.html#slack_sdk.scim.v1.group.GroupMeta "slack_sdk.scim.v1.group.GroupMeta") | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None = , schemas: List[str] | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None = , **kwargs)` Expand source code ``` class Group: display_name: Union[Optional[str], DefaultArg] id: Union[Optional[str], DefaultArg] members: Union[Optional[List[GroupMember]], DefaultArg] meta: Union[Optional[GroupMeta], DefaultArg] schemas: Union[Optional[List[str]], DefaultArg] unknown_fields: Dict[str, Any] def __init__( self, *, display_name: Union[Optional[str], DefaultArg] = NotGiven, id: Union[Optional[str], DefaultArg] = NotGiven, members: Union[Optional[List[GroupMember]], DefaultArg] = NotGiven, meta: Union[Optional[GroupMeta], DefaultArg] = NotGiven, schemas: Union[Optional[List[str]], DefaultArg] = NotGiven, **kwargs, ) -> None: self.display_name = display_name self.id = id self.members = ( [a if isinstance(a, GroupMember) else GroupMember(**a) for a in members] if _is_iterable(members) else members ) self.meta = GroupMeta(**meta) if meta is not None and isinstance(meta, dict) else meta self.schemas = schemas self.unknown_fields = kwargs def to_dict(self): return _to_dict_without_not_given(self) def __repr__(self): return f"" ``` ### Class variables `var display_name : str | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None` The type of the None singleton. `var id : str | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None` The type of the None singleton. `var members : List[[GroupMember](v1/group.html#slack_sdk.scim.v1.group.GroupMember "slack_sdk.scim.v1.group.GroupMember")] | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None` The type of the None singleton. `var meta : [GroupMeta](v1/group.html#slack_sdk.scim.v1.group.GroupMeta "slack_sdk.scim.v1.group.GroupMeta") | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None` The type of the None singleton. `var schemas : List[str] | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None` The type of the None singleton. `var unknown_fields : Dict[str, Any]` The type of the None singleton. ### Methods `def to_dict(self)` Expand source code ``` def to_dict(self): return _to_dict_without_not_given(self) ``` `class ReadGroupResponse (underlying: [SCIMResponse](v1/response.html#slack_sdk.scim.v1.response.SCIMResponse "slack_sdk.scim.v1.response.SCIMResponse"))` Expand source code ``` class ReadGroupResponse(SCIMResponse): group: Group @property def group(self) -> Group: return Group(**self.snake_cased_body) def __init__(self, underlying: SCIMResponse): self.underlying = underlying self.url = underlying.url self.status_code = underlying.status_code self.headers = underlying.headers self.raw_body = underlying.raw_body self.body = underlying.body self._snake_cased_body = None ``` ### Ancestors * [SCIMResponse](v1/response.html#slack_sdk.scim.v1.response.SCIMResponse "slack_sdk.scim.v1.response.SCIMResponse") ### Instance variables `prop group : [Group](v1/group.html#slack_sdk.scim.v1.group.Group "slack_sdk.scim.v1.group.Group")` Expand source code ``` @property def group(self) -> Group: return Group(**self.snake_cased_body) ``` ### Inherited members * `**[SCIMResponse](v1/response.html#slack_sdk.scim.v1.response.SCIMResponse "slack_sdk.scim.v1.response.SCIMResponse")**`: * `[body](v1/response.html#slack_sdk.scim.v1.response.SCIMResponse.body "slack_sdk.scim.v1.response.SCIMResponse.body")` * `[headers](v1/response.html#slack_sdk.scim.v1.response.SCIMResponse.headers "slack_sdk.scim.v1.response.SCIMResponse.headers")` * `[raw_body](v1/response.html#slack_sdk.scim.v1.response.SCIMResponse.raw_body "slack_sdk.scim.v1.response.SCIMResponse.raw_body")` * `[status_code](v1/response.html#slack_sdk.scim.v1.response.SCIMResponse.status_code "slack_sdk.scim.v1.response.SCIMResponse.status_code")` * `[url](v1/response.html#slack_sdk.scim.v1.response.SCIMResponse.url "slack_sdk.scim.v1.response.SCIMResponse.url")` `class ReadUserResponse (underlying: [SCIMResponse](v1/response.html#slack_sdk.scim.v1.response.SCIMResponse "slack_sdk.scim.v1.response.SCIMResponse"))` Expand source code ``` class ReadUserResponse(SCIMResponse): user: User @property def user(self) -> User: return User(**self.snake_cased_body) def __init__(self, underlying: SCIMResponse): self.underlying = underlying self.url = underlying.url self.status_code = underlying.status_code self.headers = underlying.headers self.raw_body = underlying.raw_body self.body = underlying.body self._snake_cased_body = None ``` ### Ancestors * [SCIMResponse](v1/response.html#slack_sdk.scim.v1.response.SCIMResponse "slack_sdk.scim.v1.response.SCIMResponse") ### Instance variables `prop user : [User](v1/user.html#slack_sdk.scim.v1.user.User "slack_sdk.scim.v1.user.User")` Expand source code ``` @property def user(self) -> User: return User(**self.snake_cased_body) ``` ### Inherited members * `**[SCIMResponse](v1/response.html#slack_sdk.scim.v1.response.SCIMResponse "slack_sdk.scim.v1.response.SCIMResponse")**`: * `[body](v1/response.html#slack_sdk.scim.v1.response.SCIMResponse.body "slack_sdk.scim.v1.response.SCIMResponse.body")` * `[headers](v1/response.html#slack_sdk.scim.v1.response.SCIMResponse.headers "slack_sdk.scim.v1.response.SCIMResponse.headers")` * `[raw_body](v1/response.html#slack_sdk.scim.v1.response.SCIMResponse.raw_body "slack_sdk.scim.v1.response.SCIMResponse.raw_body")` * `[status_code](v1/response.html#slack_sdk.scim.v1.response.SCIMResponse.status_code "slack_sdk.scim.v1.response.SCIMResponse.status_code")` * `[url](v1/response.html#slack_sdk.scim.v1.response.SCIMResponse.url "slack_sdk.scim.v1.response.SCIMResponse.url")` `class SCIMClient (token: str, timeout: int = 30, ssl: ssl.SSLContext | None = None, proxy: str | None = None, base_url: str = 'https://api.slack.com/scim/v1/', default_headers: Dict[str, str] | None = None, user_agent_prefix: str | None = None, user_agent_suffix: str | None = None, logger: logging.Logger | None = None, retry_handlers: List[[RetryHandler](../http_retry/handler.html#slack_sdk.http_retry.handler.RetryHandler "slack_sdk.http_retry.handler.RetryHandler")] | None = None)` Expand source code ``` class SCIMClient: BASE_URL = "https://api.slack.com/scim/v1/" token: str timeout: int ssl: Optional[SSLContext] proxy: Optional[str] base_url: str default_headers: Dict[str, str] logger: logging.Logger retry_handlers: List[RetryHandler] def __init__( self, token: str, timeout: int = 30, ssl: Optional[SSLContext] = None, proxy: Optional[str] = None, base_url: str = BASE_URL, default_headers: Optional[Dict[str, str]] = None, user_agent_prefix: Optional[str] = None, user_agent_suffix: Optional[str] = None, logger: Optional[logging.Logger] = None, retry_handlers: Optional[List[RetryHandler]] = None, ): """API client for SCIM API See https://docs.slack.dev/admins/scim-api/ for more details Args: token: An admin user's token, which starts with `xoxp-` timeout: Request timeout (in seconds) ssl: `ssl.SSLContext` to use for requests proxy: Proxy URL (e.g., `localhost:9000`, `http://localhost:9000`) base_url: The base URL for API calls default_headers: Request headers to add to all requests user_agent_prefix: Prefix for User-Agent header value user_agent_suffix: Suffix for User-Agent header value logger: Custom logger retry_handlers: Retry handlers """ self.token = token self.timeout = timeout self.ssl = ssl self.proxy = proxy self.base_url = base_url self.default_headers = default_headers if default_headers else {} self.default_headers["User-Agent"] = get_user_agent(user_agent_prefix, user_agent_suffix) self.logger = logger if logger is not None else logging.getLogger(__name__) self.retry_handlers = retry_handlers if retry_handlers is not None else default_retry_handlers() if self.proxy is None or len(self.proxy.strip()) == 0: env_variable = load_http_proxy_from_env(self.logger) if env_variable is not None: self.proxy = env_variable # ------------------------- # Users # ------------------------- def search_users( self, *, # Pagination required as of August 30, 2019. count: int, start_index: int, filter: Optional[str] = None, ) -> SearchUsersResponse: return SearchUsersResponse( self.api_call( http_verb="GET", path="Users", query_params={ "filter": filter, "count": count, "startIndex": start_index, }, ) ) def read_user(self, id: str) -> ReadUserResponse: return ReadUserResponse(self.api_call(http_verb="GET", path=f"Users/{quote(id)}")) def create_user(self, user: Union[Dict[str, Any], User]) -> UserCreateResponse: return UserCreateResponse( self.api_call( http_verb="POST", path="Users", body_params=user.to_dict() if isinstance(user, User) else _to_dict_without_not_given(user), ) ) def patch_user(self, id: str, partial_user: Union[Dict[str, Any], User]) -> UserPatchResponse: return UserPatchResponse( self.api_call( http_verb="PATCH", path=f"Users/{quote(id)}", body_params=( partial_user.to_dict() if isinstance(partial_user, User) else _to_dict_without_not_given(partial_user) ), ) ) def update_user(self, user: Union[Dict[str, Any], User]) -> UserUpdateResponse: user_id = user.id if isinstance(user, User) else user["id"] return UserUpdateResponse( self.api_call( http_verb="PUT", path=f"Users/{quote(user_id)}", body_params=user.to_dict() if isinstance(user, User) else _to_dict_without_not_given(user), ) ) def delete_user(self, id: str) -> UserDeleteResponse: return UserDeleteResponse( self.api_call( http_verb="DELETE", path=f"Users/{quote(id)}", ) ) # ------------------------- # Groups # ------------------------- def search_groups( self, *, # Pagination required as of August 30, 2019. count: int, start_index: int, filter: Optional[str] = None, ) -> SearchGroupsResponse: return SearchGroupsResponse( self.api_call( http_verb="GET", path="Groups", query_params={ "filter": filter, "count": count, "startIndex": start_index, }, ) ) def read_group(self, id: str) -> ReadGroupResponse: return ReadGroupResponse(self.api_call(http_verb="GET", path=f"Groups/{quote(id)}")) def create_group(self, group: Union[Dict[str, Any], Group]) -> GroupCreateResponse: return GroupCreateResponse( self.api_call( http_verb="POST", path="Groups", body_params=group.to_dict() if isinstance(group, Group) else _to_dict_without_not_given(group), ) ) def patch_group(self, id: str, partial_group: Union[Dict[str, Any], Group]) -> GroupPatchResponse: return GroupPatchResponse( self.api_call( http_verb="PATCH", path=f"Groups/{quote(id)}", body_params=( partial_group.to_dict() if isinstance(partial_group, Group) else _to_dict_without_not_given(partial_group) ), ) ) def update_group(self, group: Union[Dict[str, Any], Group]) -> GroupUpdateResponse: group_id = group.id if isinstance(group, Group) else group["id"] return GroupUpdateResponse( self.api_call( http_verb="PUT", path=f"Groups/{quote(group_id)}", body_params=group.to_dict() if isinstance(group, Group) else _to_dict_without_not_given(group), ) ) def delete_group(self, id: str) -> GroupDeleteResponse: return GroupDeleteResponse( self.api_call( http_verb="DELETE", path=f"Groups/{quote(id)}", ) ) # ------------------------- def api_call( self, *, http_verb: str, path: str, query_params: Optional[Dict[str, Any]] = None, body_params: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None, ) -> SCIMResponse: """Performs a Slack API request and returns the result.""" url = f"{self.base_url}{path}" query = _build_query(query_params) if len(query) > 0: url += f"?{query}" return self._perform_http_request( http_verb=http_verb, url=url, body=body_params, headers=_build_request_headers( token=self.token, default_headers=self.default_headers, additional_headers=headers, ), ) def _perform_http_request( self, *, http_verb: str = "GET", url: str, body: Optional[Dict[str, Any]] = None, headers: Dict[str, str], ) -> SCIMResponse: if body is not None: if body.get("schemas") is None: body["schemas"] = ["urn:scim:schemas:core:1.0"] body = json.dumps(body) headers["Content-Type"] = "application/json;charset=utf-8" if self.logger.level <= logging.DEBUG: headers_for_logging = {k: "(redacted)" if k.lower() == "authorization" else v for k, v in headers.items()} self.logger.debug(f"Sending a request - {http_verb} url: {url}, body: {body}, headers: {headers_for_logging}") # NOTE: Intentionally ignore the `http_verb` here # Slack APIs accepts any API method requests with POST methods req = Request( method=http_verb, url=url, data=body.encode("utf-8") if body is not None else None, headers=headers, ) resp = None last_error = None retry_state = RetryState() counter_for_safety = 0 while counter_for_safety < 100: counter_for_safety += 1 # If this is a retry, the next try started here. We can reset the flag. retry_state.next_attempt_requested = False try: resp = self._perform_http_request_internal(url, req) # The resp is a 200 OK response return resp except HTTPError as e: # read the response body here charset = e.headers.get_content_charset() or "utf-8" response_body: str = e.read().decode(charset) # As adding new values to HTTPError#headers can be ignored, building a new dict object here response_headers = dict(e.headers.items()) resp = SCIMResponse( url=url, status_code=e.code, raw_body=response_body, headers=response_headers, ) if e.code == 429: # for backward-compatibility with WebClient (v.2.5.0 or older) if "retry-after" not in resp.headers and "Retry-After" in resp.headers: resp.headers["retry-after"] = resp.headers["Retry-After"] if "Retry-After" not in resp.headers and "retry-after" in resp.headers: resp.headers["Retry-After"] = resp.headers["retry-after"] _debug_log_response(self.logger, resp) # Try to find a retry handler for this error retry_request = RetryHttpRequest.from_urllib_http_request(req) retry_response = RetryHttpResponse( status_code=e.code, headers={k: [v] for k, v in e.headers.items()}, data=response_body.encode("utf-8") if response_body is not None else None, ) for handler in self.retry_handlers: if handler.can_retry( state=retry_state, request=retry_request, response=retry_response, error=e, ): if self.logger.level <= logging.DEBUG: self.logger.info( f"A retry handler found: {type(handler).__name__} for {req.method} {req.full_url} - {e}" ) handler.prepare_for_next_attempt( state=retry_state, request=retry_request, response=retry_response, error=e, ) break if retry_state.next_attempt_requested is False: return resp except Exception as err: last_error = err self.logger.error(f"Failed to send a request to Slack API server: {err}") # Try to find a retry handler for this error retry_request = RetryHttpRequest.from_urllib_http_request(req) for handler in self.retry_handlers: if handler.can_retry( state=retry_state, request=retry_request, response=None, error=err, ): if self.logger.level <= logging.DEBUG: self.logger.info( f"A retry handler found: {type(handler).__name__} for {req.method} {req.full_url} - {err}" ) handler.prepare_for_next_attempt( state=retry_state, request=retry_request, response=None, error=err, ) self.logger.info(f"Going to retry the same request: {req.method} {req.full_url}") break if retry_state.next_attempt_requested is False: raise err if resp is not None: return resp raise last_error def _perform_http_request_internal(self, url: str, req: Request) -> SCIMResponse: opener: Optional[OpenerDirector] = None # for security (BAN-B310) if url.lower().startswith("http"): if self.proxy is not None: if isinstance(self.proxy, str): opener = urllib.request.build_opener( ProxyHandler({"http": self.proxy, "https": self.proxy}), HTTPSHandler(context=self.ssl), ) else: raise SlackRequestError(f"Invalid proxy detected: {self.proxy} must be a str value") else: raise SlackRequestError(f"Invalid URL detected: {url}") # NOTE: BAN-B310 is already checked above http_resp: Optional[HTTPResponse] = None if opener: http_resp = opener.open(req, timeout=self.timeout) else: http_resp = urlopen(req, context=self.ssl, timeout=self.timeout) charset: str = http_resp.headers.get_content_charset() or "utf-8" response_body: str = http_resp.read().decode(charset) resp = SCIMResponse( url=url, status_code=http_resp.status, raw_body=response_body, headers=http_resp.headers, ) _debug_log_response(self.logger, resp) return resp ``` API client for SCIM API See [https://docs.slack.dev/admins/scim-api/](https://docs.slack.dev/admins/scim-api/) for more details ## Args **`token`** An admin user's token, which starts with `xoxp-` **`timeout`** Request timeout (in seconds) **`ssl`** `ssl.SSLContext` to use for requests **`proxy`** Proxy URL (e.g., `localhost:9000`, `http://localhost:9000`) **`base_url`** The base URL for API calls **`default_headers`** Request headers to add to all requests **`user_agent_prefix`** Prefix for User-Agent header value **`user_agent_suffix`** Suffix for User-Agent header value **`logger`** Custom logger **`retry_handlers`** Retry handlers ### Class variables `var BASE_URL` The type of the None singleton. `var base_url : str` The type of the None singleton. `var default_headers : Dict[str, str]` The type of the None singleton. `var logger : logging.Logger` The type of the None singleton. `var proxy : str | None` The type of the None singleton. `var retry_handlers : List[[RetryHandler](../http_retry/handler.html#slack_sdk.http_retry.handler.RetryHandler "slack_sdk.http_retry.handler.RetryHandler")]` The type of the None singleton. `var ssl : ssl.SSLContext | None` The type of the None singleton. `var timeout : int` The type of the None singleton. `var token : str` The type of the None singleton. ### Methods `def api_call(self, *, http_verb: str, path: str, query_params: Dict[str, Any] | None = None, body_params: Dict[str, Any] | None = None, headers: Dict[str, str] | None = None) ‑> [SCIMResponse](v1/response.html#slack_sdk.scim.v1.response.SCIMResponse "slack_sdk.scim.v1.response.SCIMResponse")` Expand source code ``` def api_call( self, *, http_verb: str, path: str, query_params: Optional[Dict[str, Any]] = None, body_params: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None, ) -> SCIMResponse: """Performs a Slack API request and returns the result.""" url = f"{self.base_url}{path}" query = _build_query(query_params) if len(query) > 0: url += f"?{query}" return self._perform_http_request( http_verb=http_verb, url=url, body=body_params, headers=_build_request_headers( token=self.token, default_headers=self.default_headers, additional_headers=headers, ), ) ``` Performs a Slack API request and returns the result. `def create_group(self, group: Dict[str, Any] | [Group](v1/group.html#slack_sdk.scim.v1.group.Group "slack_sdk.scim.v1.group.Group")) ‑> [GroupCreateResponse](v1/response.html#slack_sdk.scim.v1.response.GroupCreateResponse "slack_sdk.scim.v1.response.GroupCreateResponse")` Expand source code ``` def create_group(self, group: Union[Dict[str, Any], Group]) -> GroupCreateResponse: return GroupCreateResponse( self.api_call( http_verb="POST", path="Groups", body_params=group.to_dict() if isinstance(group, Group) else _to_dict_without_not_given(group), ) ) ``` `def create_user(self, user: Dict[str, Any] | [User](v1/user.html#slack_sdk.scim.v1.user.User "slack_sdk.scim.v1.user.User")) ‑> [UserCreateResponse](v1/response.html#slack_sdk.scim.v1.response.UserCreateResponse "slack_sdk.scim.v1.response.UserCreateResponse")` Expand source code ``` def create_user(self, user: Union[Dict[str, Any], User]) -> UserCreateResponse: return UserCreateResponse( self.api_call( http_verb="POST", path="Users", body_params=user.to_dict() if isinstance(user, User) else _to_dict_without_not_given(user), ) ) ``` `def delete_group(self, id: str) ‑> [GroupDeleteResponse](v1/response.html#slack_sdk.scim.v1.response.GroupDeleteResponse "slack_sdk.scim.v1.response.GroupDeleteResponse")` Expand source code ``` def delete_group(self, id: str) -> GroupDeleteResponse: return GroupDeleteResponse( self.api_call( http_verb="DELETE", path=f"Groups/{quote(id)}", ) ) ``` `def delete_user(self, id: str) ‑> [UserDeleteResponse](v1/response.html#slack_sdk.scim.v1.response.UserDeleteResponse "slack_sdk.scim.v1.response.UserDeleteResponse")` Expand source code ``` def delete_user(self, id: str) -> UserDeleteResponse: return UserDeleteResponse( self.api_call( http_verb="DELETE", path=f"Users/{quote(id)}", ) ) ``` `def patch_group(self, id: str, partial_group: Dict[str, Any] | [Group](v1/group.html#slack_sdk.scim.v1.group.Group "slack_sdk.scim.v1.group.Group")) ‑> [GroupPatchResponse](v1/response.html#slack_sdk.scim.v1.response.GroupPatchResponse "slack_sdk.scim.v1.response.GroupPatchResponse")` Expand source code ``` def patch_group(self, id: str, partial_group: Union[Dict[str, Any], Group]) -> GroupPatchResponse: return GroupPatchResponse( self.api_call( http_verb="PATCH", path=f"Groups/{quote(id)}", body_params=( partial_group.to_dict() if isinstance(partial_group, Group) else _to_dict_without_not_given(partial_group) ), ) ) ``` `def patch_user(self, id: str, partial_user: Dict[str, Any] | [User](v1/user.html#slack_sdk.scim.v1.user.User "slack_sdk.scim.v1.user.User")) ‑> [UserPatchResponse](v1/response.html#slack_sdk.scim.v1.response.UserPatchResponse "slack_sdk.scim.v1.response.UserPatchResponse")` Expand source code ``` def patch_user(self, id: str, partial_user: Union[Dict[str, Any], User]) -> UserPatchResponse: return UserPatchResponse( self.api_call( http_verb="PATCH", path=f"Users/{quote(id)}", body_params=( partial_user.to_dict() if isinstance(partial_user, User) else _to_dict_without_not_given(partial_user) ), ) ) ``` `def read_group(self, id: str) ‑> [ReadGroupResponse](v1/response.html#slack_sdk.scim.v1.response.ReadGroupResponse "slack_sdk.scim.v1.response.ReadGroupResponse")` Expand source code ``` def read_group(self, id: str) -> ReadGroupResponse: return ReadGroupResponse(self.api_call(http_verb="GET", path=f"Groups/{quote(id)}")) ``` `def read_user(self, id: str) ‑> [ReadUserResponse](v1/response.html#slack_sdk.scim.v1.response.ReadUserResponse "slack_sdk.scim.v1.response.ReadUserResponse")` Expand source code ``` def read_user(self, id: str) -> ReadUserResponse: return ReadUserResponse(self.api_call(http_verb="GET", path=f"Users/{quote(id)}")) ``` `def search_groups(self, *, count: int, start_index: int, filter: str | None = None) ‑> [SearchGroupsResponse](v1/response.html#slack_sdk.scim.v1.response.SearchGroupsResponse "slack_sdk.scim.v1.response.SearchGroupsResponse")` Expand source code ``` def search_groups( self, *, # Pagination required as of August 30, 2019. count: int, start_index: int, filter: Optional[str] = None, ) -> SearchGroupsResponse: return SearchGroupsResponse( self.api_call( http_verb="GET", path="Groups", query_params={ "filter": filter, "count": count, "startIndex": start_index, }, ) ) ``` `def search_users(self, *, count: int, start_index: int, filter: str | None = None) ‑> [SearchUsersResponse](v1/response.html#slack_sdk.scim.v1.response.SearchUsersResponse "slack_sdk.scim.v1.response.SearchUsersResponse")` Expand source code ``` def search_users( self, *, # Pagination required as of August 30, 2019. count: int, start_index: int, filter: Optional[str] = None, ) -> SearchUsersResponse: return SearchUsersResponse( self.api_call( http_verb="GET", path="Users", query_params={ "filter": filter, "count": count, "startIndex": start_index, }, ) ) ``` `def update_group(self, group: Dict[str, Any] | [Group](v1/group.html#slack_sdk.scim.v1.group.Group "slack_sdk.scim.v1.group.Group")) ‑> [GroupUpdateResponse](v1/response.html#slack_sdk.scim.v1.response.GroupUpdateResponse "slack_sdk.scim.v1.response.GroupUpdateResponse")` Expand source code ``` def update_group(self, group: Union[Dict[str, Any], Group]) -> GroupUpdateResponse: group_id = group.id if isinstance(group, Group) else group["id"] return GroupUpdateResponse( self.api_call( http_verb="PUT", path=f"Groups/{quote(group_id)}", body_params=group.to_dict() if isinstance(group, Group) else _to_dict_without_not_given(group), ) ) ``` `def update_user(self, user: Dict[str, Any] | [User](v1/user.html#slack_sdk.scim.v1.user.User "slack_sdk.scim.v1.user.User")) ‑> [UserUpdateResponse](v1/response.html#slack_sdk.scim.v1.response.UserUpdateResponse "slack_sdk.scim.v1.response.UserUpdateResponse")` Expand source code ``` def update_user(self, user: Union[Dict[str, Any], User]) -> UserUpdateResponse: user_id = user.id if isinstance(user, User) else user["id"] return UserUpdateResponse( self.api_call( http_verb="PUT", path=f"Users/{quote(user_id)}", body_params=user.to_dict() if isinstance(user, User) else _to_dict_without_not_given(user), ) ) ``` `class SCIMResponse (*, url: str, status_code: int, raw_body: str | None, headers: dict)` Expand source code ``` class SCIMResponse: url: str status_code: int headers: Dict[str, Any] raw_body: Optional[str] body: Optional[Dict[str, Any]] snake_cased_body: Optional[Dict[str, Any]] errors: Optional[Errors] @property def snake_cased_body(self) -> Optional[Dict[str, Any]]: if self._snake_cased_body is None: self._snake_cased_body = _to_snake_cased(self.body) return self._snake_cased_body @property def errors(self) -> Optional[Errors]: errors = self.snake_cased_body.get("errors") if errors is None: return None return Errors(**errors) def __init__( self, *, url: str, status_code: int, raw_body: Optional[str], headers: dict, ): self.url = url self.status_code = status_code self.headers = headers self.raw_body = raw_body self.body = json.loads(raw_body) if raw_body is not None and raw_body.startswith("{") else None self._snake_cased_body = None # build this when it's accessed for the first time def __repr__(self): dict_value = {} for key, value in vars(self).items(): dict_value[key] = value.to_dict() if hasattr(value, "to_dict") else value if dict_value: return f"" else: return self.__str__() ``` ### Subclasses * [GroupCreateResponse](v1/response.html#slack_sdk.scim.v1.response.GroupCreateResponse "slack_sdk.scim.v1.response.GroupCreateResponse") * [GroupDeleteResponse](v1/response.html#slack_sdk.scim.v1.response.GroupDeleteResponse "slack_sdk.scim.v1.response.GroupDeleteResponse") * [GroupPatchResponse](v1/response.html#slack_sdk.scim.v1.response.GroupPatchResponse "slack_sdk.scim.v1.response.GroupPatchResponse") * [GroupUpdateResponse](v1/response.html#slack_sdk.scim.v1.response.GroupUpdateResponse "slack_sdk.scim.v1.response.GroupUpdateResponse") * [ReadGroupResponse](v1/response.html#slack_sdk.scim.v1.response.ReadGroupResponse "slack_sdk.scim.v1.response.ReadGroupResponse") * [ReadUserResponse](v1/response.html#slack_sdk.scim.v1.response.ReadUserResponse "slack_sdk.scim.v1.response.ReadUserResponse") * [SearchGroupsResponse](v1/response.html#slack_sdk.scim.v1.response.SearchGroupsResponse "slack_sdk.scim.v1.response.SearchGroupsResponse") * [SearchUsersResponse](v1/response.html#slack_sdk.scim.v1.response.SearchUsersResponse "slack_sdk.scim.v1.response.SearchUsersResponse") * [UserCreateResponse](v1/response.html#slack_sdk.scim.v1.response.UserCreateResponse "slack_sdk.scim.v1.response.UserCreateResponse") * [UserDeleteResponse](v1/response.html#slack_sdk.scim.v1.response.UserDeleteResponse "slack_sdk.scim.v1.response.UserDeleteResponse") * [UserPatchResponse](v1/response.html#slack_sdk.scim.v1.response.UserPatchResponse "slack_sdk.scim.v1.response.UserPatchResponse") * [UserUpdateResponse](v1/response.html#slack_sdk.scim.v1.response.UserUpdateResponse "slack_sdk.scim.v1.response.UserUpdateResponse") ### Class variables `var body : Dict[str, Any] | None` The type of the None singleton. `var headers : Dict[str, Any]` The type of the None singleton. `var raw_body : str | None` The type of the None singleton. `var status_code : int` The type of the None singleton. `var url : str` The type of the None singleton. ### Instance variables `prop errors : [Errors](v1/response.html#slack_sdk.scim.v1.response.Errors "slack_sdk.scim.v1.response.Errors") | None` Expand source code ``` @property def errors(self) -> Optional[Errors]: errors = self.snake_cased_body.get("errors") if errors is None: return None return Errors(**errors) ``` `prop snake_cased_body : Dict[str, Any] | None` Expand source code ``` @property def snake_cased_body(self) -> Optional[Dict[str, Any]]: if self._snake_cased_body is None: self._snake_cased_body = _to_snake_cased(self.body) return self._snake_cased_body ``` `class SearchGroupsResponse (underlying: [SCIMResponse](v1/response.html#slack_sdk.scim.v1.response.SCIMResponse "slack_sdk.scim.v1.response.SCIMResponse"))` Expand source code ``` class SearchGroupsResponse(SCIMResponse): groups: List[Group] @property def groups(self) -> List[Group]: return [Group(**r) for r in self.snake_cased_body.get("resources")] def __init__(self, underlying: SCIMResponse): self.underlying = underlying self.url = underlying.url self.status_code = underlying.status_code self.headers = underlying.headers self.raw_body = underlying.raw_body self.body = underlying.body self._snake_cased_body = None ``` ### Ancestors * [SCIMResponse](v1/response.html#slack_sdk.scim.v1.response.SCIMResponse "slack_sdk.scim.v1.response.SCIMResponse") ### Instance variables `prop groups : List[[Group](v1/group.html#slack_sdk.scim.v1.group.Group "slack_sdk.scim.v1.group.Group")]` Expand source code ``` @property def groups(self) -> List[Group]: return [Group(**r) for r in self.snake_cased_body.get("resources")] ``` ### Inherited members * `**[SCIMResponse](v1/response.html#slack_sdk.scim.v1.response.SCIMResponse "slack_sdk.scim.v1.response.SCIMResponse")**`: * `[body](v1/response.html#slack_sdk.scim.v1.response.SCIMResponse.body "slack_sdk.scim.v1.response.SCIMResponse.body")` * `[headers](v1/response.html#slack_sdk.scim.v1.response.SCIMResponse.headers "slack_sdk.scim.v1.response.SCIMResponse.headers")` * `[raw_body](v1/response.html#slack_sdk.scim.v1.response.SCIMResponse.raw_body "slack_sdk.scim.v1.response.SCIMResponse.raw_body")` * `[status_code](v1/response.html#slack_sdk.scim.v1.response.SCIMResponse.status_code "slack_sdk.scim.v1.response.SCIMResponse.status_code")` * `[url](v1/response.html#slack_sdk.scim.v1.response.SCIMResponse.url "slack_sdk.scim.v1.response.SCIMResponse.url")` `class SearchUsersResponse (underlying: [SCIMResponse](v1/response.html#slack_sdk.scim.v1.response.SCIMResponse "slack_sdk.scim.v1.response.SCIMResponse"))` Expand source code ``` class SearchUsersResponse(SCIMResponse): users: List[User] @property def users(self) -> List[User]: return [User(**r) for r in self.snake_cased_body.get("resources")] def __init__(self, underlying: SCIMResponse): self.underlying = underlying self.url = underlying.url self.status_code = underlying.status_code self.headers = underlying.headers self.raw_body = underlying.raw_body self.body = underlying.body self._snake_cased_body = None ``` ### Ancestors * [SCIMResponse](v1/response.html#slack_sdk.scim.v1.response.SCIMResponse "slack_sdk.scim.v1.response.SCIMResponse") ### Instance variables `prop users : List[[User](v1/user.html#slack_sdk.scim.v1.user.User "slack_sdk.scim.v1.user.User")]` Expand source code ``` @property def users(self) -> List[User]: return [User(**r) for r in self.snake_cased_body.get("resources")] ``` ### Inherited members * `**[SCIMResponse](v1/response.html#slack_sdk.scim.v1.response.SCIMResponse "slack_sdk.scim.v1.response.SCIMResponse")**`: * `[body](v1/response.html#slack_sdk.scim.v1.response.SCIMResponse.body "slack_sdk.scim.v1.response.SCIMResponse.body")` * `[headers](v1/response.html#slack_sdk.scim.v1.response.SCIMResponse.headers "slack_sdk.scim.v1.response.SCIMResponse.headers")` * `[raw_body](v1/response.html#slack_sdk.scim.v1.response.SCIMResponse.raw_body "slack_sdk.scim.v1.response.SCIMResponse.raw_body")` * `[status_code](v1/response.html#slack_sdk.scim.v1.response.SCIMResponse.status_code "slack_sdk.scim.v1.response.SCIMResponse.status_code")` * `[url](v1/response.html#slack_sdk.scim.v1.response.SCIMResponse.url "slack_sdk.scim.v1.response.SCIMResponse.url")` `class User (*, active: bool | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None = , addresses: List[[UserAddress](v1/user.html#slack_sdk.scim.v1.user.UserAddress "slack_sdk.scim.v1.user.UserAddress") | Dict[str, Any]] | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None = , display_name: str | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None = , emails: List[[TypeAndValue](v1/types.html#slack_sdk.scim.v1.types.TypeAndValue "slack_sdk.scim.v1.types.TypeAndValue") | Dict[str, Any]] | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None = , external_id: str | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None = , groups: List[[UserGroup](v1/user.html#slack_sdk.scim.v1.user.UserGroup "slack_sdk.scim.v1.user.UserGroup") | Dict[str, Any]] | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None = , id: str | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None = , meta: [UserMeta](v1/user.html#slack_sdk.scim.v1.user.UserMeta "slack_sdk.scim.v1.user.UserMeta") | Dict[str, Any] | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None = , name: [UserName](v1/user.html#slack_sdk.scim.v1.user.UserName "slack_sdk.scim.v1.user.UserName") | Dict[str, Any] | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None = , nick_name: str | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None = , phone_numbers: List[[TypeAndValue](v1/types.html#slack_sdk.scim.v1.types.TypeAndValue "slack_sdk.scim.v1.types.TypeAndValue") | Dict[str, Any]] | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None = , photos: List[[UserPhoto](v1/user.html#slack_sdk.scim.v1.user.UserPhoto "slack_sdk.scim.v1.user.UserPhoto") | Dict[str, Any]] | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None = , profile_url: str | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None = , roles: List[[TypeAndValue](v1/types.html#slack_sdk.scim.v1.types.TypeAndValue "slack_sdk.scim.v1.types.TypeAndValue") | Dict[str, Any]] | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None = , schemas: List[str] | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None = , timezone: str | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None = , title: str | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None = , user_name: str | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None = , **kwargs)` Expand source code ``` class User: active: Union[Optional[bool], DefaultArg] addresses: Union[Optional[List[UserAddress]], DefaultArg] display_name: Union[Optional[str], DefaultArg] emails: Union[Optional[List[TypeAndValue]], DefaultArg] external_id: Union[Optional[str], DefaultArg] groups: Union[Optional[List[UserGroup]], DefaultArg] id: Union[Optional[str], DefaultArg] meta: Union[Optional[UserMeta], DefaultArg] name: Union[Optional[UserName], DefaultArg] nick_name: Union[Optional[str], DefaultArg] phone_numbers: Union[Optional[List[TypeAndValue]], DefaultArg] photos: Union[Optional[List[UserPhoto]], DefaultArg] profile_url: Union[Optional[str], DefaultArg] roles: Union[Optional[List[TypeAndValue]], DefaultArg] schemas: Union[Optional[List[str]], DefaultArg] timezone: Union[Optional[str], DefaultArg] title: Union[Optional[str], DefaultArg] user_name: Union[Optional[str], DefaultArg] unknown_fields: Dict[str, Any] def __init__( self, *, active: Union[Optional[bool], DefaultArg] = NotGiven, addresses: Union[Optional[List[Union[UserAddress, Dict[str, Any]]]], DefaultArg] = NotGiven, display_name: Union[Optional[str], DefaultArg] = NotGiven, emails: Union[Optional[List[Union[TypeAndValue, Dict[str, Any]]]], DefaultArg] = NotGiven, external_id: Union[Optional[str], DefaultArg] = NotGiven, groups: Union[Optional[List[Union[UserGroup, Dict[str, Any]]]], DefaultArg] = NotGiven, id: Union[Optional[str], DefaultArg] = NotGiven, meta: Union[Optional[Union[UserMeta, Dict[str, Any]]], DefaultArg] = NotGiven, name: Union[Optional[Union[UserName, Dict[str, Any]]], DefaultArg] = NotGiven, nick_name: Union[Optional[str], DefaultArg] = NotGiven, phone_numbers: Union[Optional[List[Union[TypeAndValue, Dict[str, Any]]]], DefaultArg] = NotGiven, photos: Union[Optional[List[Union[UserPhoto, Dict[str, Any]]]], DefaultArg] = NotGiven, profile_url: Union[Optional[str], DefaultArg] = NotGiven, roles: Union[Optional[List[Union[TypeAndValue, Dict[str, Any]]]], DefaultArg] = NotGiven, schemas: Union[Optional[List[str]], DefaultArg] = NotGiven, timezone: Union[Optional[str], DefaultArg] = NotGiven, title: Union[Optional[str], DefaultArg] = NotGiven, user_name: Union[Optional[str], DefaultArg] = NotGiven, **kwargs, ) -> None: self.active = active self.addresses = ( [a if isinstance(a, UserAddress) else UserAddress(**a) for a in addresses] # type: ignore if _is_iterable(addresses) else addresses ) self.display_name = display_name self.emails = ( [a if isinstance(a, TypeAndValue) else TypeAndValue(**a) for a in emails] # type: ignore if _is_iterable(emails) else emails ) self.external_id = external_id self.groups = ( [a if isinstance(a, UserGroup) else UserGroup(**a) for a in groups] # type: ignore if _is_iterable(groups) else groups ) self.id = id self.meta = UserMeta(**meta) if meta is not None and isinstance(meta, dict) else meta self.name = UserName(**name) if name is not None and isinstance(name, dict) else name self.nick_name = nick_name self.phone_numbers = ( [a if isinstance(a, TypeAndValue) else TypeAndValue(**a) for a in phone_numbers] # type: ignore if _is_iterable(phone_numbers) else phone_numbers ) self.photos = ( [a if isinstance(a, UserPhoto) else UserPhoto(**a) for a in photos] # type: ignore if _is_iterable(photos) else photos ) self.profile_url = profile_url self.roles = ( [a if isinstance(a, TypeAndValue) else TypeAndValue(**a) for a in roles] # type: ignore if _is_iterable(roles) else roles ) self.schemas = schemas self.timezone = timezone self.title = title self.user_name = user_name self.unknown_fields = kwargs def to_dict(self): return _to_dict_without_not_given(self) def __repr__(self): return f"" ``` ### Class variables `var active : bool | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None` The type of the None singleton. `var addresses : List[[UserAddress](v1/user.html#slack_sdk.scim.v1.user.UserAddress "slack_sdk.scim.v1.user.UserAddress")] | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None` The type of the None singleton. `var display_name : str | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None` The type of the None singleton. `var emails : List[[TypeAndValue](v1/types.html#slack_sdk.scim.v1.types.TypeAndValue "slack_sdk.scim.v1.types.TypeAndValue")] | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None` The type of the None singleton. `var external_id : str | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None` The type of the None singleton. `var groups : List[[UserGroup](v1/user.html#slack_sdk.scim.v1.user.UserGroup "slack_sdk.scim.v1.user.UserGroup")] | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None` The type of the None singleton. `var id : str | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None` The type of the None singleton. `var meta : [UserMeta](v1/user.html#slack_sdk.scim.v1.user.UserMeta "slack_sdk.scim.v1.user.UserMeta") | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None` The type of the None singleton. `var name : [UserName](v1/user.html#slack_sdk.scim.v1.user.UserName "slack_sdk.scim.v1.user.UserName") | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None` The type of the None singleton. `var nick_name : str | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None` The type of the None singleton. `var phone_numbers : List[[TypeAndValue](v1/types.html#slack_sdk.scim.v1.types.TypeAndValue "slack_sdk.scim.v1.types.TypeAndValue")] | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None` The type of the None singleton. `var photos : List[[UserPhoto](v1/user.html#slack_sdk.scim.v1.user.UserPhoto "slack_sdk.scim.v1.user.UserPhoto")] | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None` The type of the None singleton. `var profile_url : str | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None` The type of the None singleton. `var roles : List[[TypeAndValue](v1/types.html#slack_sdk.scim.v1.types.TypeAndValue "slack_sdk.scim.v1.types.TypeAndValue")] | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None` The type of the None singleton. `var schemas : List[str] | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None` The type of the None singleton. `var timezone : str | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None` The type of the None singleton. `var title : str | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None` The type of the None singleton. `var unknown_fields : Dict[str, Any]` The type of the None singleton. `var user_name : str | [DefaultArg](v1/default_arg.html#slack_sdk.scim.v1.default_arg.DefaultArg "slack_sdk.scim.v1.default_arg.DefaultArg") | None` The type of the None singleton. ### Methods `def to_dict(self)` Expand source code ``` def to_dict(self): return _to_dict_without_not_given(self) ``` --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/scim/v1 # Module slack_sdk.scim.v1 SCIM API is a set of APIs for provisioning and managing user accounts and groups. SCIM is used by Single Sign-On (SSO) services and identity providers to manage people across a variety of tools, including Slack. Refer to [https://docs.slack.dev/tools/python-slack-sdk/scim](https://docs.slack.dev/tools/python-slack-sdk/scim) for details. ## Sub-modules `[slack_sdk.scim.v1.async_client](async_client.html "slack_sdk.scim.v1.async_client")` `[slack_sdk.scim.v1.client](client.html "slack_sdk.scim.v1.client")` SCIM API is a set of APIs for provisioning and managing user accounts and groups. SCIM is used by Single Sign-On (SSO) services and identity providers … `[slack_sdk.scim.v1.default_arg](default_arg.html "slack_sdk.scim.v1.default_arg")` `[slack_sdk.scim.v1.group](group.html "slack_sdk.scim.v1.group")` `[slack_sdk.scim.v1.internal_utils](internal_utils.html "slack_sdk.scim.v1.internal_utils")` `[slack_sdk.scim.v1.response](response.html "slack_sdk.scim.v1.response")` `[slack_sdk.scim.v1.types](types.html "slack_sdk.scim.v1.types")` `[slack_sdk.scim.v1.user](user.html "slack_sdk.scim.v1.user")` --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/signature # Module slack_sdk.signature Slack request signature verifier ## Classes `class Clock` Expand source code ``` class Clock: def now(self) -> float: return time() ``` ### Methods `def now(self) ‑> float` Expand source code ``` def now(self) -> float: return time() ``` `class SignatureVerifier (signing_secret: str, clock: [Clock](#slack_sdk.signature.Clock "slack_sdk.signature.Clock") = )` Expand source code ``` class SignatureVerifier: def __init__(self, signing_secret: str, clock: Clock = Clock()): """Slack request signature verifier Slack signs its requests using a secret that's unique to your app. With the help of signing secrets, your app can more confidently verify whether requests from us are authentic. https://docs.slack.dev/authentication/verifying-requests-from-slack/ """ self.signing_secret = signing_secret self.clock = clock @property def signing_secret(self) -> str: return self._signing_secret @signing_secret.setter def signing_secret(self, value: str) -> None: if not isinstance(value, str): raise ValueError("signing_secret must be a string") if not value.strip(): raise ValueError("signing_secret must not be empty.") self._signing_secret = value def is_valid_request( self, body: Union[str, bytes], headers: Mapping[str, str], ) -> bool: """Verifies if the given signature is valid""" if headers is None: return False normalized_headers = {k.lower(): v for k, v in headers.items()} return self.is_valid( body=body, timestamp=normalized_headers.get("x-slack-request-timestamp", None), signature=normalized_headers.get("x-slack-signature", None), ) def is_valid( self, body: Union[str, bytes], timestamp: Optional[str], signature: Optional[str], ) -> bool: """Verifies if the given signature is valid""" if timestamp is None or signature is None: return False if abs(self.clock.now() - int(timestamp)) > 60 * 5: return False calculated_signature = self.generate_signature(timestamp=timestamp, body=body) if calculated_signature is None: return False return hmac.compare_digest(calculated_signature, signature) def generate_signature(self, *, timestamp: str, body: Union[str, bytes]) -> Optional[str]: """Generates a signature""" if timestamp is None: return None if body is None: body = "" if isinstance(body, bytes): body = body.decode("utf-8") format_req = str.encode(f"v0:{timestamp}:{body}") encoded_secret = str.encode(self.signing_secret) request_hash = hmac.new(encoded_secret, format_req, hashlib.sha256).hexdigest() calculated_signature = f"v0={request_hash}" return calculated_signature ``` Slack request signature verifier Slack signs its requests using a secret that's unique to your app. With the help of signing secrets, your app can more confidently verify whether requests from us are authentic. [https://docs.slack.dev/authentication/verifying-requests-from-slack/](https://docs.slack.dev/authentication/verifying-requests-from-slack/) ### Instance variables `prop signing_secret : str` Expand source code ``` @property def signing_secret(self) -> str: return self._signing_secret ``` ### Methods `def generate_signature(self, *, timestamp: str, body: str | bytes) ‑> str | None` Expand source code ``` def generate_signature(self, *, timestamp: str, body: Union[str, bytes]) -> Optional[str]: """Generates a signature""" if timestamp is None: return None if body is None: body = "" if isinstance(body, bytes): body = body.decode("utf-8") format_req = str.encode(f"v0:{timestamp}:{body}") encoded_secret = str.encode(self.signing_secret) request_hash = hmac.new(encoded_secret, format_req, hashlib.sha256).hexdigest() calculated_signature = f"v0={request_hash}" return calculated_signature ``` Generates a signature `def is_valid(self, body: str | bytes, timestamp: str | None, signature: str | None) ‑> bool` Expand source code ``` def is_valid( self, body: Union[str, bytes], timestamp: Optional[str], signature: Optional[str], ) -> bool: """Verifies if the given signature is valid""" if timestamp is None or signature is None: return False if abs(self.clock.now() - int(timestamp)) > 60 * 5: return False calculated_signature = self.generate_signature(timestamp=timestamp, body=body) if calculated_signature is None: return False return hmac.compare_digest(calculated_signature, signature) ``` Verifies if the given signature is valid `def is_valid_request(self, body: str | bytes, headers: Dict[str, str]) ‑> bool` Expand source code ``` def is_valid_request( self, body: Union[str, bytes], headers: Mapping[str, str], ) -> bool: """Verifies if the given signature is valid""" if headers is None: return False normalized_headers = {k.lower(): v for k, v in headers.items()} return self.is_valid( body=body, timestamp=normalized_headers.get("x-slack-request-timestamp", None), signature=normalized_headers.get("x-slack-signature", None), ) ``` Verifies if the given signature is valid --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/socket_mode # Module slack_sdk.socket_mode Socket Mode is a method of connecting your app to Slack’s APIs using WebSockets instead of HTTP. You can use slack\_sdk.socket\_mode.SocketModeClient for managing Socket Mode connections and performing interactions with Slack. [https://docs.slack.dev/apis/events-api/using-socket-mode/](https://docs.slack.dev/apis/events-api/using-socket-mode/) ## Sub-modules `[slack_sdk.socket_mode.aiohttp](aiohttp/index.html "slack_sdk.socket_mode.aiohttp")` aiohttp based Socket Mode client … `[slack_sdk.socket_mode.async_client](async_client.html "slack_sdk.socket_mode.async_client")` `[slack_sdk.socket_mode.async_listeners](async_listeners.html "slack_sdk.socket_mode.async_listeners")` `[slack_sdk.socket_mode.builtin](builtin/index.html "slack_sdk.socket_mode.builtin")` `[slack_sdk.socket_mode.client](client.html "slack_sdk.socket_mode.client")` `[slack_sdk.socket_mode.interval_runner](interval_runner.html "slack_sdk.socket_mode.interval_runner")` `[slack_sdk.socket_mode.listeners](listeners.html "slack_sdk.socket_mode.listeners")` `[slack_sdk.socket_mode.logger](logger/index.html "slack_sdk.socket_mode.logger")` `[slack_sdk.socket_mode.request](request.html "slack_sdk.socket_mode.request")` `[slack_sdk.socket_mode.response](response.html "slack_sdk.socket_mode.response")` `[slack_sdk.socket_mode.websocket_client](websocket_client/index.html "slack_sdk.socket_mode.websocket_client")` websocket-client based Socket Mode client … `[slack_sdk.socket_mode.websockets](websockets/index.html "slack_sdk.socket_mode.websockets")` websockets based Socket Mode client … ## Classes `class SocketModeClient (app_token: str, logger: logging.Logger | None = None, web_client: [WebClient](../web/client.html#slack_sdk.web.client.WebClient "slack_sdk.web.client.WebClient") | None = None, auto_reconnect_enabled: bool = True, trace_enabled: bool = False, all_message_trace_enabled: bool = False, ping_pong_trace_enabled: bool = False, ping_interval: float = 5, receive_buffer_size: int = 1024, concurrency: int = 10, proxy: str | None = None, proxy_headers: Dict[str, str] | None = None, on_message_listeners: List[Callable[[str], None]] | None = None, on_error_listeners: List[Callable[[Exception], None]] | None = None, on_close_listeners: List[Callable[[int, str | None], None]] | None = None)` Expand source code ``` class SocketModeClient(BaseSocketModeClient): logger: Logger web_client: WebClient app_token: str wss_uri: Optional[str] # type: ignore[assignment] message_queue: Queue message_listeners: List[ Union[ WebSocketMessageListener, Callable[["BaseSocketModeClient", dict, Optional[str]], None], ] ] socket_mode_request_listeners: List[ Union[ SocketModeRequestListener, Callable[["BaseSocketModeClient", SocketModeRequest], None], ] ] current_session: Optional[Connection] current_session_state: ConnectionState current_session_runner: IntervalRunner current_app_monitor: IntervalRunner current_app_monitor_started: bool message_processor: IntervalRunner message_workers: ThreadPoolExecutor auto_reconnect_enabled: bool default_auto_reconnect_enabled: bool trace_enabled: bool receive_buffer_size: int # bytes size connect_operation_lock: Lock on_message_listeners: List[Callable[[str], None]] on_error_listeners: List[Callable[[Exception], None]] on_close_listeners: List[Callable[[int, Optional[str]], None]] def __init__( self, app_token: str, logger: Optional[Logger] = None, web_client: Optional[WebClient] = None, auto_reconnect_enabled: bool = True, trace_enabled: bool = False, all_message_trace_enabled: bool = False, ping_pong_trace_enabled: bool = False, ping_interval: float = 5, receive_buffer_size: int = 1024, concurrency: int = 10, proxy: Optional[str] = None, proxy_headers: Optional[Dict[str, str]] = None, on_message_listeners: Optional[List[Callable[[str], None]]] = None, on_error_listeners: Optional[List[Callable[[Exception], None]]] = None, on_close_listeners: Optional[List[Callable[[int, Optional[str]], None]]] = None, ): """Socket Mode client Args: app_token: App-level token logger: Custom logger web_client: Web API client auto_reconnect_enabled: True if automatic reconnection is enabled (default: True) trace_enabled: True if more detailed debug-logging is enabled (default: False) all_message_trace_enabled: True if all message dump in debug logs is enabled (default: False) ping_pong_trace_enabled: True if trace logging for all ping-pong communications is enabled (default: False) ping_interval: interval for ping-pong with Slack servers (seconds) receive_buffer_size: the chunk size of a single socket recv operation (default: 1024) concurrency: the size of thread pool (default: 10) proxy: the HTTP proxy URL proxy_headers: additional HTTP header for proxy connection on_message_listeners: listener functions for on_message on_error_listeners: listener functions for on_error on_close_listeners: listener functions for on_close """ self.app_token = app_token self.logger = logger or logging.getLogger(__name__) self.web_client = web_client or WebClient() self.default_auto_reconnect_enabled = auto_reconnect_enabled self.auto_reconnect_enabled = self.default_auto_reconnect_enabled self.trace_enabled = trace_enabled self.all_message_trace_enabled = all_message_trace_enabled self.ping_pong_trace_enabled = ping_pong_trace_enabled self.ping_interval = ping_interval self.receive_buffer_size = receive_buffer_size if self.receive_buffer_size < 16: raise SlackClientConfigurationError("Too small receive_buffer_size detected.") self.wss_uri = None self.message_queue = Queue() self.message_listeners = [] self.socket_mode_request_listeners = [] self.current_session = None self.current_session_state = ConnectionState() self.current_session_runner = IntervalRunner(self._run_current_session, 0.1).start() self.current_app_monitor_started = False self.current_app_monitor = IntervalRunner(self._monitor_current_session, self.ping_interval) self.closed = False self.connect_operation_lock = Lock() self.message_processor = IntervalRunner(self.process_messages, 0.001).start() self.message_workers = ThreadPoolExecutor(max_workers=concurrency) self.proxy = proxy if self.proxy is None or len(self.proxy.strip()) == 0: env_variable = load_http_proxy_from_env(self.logger) if env_variable is not None: self.proxy = env_variable self.proxy_headers = proxy_headers self.on_message_listeners = on_message_listeners or [] self.on_error_listeners = on_error_listeners or [] self.on_close_listeners = on_close_listeners or [] def session_id(self) -> Optional[str]: if self.current_session is not None: return self.current_session.session_id return None def is_connected(self) -> bool: return self.current_session is not None and self.current_session.is_active() def connect(self) -> None: old_session: Optional[Connection] = self.current_session old_current_session_state: ConnectionState = self.current_session_state if self.wss_uri is None: self.wss_uri = self.issue_new_wss_url() current_session = Connection( url=self.wss_uri, logger=self.logger, ping_interval=self.ping_interval, trace_enabled=self.trace_enabled, all_message_trace_enabled=self.all_message_trace_enabled, ping_pong_trace_enabled=self.ping_pong_trace_enabled, receive_buffer_size=self.receive_buffer_size, proxy=self.proxy, proxy_headers=self.proxy_headers, on_message_listener=self._on_message, on_error_listener=self._on_error, on_close_listener=self._on_close, ssl_context=self.web_client.ssl, ) current_session.connect() if old_current_session_state is not None: old_current_session_state.terminated = True if old_session is not None: old_session.close() self.current_session = current_session self.current_session_state = ConnectionState() self.auto_reconnect_enabled = self.default_auto_reconnect_enabled if not self.current_app_monitor_started: self.current_app_monitor_started = True self.current_app_monitor.start() self.logger.info(f"A new session has been established (session id: {self.session_id()})") def disconnect(self) -> None: if self.current_session is not None: self.current_session.close() def send_message(self, message: str) -> None: if self.logger.level <= logging.DEBUG: self.logger.debug(f"Sending a message (session id: {self.session_id()}, message: {message})") try: self.current_session.send(message) # type: ignore[union-attr] except SlackClientNotConnectedError as e: # We rarely get this exception while replacing the underlying WebSocket connections. # We can do one more try here as the self.current_session should be ready now. if self.logger.level <= logging.DEBUG: self.logger.debug( f"Failed to send a message (session id: {self.session_id()}, error: {e}, message: {message})" " as the underlying connection was replaced. Retrying the same request only one time..." ) # Although acquiring self.connect_operation_lock also for the first method call is the safest way, # we avoid synchronizing a lot for better performance. That's why we are doing a retry here. with self.connect_operation_lock: if self.is_connected(): self.current_session.send(message) # type: ignore[union-attr] else: self.logger.warning( f"The current session (session id: {self.session_id()}) is no longer active. " "Failed to send a message" ) raise e def close(self): self.closed = True self.auto_reconnect_enabled = False self.disconnect() if self.current_app_monitor.is_alive(): self.current_app_monitor.shutdown() if self.message_processor.is_alive(): self.message_processor.shutdown() self.message_workers.shutdown() def _on_message(self, message: str): if self.logger.level <= logging.DEBUG: self.logger.debug(f"on_message invoked: (message: {debug_redacted_message_string(message)})") self.enqueue_message(message) for listener in self.on_message_listeners: listener(message) def _on_error(self, error: Exception): error_message = ( f"on_error invoked (session id: {self.session_id()}, " f"error: {type(error).__name__}, message: {error})" ) if self.trace_enabled: self.logger.exception(error_message) else: self.logger.error(error_message) for listener in self.on_error_listeners: listener(error) def _on_close(self, code: int, reason: Optional[str] = None): if self.logger.level <= logging.DEBUG: self.logger.debug(f"on_close invoked (session id: {self.session_id()})") if self.auto_reconnect_enabled: self.logger.info("Received CLOSE event. Reconnecting... " f"(session id: {self.session_id()})") self.connect_to_new_endpoint() for listener in self.on_close_listeners: listener(code, reason) def _run_current_session(self): if self.current_session is not None and self.current_session.is_active(): session_id = self.session_id() try: self.logger.info("Starting to receive messages from a new connection" f" (session id: {session_id})") self.current_session_state.terminated = False self.current_session.run_until_completion(self.current_session_state) self.logger.info("Stopped receiving messages from a connection" f" (session id: {session_id})") except Exception as e: error_message = "Failed to start or stop the current session" f" (session id: {session_id}, error: {e})" if self.trace_enabled: self.logger.exception(error_message) else: self.logger.error(error_message) def _monitor_current_session(self): if self.current_app_monitor_started: try: self.current_session.check_state() if self.auto_reconnect_enabled and (self.current_session is None or not self.current_session.is_active()): self.logger.info( "The session seems to be already closed. Reconnecting... " f"(session id: {self.session_id()})" ) self.connect_to_new_endpoint() except Exception as e: self.logger.error( "Failed to check the current session or reconnect to the server " f"(session id: {self.session_id()}, error: {type(e).__name__}, message: {e})" ) ``` Socket Mode client ## Args **`app_token`** App-level token **`logger`** Custom logger **`web_client`** Web API client **`auto_reconnect_enabled`** True if automatic reconnection is enabled (default: True) **`trace_enabled`** True if more detailed debug-logging is enabled (default: False) **`all_message_trace_enabled`** True if all message dump in debug logs is enabled (default: False) **`ping_pong_trace_enabled`** True if trace logging for all ping-pong communications is enabled (default: False) **`ping_interval`** interval for ping-pong with Slack servers (seconds) **`receive_buffer_size`** the chunk size of a single socket recv operation (default: 1024) **`concurrency`** the size of thread pool (default: 10) **`proxy`** the HTTP proxy URL **`proxy_headers`** additional HTTP header for proxy connection **`on_message_listeners`** listener functions for on\_message **`on_error_listeners`** listener functions for on\_error **`on_close_listeners`** listener functions for on\_close ### Ancestors * [BaseSocketModeClient](client.html#slack_sdk.socket_mode.client.BaseSocketModeClient "slack_sdk.socket_mode.client.BaseSocketModeClient") ### Class variables `var auto_reconnect_enabled : bool` The type of the None singleton. `var current_app_monitor : [IntervalRunner](interval_runner.html#slack_sdk.socket_mode.interval_runner.IntervalRunner "slack_sdk.socket_mode.interval_runner.IntervalRunner")` The type of the None singleton. `var current_app_monitor_started : bool` The type of the None singleton. `var current_session : [Connection](builtin/connection.html#slack_sdk.socket_mode.builtin.connection.Connection "slack_sdk.socket_mode.builtin.connection.Connection") | None` The type of the None singleton. `var current_session_runner : [IntervalRunner](interval_runner.html#slack_sdk.socket_mode.interval_runner.IntervalRunner "slack_sdk.socket_mode.interval_runner.IntervalRunner")` The type of the None singleton. `var current_session_state : [ConnectionState](builtin/connection.html#slack_sdk.socket_mode.builtin.connection.ConnectionState "slack_sdk.socket_mode.builtin.connection.ConnectionState")` The type of the None singleton. `var default_auto_reconnect_enabled : bool` The type of the None singleton. `var on_close_listeners : List[Callable[[int, str | None], None]]` The type of the None singleton. `var on_error_listeners : List[Callable[[Exception], None]]` The type of the None singleton. `var on_message_listeners : List[Callable[[str], None]]` The type of the None singleton. `var receive_buffer_size : int` The type of the None singleton. `var trace_enabled : bool` The type of the None singleton. ### Methods `def close(self)` Expand source code ``` def close(self): self.closed = True self.auto_reconnect_enabled = False self.disconnect() if self.current_app_monitor.is_alive(): self.current_app_monitor.shutdown() if self.message_processor.is_alive(): self.message_processor.shutdown() self.message_workers.shutdown() ``` `def connect(self) ‑> None` Expand source code ``` def connect(self) -> None: old_session: Optional[Connection] = self.current_session old_current_session_state: ConnectionState = self.current_session_state if self.wss_uri is None: self.wss_uri = self.issue_new_wss_url() current_session = Connection( url=self.wss_uri, logger=self.logger, ping_interval=self.ping_interval, trace_enabled=self.trace_enabled, all_message_trace_enabled=self.all_message_trace_enabled, ping_pong_trace_enabled=self.ping_pong_trace_enabled, receive_buffer_size=self.receive_buffer_size, proxy=self.proxy, proxy_headers=self.proxy_headers, on_message_listener=self._on_message, on_error_listener=self._on_error, on_close_listener=self._on_close, ssl_context=self.web_client.ssl, ) current_session.connect() if old_current_session_state is not None: old_current_session_state.terminated = True if old_session is not None: old_session.close() self.current_session = current_session self.current_session_state = ConnectionState() self.auto_reconnect_enabled = self.default_auto_reconnect_enabled if not self.current_app_monitor_started: self.current_app_monitor_started = True self.current_app_monitor.start() self.logger.info(f"A new session has been established (session id: {self.session_id()})") ``` `def disconnect(self) ‑> None` Expand source code ``` def disconnect(self) -> None: if self.current_session is not None: self.current_session.close() ``` `def is_connected(self) ‑> bool` Expand source code ``` def is_connected(self) -> bool: return self.current_session is not None and self.current_session.is_active() ``` `def send_message(self, message: str) ‑> None` Expand source code ``` def send_message(self, message: str) -> None: if self.logger.level <= logging.DEBUG: self.logger.debug(f"Sending a message (session id: {self.session_id()}, message: {message})") try: self.current_session.send(message) # type: ignore[union-attr] except SlackClientNotConnectedError as e: # We rarely get this exception while replacing the underlying WebSocket connections. # We can do one more try here as the self.current_session should be ready now. if self.logger.level <= logging.DEBUG: self.logger.debug( f"Failed to send a message (session id: {self.session_id()}, error: {e}, message: {message})" " as the underlying connection was replaced. Retrying the same request only one time..." ) # Although acquiring self.connect_operation_lock also for the first method call is the safest way, # we avoid synchronizing a lot for better performance. That's why we are doing a retry here. with self.connect_operation_lock: if self.is_connected(): self.current_session.send(message) # type: ignore[union-attr] else: self.logger.warning( f"The current session (session id: {self.session_id()}) is no longer active. " "Failed to send a message" ) raise e ``` `def session_id(self) ‑> str | None` Expand source code ``` def session_id(self) -> Optional[str]: if self.current_session is not None: return self.current_session.session_id return None ``` ### Inherited members * `**[BaseSocketModeClient](client.html#slack_sdk.socket_mode.client.BaseSocketModeClient "slack_sdk.socket_mode.client.BaseSocketModeClient")**`: * `[app_token](client.html#slack_sdk.socket_mode.client.BaseSocketModeClient.app_token "slack_sdk.socket_mode.client.BaseSocketModeClient.app_token")` * `[closed](client.html#slack_sdk.socket_mode.client.BaseSocketModeClient.closed "slack_sdk.socket_mode.client.BaseSocketModeClient.closed")` * `[connect_operation_lock](client.html#slack_sdk.socket_mode.client.BaseSocketModeClient.connect_operation_lock "slack_sdk.socket_mode.client.BaseSocketModeClient.connect_operation_lock")` * `[logger](client.html#slack_sdk.socket_mode.client.BaseSocketModeClient.logger "slack_sdk.socket_mode.client.BaseSocketModeClient.logger")` * `[message_listeners](client.html#slack_sdk.socket_mode.client.BaseSocketModeClient.message_listeners "slack_sdk.socket_mode.client.BaseSocketModeClient.message_listeners")` * `[message_processor](client.html#slack_sdk.socket_mode.client.BaseSocketModeClient.message_processor "slack_sdk.socket_mode.client.BaseSocketModeClient.message_processor")` * `[message_queue](client.html#slack_sdk.socket_mode.client.BaseSocketModeClient.message_queue "slack_sdk.socket_mode.client.BaseSocketModeClient.message_queue")` * `[message_workers](client.html#slack_sdk.socket_mode.client.BaseSocketModeClient.message_workers "slack_sdk.socket_mode.client.BaseSocketModeClient.message_workers")` * `[socket_mode_request_listeners](client.html#slack_sdk.socket_mode.client.BaseSocketModeClient.socket_mode_request_listeners "slack_sdk.socket_mode.client.BaseSocketModeClient.socket_mode_request_listeners")` * `[web_client](client.html#slack_sdk.socket_mode.client.BaseSocketModeClient.web_client "slack_sdk.socket_mode.client.BaseSocketModeClient.web_client")` * `[wss_uri](client.html#slack_sdk.socket_mode.client.BaseSocketModeClient.wss_uri "slack_sdk.socket_mode.client.BaseSocketModeClient.wss_uri")` --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/socket_mode/aiohttp # Module slack_sdk.socket_mode.aiohttp aiohttp based Socket Mode client * [https://docs.slack.dev/apis/events-api/using-socket-mode/](https://docs.slack.dev/apis/events-api/using-socket-mode/) * [https://docs.slack.dev/tools/python-slack-sdk/socket-mode/](https://docs.slack.dev/tools/python-slack-sdk/socket-mode/) * [https://pypi.org/project/aiohttp/](https://pypi.org/project/aiohttp/) ## Classes `class SocketModeClient (app_token: str, logger: logging.Logger | None = None, web_client: [AsyncWebClient](../../web/async_client.html#slack_sdk.web.async_client.AsyncWebClient "slack_sdk.web.async_client.AsyncWebClient") | None = None, proxy: str | None = None, auto_reconnect_enabled: bool = True, ping_interval: float = 5, trace_enabled: bool = False, on_message_listeners: List[Callable[[aiohttp._websocket.models.WSMessage], Awaitable[None]]] | None = None, on_error_listeners: List[Callable[[aiohttp._websocket.models.WSMessage], Awaitable[None]]] | None = None, on_close_listeners: List[Callable[[aiohttp._websocket.models.WSMessage], Awaitable[None]]] | None = None, loop: asyncio.events.AbstractEventLoop | None = None)` Expand source code ``` class SocketModeClient(AsyncBaseSocketModeClient): logger: Logger web_client: AsyncWebClient app_token: str wss_uri: Optional[str] # type: ignore[assignment] auto_reconnect_enabled: bool message_queue: Queue message_listeners: List[ Union[ AsyncWebSocketMessageListener, Callable[["AsyncBaseSocketModeClient", dict, Optional[str]], Awaitable[None]], ] ] socket_mode_request_listeners: List[ Union[ AsyncSocketModeRequestListener, Callable[["AsyncBaseSocketModeClient", SocketModeRequest], Awaitable[None]], ] ] message_receiver: Optional[Future] message_processor: Future proxy: Optional[str] ping_interval: float trace_enabled: bool last_ping_pong_time: Optional[float] current_session: Optional[ClientWebSocketResponse] current_session_monitor: Optional[Future] default_auto_reconnect_enabled: bool closed: bool stale: bool connect_operation_lock: Lock on_message_listeners: List[Callable[[WSMessage], Awaitable[None]]] on_error_listeners: List[Callable[[WSMessage], Awaitable[None]]] on_close_listeners: List[Callable[[WSMessage], Awaitable[None]]] def __init__( self, app_token: str, logger: Optional[Logger] = None, web_client: Optional[AsyncWebClient] = None, proxy: Optional[str] = None, auto_reconnect_enabled: bool = True, ping_interval: float = 5, trace_enabled: bool = False, on_message_listeners: Optional[List[Callable[[WSMessage], Awaitable[None]]]] = None, on_error_listeners: Optional[List[Callable[[WSMessage], Awaitable[None]]]] = None, on_close_listeners: Optional[List[Callable[[WSMessage], Awaitable[None]]]] = None, loop: Optional[AbstractEventLoop] = None, ): """Socket Mode client Args: app_token: App-level token logger: Custom logger web_client: Web API client auto_reconnect_enabled: True if automatic reconnection is enabled (default: True) ping_interval: interval for ping-pong with Slack servers (seconds) trace_enabled: True if more verbose logs to see what's happening under the hood proxy: the HTTP proxy URL on_message_listeners: listener functions for on_message on_error_listeners: listener functions for on_error on_close_listeners: listener functions for on_close loop: an existing asyncio event loop """ self.app_token = app_token self.logger = logger or logging.getLogger(__name__) self.web_client = web_client or AsyncWebClient() self.closed = False self.stale = False self.connect_operation_lock = Lock() self.proxy = proxy if self.proxy is None or len(self.proxy.strip()) == 0: env_variable = load_http_proxy_from_env(self.logger) if env_variable is not None: self.proxy = env_variable self.default_auto_reconnect_enabled = auto_reconnect_enabled self.auto_reconnect_enabled = self.default_auto_reconnect_enabled self.ping_interval = ping_interval self.trace_enabled = trace_enabled self.last_ping_pong_time = None self.wss_uri = None self.message_queue = Queue() self.message_listeners = [] self.socket_mode_request_listeners = [] self.current_session = None self.current_session_monitor = None # https://docs.aiohttp.org/en/stable/client_reference.html # Unless you are connecting to a large, unknown number of different servers # over the lifetime of your application, # it is suggested you use a single session for the lifetime of your application # to benefit from connection pooling. self.aiohttp_client_session = aiohttp.ClientSession(loop=loop) self.on_message_listeners = on_message_listeners or [] self.on_error_listeners = on_error_listeners or [] self.on_close_listeners = on_close_listeners or [] self.message_receiver = None self.message_processor = asyncio.ensure_future(self.process_messages()) async def monitor_current_session(self) -> None: # In the asyncio runtime, accessing a shared object (self.current_session here) from # multiple tasks can cause race conditions and errors. # To avoid such, we access only the session that is active when this loop starts. session: ClientWebSocketResponse = self.current_session # type: ignore[assignment] session_id: str = self.build_session_id(session) if self.logger.level <= logging.DEBUG: self.logger.debug(f"A new monitor_current_session() execution loop for {session_id} started") try: logging_interval = 100 counter_for_logging = 0 while not self.closed: if session != self.current_session: if self.logger.level <= logging.DEBUG: self.logger.debug(f"The monitor_current_session task for {session_id} is now cancelled") break try: if self.trace_enabled and self.logger.level <= logging.DEBUG: # The logging here is for detailed investigation on potential issues in this client. # If you don't see this log for a while, it means that # this receive_messages execution is no longer working for some reason. counter_for_logging += 1 if counter_for_logging >= logging_interval: counter_for_logging = 0 log_message = ( "#monitor_current_session method has been verifying if this session is active " f"(session: {session_id}, logging interval: {logging_interval})" ) self.logger.debug(log_message) await asyncio.sleep(self.ping_interval) if session is not None and session.closed is False: t = time.time() if self.last_ping_pong_time is None: self.last_ping_pong_time = float(t) try: await session.ping(f"sdk-ping-pong:{t}".encode("utf-8")) except Exception as e: # The ping() method can fail for some reason. # To establish a new connection even in this scenario, # we ignore the exception here. self.logger.warning(f"Failed to send a ping message ({session_id}): {e}") if self.auto_reconnect_enabled: should_reconnect = False if session is None or session.closed: self.logger.info(f"The session ({session_id}) seems to be already closed. Reconnecting...") should_reconnect = True if await self.is_ping_pong_failing(): disconnected_seconds = int(time.time() - self.last_ping_pong_time) # type: ignore[operator] self.logger.info( f"The session ({session_id}) seems to be stale. Reconnecting..." f" reason: disconnected for {disconnected_seconds}+ seconds)" ) self.stale = True self.last_ping_pong_time = None should_reconnect = True if should_reconnect is True or not await self.is_connected(): await self.connect_to_new_endpoint() except Exception as e: self.logger.error( f"Failed to check the current session ({session_id}) or reconnect to the server " f"(error: {type(e).__name__}, message: {e})" ) except asyncio.CancelledError: if self.logger.level <= logging.DEBUG: self.logger.debug(f"The monitor_current_session task for {session_id} is now cancelled") raise async def receive_messages(self) -> None: # In the asyncio runtime, accessing a shared object (self.current_session here) from # multiple tasks can cause race conditions and errors. # To avoid such, we access only the session that is active when this loop starts. session = self.current_session session_id = self.build_session_id(session) # type: ignore[arg-type] if self.logger.level <= logging.DEBUG: self.logger.debug(f"A new receive_messages() execution loop with {session_id} started") try: consecutive_error_count = 0 logging_interval = 100 counter_for_logging = 0 while not self.closed: if session != self.current_session: if self.logger.level <= logging.DEBUG: self.logger.debug(f"The running receive_messages task for {session_id} is now cancelled") break try: message: WSMessage = await session.receive() # type: ignore[union-attr] # just in case, checking if the value is not None if message is not None: if self.logger.level <= logging.DEBUG: # The following logging prints every single received message # except empty message data ones. m_type = WSMsgType(message.type) message_type = m_type.name if m_type is not None else message.type message_data = message.data if isinstance(message_data, bytes): message_data = message_data.decode("utf-8") if message_data is not None and isinstance(message_data, (str, bytes)) and len(message_data) > 0: # To skip the empty message that Slack server-side often sends self.logger.debug( f"Received message " f"(type: {message_type}, " f"data: {message_data}, " f"extra: {message.extra}, " f"session: {session_id})" ) if self.trace_enabled: # The logging here is for detailed trouble shooting of potential issues in this client. # If you don't see this log for a while, it can mean that # this receive_messages execution is no longer working for some reason. counter_for_logging += 1 if counter_for_logging >= logging_interval: counter_for_logging = 0 log_message = ( "#receive_messages method has been working without any issues " f"(session: {session_id}, logging interval: {logging_interval})" ) self.logger.debug(log_message) if message.type == WSMsgType.TEXT: message_data = message.data await self.enqueue_message(message_data) for listener in self.on_message_listeners: await listener(message) elif message.type == WSMsgType.CLOSE: if self.auto_reconnect_enabled: self.logger.info(f"Received CLOSE event from {session_id}. Reconnecting...") await self.connect_to_new_endpoint() for listener in self.on_close_listeners: await listener(message) elif message.type == WSMsgType.ERROR: for listener in self.on_error_listeners: await listener(message) elif message.type == WSMsgType.CLOSED: await asyncio.sleep(self.ping_interval) continue elif message.type == WSMsgType.PING: await session.pong(message.data) # type: ignore[union-attr] continue elif message.type == WSMsgType.PONG: if message.data is not None: str_message_data = message.data.decode("utf-8") elements = str_message_data.split(":") if len(elements) == 2 and elements[0] == "sdk-ping-pong": try: self.last_ping_pong_time = float(elements[1]) except Exception as e: self.logger.warning( f"Failed to parse the last_ping_pong_time value from {str_message_data}" f" - error : {e}, session: {session_id}" ) continue consecutive_error_count = 0 except Exception as e: consecutive_error_count += 1 self.logger.error(f"Failed to receive or enqueue a message: {type(e).__name__}, {e} ({session_id})") if isinstance(e, ClientConnectionError): await asyncio.sleep(self.ping_interval) else: await asyncio.sleep(consecutive_error_count) except asyncio.CancelledError: if self.logger.level <= logging.DEBUG: self.logger.debug(f"The running receive_messages task for {session_id} is now cancelled") raise async def is_ping_pong_failing(self) -> bool: if self.last_ping_pong_time is None: return False disconnected_seconds = int(time.time() - self.last_ping_pong_time) return disconnected_seconds >= (self.ping_interval * 4) async def is_connected(self) -> bool: connected: bool = ( not self.closed and not self.stale and self.current_session is not None and not self.current_session.closed and not await self.is_ping_pong_failing() ) if self.logger.level <= logging.DEBUG and connected is False: # Prints more detailed information about the inactive connection is_ping_pong_failing = await self.is_ping_pong_failing() session_id = await self.session_id() self.logger.debug( "Inactive connection detected (" f"session_id: {session_id}, " f"closed: {self.closed}, " f"stale: {self.stale}, " f"current_session.closed: {self.current_session and self.current_session.closed}, " f"is_ping_pong_failing: {is_ping_pong_failing}" ")" ) return connected async def session_id(self) -> str: return self.build_session_id(self.current_session) # type: ignore[arg-type] async def connect(self): # This loop is used to ensure when a new session is created, # a new monitor and a new message receiver are also created. # If a new session is created but we failed to create the new # monitor or the new message, we should try it. while True: try: old_session: Optional[ClientWebSocketResponse] = ( None if self.current_session is None else self.current_session ) # If the old session is broken (e.g. reset by peer), it might fail to close it. # We don't want to retry when this kind of cases happen. try: # We should close old session before create a new one. Because when disconnect # reason is `too_many_websockets`, we need to close the old one first to # to decrease the number of connections. self.auto_reconnect_enabled = False if old_session is not None: await old_session.close() old_session_id = self.build_session_id(old_session) self.logger.info(f"The old session ({old_session_id}) has been abandoned") except Exception as e: self.logger.exception(f"Failed to close the old session : {e}") if self.wss_uri is None: # If the underlying WSS URL does not exist, # acquiring a new active WSS URL from the server-side first self.wss_uri = await self.issue_new_wss_url() self.current_session = await self.aiohttp_client_session.ws_connect( self.wss_uri, autoping=False, heartbeat=self.ping_interval, proxy=self.proxy, ssl=self.web_client.ssl if self.web_client.ssl is not None else True, ) session_id: str = await self.session_id() self.auto_reconnect_enabled = self.default_auto_reconnect_enabled self.stale = False self.logger.info(f"A new session ({session_id}) has been established") # The first ping from the new connection if self.logger.level <= logging.DEBUG: self.logger.debug(f"Sending a ping message with the newly established connection ({session_id})...") t = time.time() await self.current_session.ping(f"sdk-ping-pong:{t}".encode("utf-8")) if self.current_session_monitor is not None: self.current_session_monitor.cancel() self.current_session_monitor = asyncio.ensure_future(self.monitor_current_session()) if self.logger.level <= logging.DEBUG: self.logger.debug(f"A new monitor_current_session() executor has been recreated for {session_id}") if self.message_receiver is not None: self.message_receiver.cancel() self.message_receiver = asyncio.ensure_future(self.receive_messages()) if self.logger.level <= logging.DEBUG: self.logger.debug(f"A new receive_messages() executor has been recreated for {session_id}") break except Exception as e: self.logger.exception(f"Failed to connect (error: {e}); Retrying...") await asyncio.sleep(self.ping_interval) async def disconnect(self): if self.current_session is not None: await self.current_session.close() session_id = await self.session_id() self.logger.info(f"The current session ({session_id}) has been abandoned by disconnect() method call") async def send_message(self, message: str): session_id = await self.session_id() if self.logger.level <= logging.DEBUG: self.logger.debug(f"Sending a message: {message} from session: {session_id}") try: await self.current_session.send_str(message) # type: ignore[union-attr] except ConnectionError as e: # We rarely get this exception while replacing the underlying WebSocket connections. # We can do one more try here as the self.current_session should be ready now. if self.logger.level <= logging.DEBUG: self.logger.debug( f"Failed to send a message (error: {e}, message: {message}, session: {session_id})" " as the underlying connection was replaced. Retrying the same request only one time..." ) # Although acquiring self.connect_operation_lock also for the first method call is the safest way, # we avoid synchronizing a lot for better performance. That's why we are doing a retry here. try: await self.connect_operation_lock.acquire() if await self.is_connected(): await self.current_session.send_str(message) # type: ignore[union-attr] else: self.logger.warning( f"The current session ({session_id}) is no longer active. " "Failed to send a message" ) raise e finally: if self.connect_operation_lock.locked() is True: self.connect_operation_lock.release() async def close(self): self.closed = True self.auto_reconnect_enabled = False await self.disconnect() if self.message_processor is not None: self.message_processor.cancel() if self.current_session_monitor is not None: self.current_session_monitor.cancel() if self.message_receiver is not None: self.message_receiver.cancel() if self.aiohttp_client_session is not None: await self.aiohttp_client_session.close() @classmethod def build_session_id(cls, session: ClientWebSocketResponse) -> str: if session is None: return "" return "s_" + str(hash(session)) ``` Socket Mode client ## Args **`app_token`** App-level token **`logger`** Custom logger **`web_client`** Web API client **`auto_reconnect_enabled`** True if automatic reconnection is enabled (default: True) **`ping_interval`** interval for ping-pong with Slack servers (seconds) **`trace_enabled`** True if more verbose logs to see what's happening under the hood **`proxy`** the HTTP proxy URL **`on_message_listeners`** listener functions for on\_message **`on_error_listeners`** listener functions for on\_error **`on_close_listeners`** listener functions for on\_close **`loop`** an existing asyncio event loop ### Ancestors * [AsyncBaseSocketModeClient](../async_client.html#slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient "slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient") ### Class variables `var current_session : aiohttp.client_ws.ClientWebSocketResponse | None` The type of the None singleton. `var current_session_monitor : _asyncio.Future | None` The type of the None singleton. `var default_auto_reconnect_enabled : bool` The type of the None singleton. `var last_ping_pong_time : float | None` The type of the None singleton. `var message_processor : _asyncio.Future` The type of the None singleton. `var message_receiver : _asyncio.Future | None` The type of the None singleton. `var on_close_listeners : List[Callable[[aiohttp._websocket.models.WSMessage], Awaitable[None]]]` The type of the None singleton. `var on_error_listeners : List[Callable[[aiohttp._websocket.models.WSMessage], Awaitable[None]]]` The type of the None singleton. `var on_message_listeners : List[Callable[[aiohttp._websocket.models.WSMessage], Awaitable[None]]]` The type of the None singleton. `var ping_interval : float` The type of the None singleton. `var proxy : str | None` The type of the None singleton. `var stale : bool` The type of the None singleton. ### Static methods `def build_session_id(session: aiohttp.client_ws.ClientWebSocketResponse) ‑> str` ### Methods `async def close(self)` Expand source code ``` async def close(self): self.closed = True self.auto_reconnect_enabled = False await self.disconnect() if self.message_processor is not None: self.message_processor.cancel() if self.current_session_monitor is not None: self.current_session_monitor.cancel() if self.message_receiver is not None: self.message_receiver.cancel() if self.aiohttp_client_session is not None: await self.aiohttp_client_session.close() ``` `async def connect(self)` Expand source code ``` async def connect(self): # This loop is used to ensure when a new session is created, # a new monitor and a new message receiver are also created. # If a new session is created but we failed to create the new # monitor or the new message, we should try it. while True: try: old_session: Optional[ClientWebSocketResponse] = ( None if self.current_session is None else self.current_session ) # If the old session is broken (e.g. reset by peer), it might fail to close it. # We don't want to retry when this kind of cases happen. try: # We should close old session before create a new one. Because when disconnect # reason is `too_many_websockets`, we need to close the old one first to # to decrease the number of connections. self.auto_reconnect_enabled = False if old_session is not None: await old_session.close() old_session_id = self.build_session_id(old_session) self.logger.info(f"The old session ({old_session_id}) has been abandoned") except Exception as e: self.logger.exception(f"Failed to close the old session : {e}") if self.wss_uri is None: # If the underlying WSS URL does not exist, # acquiring a new active WSS URL from the server-side first self.wss_uri = await self.issue_new_wss_url() self.current_session = await self.aiohttp_client_session.ws_connect( self.wss_uri, autoping=False, heartbeat=self.ping_interval, proxy=self.proxy, ssl=self.web_client.ssl if self.web_client.ssl is not None else True, ) session_id: str = await self.session_id() self.auto_reconnect_enabled = self.default_auto_reconnect_enabled self.stale = False self.logger.info(f"A new session ({session_id}) has been established") # The first ping from the new connection if self.logger.level <= logging.DEBUG: self.logger.debug(f"Sending a ping message with the newly established connection ({session_id})...") t = time.time() await self.current_session.ping(f"sdk-ping-pong:{t}".encode("utf-8")) if self.current_session_monitor is not None: self.current_session_monitor.cancel() self.current_session_monitor = asyncio.ensure_future(self.monitor_current_session()) if self.logger.level <= logging.DEBUG: self.logger.debug(f"A new monitor_current_session() executor has been recreated for {session_id}") if self.message_receiver is not None: self.message_receiver.cancel() self.message_receiver = asyncio.ensure_future(self.receive_messages()) if self.logger.level <= logging.DEBUG: self.logger.debug(f"A new receive_messages() executor has been recreated for {session_id}") break except Exception as e: self.logger.exception(f"Failed to connect (error: {e}); Retrying...") await asyncio.sleep(self.ping_interval) ``` `async def disconnect(self)` Expand source code ``` async def disconnect(self): if self.current_session is not None: await self.current_session.close() session_id = await self.session_id() self.logger.info(f"The current session ({session_id}) has been abandoned by disconnect() method call") ``` `async def is_connected(self) ‑> bool` Expand source code ``` async def is_connected(self) -> bool: connected: bool = ( not self.closed and not self.stale and self.current_session is not None and not self.current_session.closed and not await self.is_ping_pong_failing() ) if self.logger.level <= logging.DEBUG and connected is False: # Prints more detailed information about the inactive connection is_ping_pong_failing = await self.is_ping_pong_failing() session_id = await self.session_id() self.logger.debug( "Inactive connection detected (" f"session_id: {session_id}, " f"closed: {self.closed}, " f"stale: {self.stale}, " f"current_session.closed: {self.current_session and self.current_session.closed}, " f"is_ping_pong_failing: {is_ping_pong_failing}" ")" ) return connected ``` `async def is_ping_pong_failing(self) ‑> bool` Expand source code ``` async def is_ping_pong_failing(self) -> bool: if self.last_ping_pong_time is None: return False disconnected_seconds = int(time.time() - self.last_ping_pong_time) return disconnected_seconds >= (self.ping_interval * 4) ``` `async def monitor_current_session(self) ‑> None` Expand source code ``` async def monitor_current_session(self) -> None: # In the asyncio runtime, accessing a shared object (self.current_session here) from # multiple tasks can cause race conditions and errors. # To avoid such, we access only the session that is active when this loop starts. session: ClientWebSocketResponse = self.current_session # type: ignore[assignment] session_id: str = self.build_session_id(session) if self.logger.level <= logging.DEBUG: self.logger.debug(f"A new monitor_current_session() execution loop for {session_id} started") try: logging_interval = 100 counter_for_logging = 0 while not self.closed: if session != self.current_session: if self.logger.level <= logging.DEBUG: self.logger.debug(f"The monitor_current_session task for {session_id} is now cancelled") break try: if self.trace_enabled and self.logger.level <= logging.DEBUG: # The logging here is for detailed investigation on potential issues in this client. # If you don't see this log for a while, it means that # this receive_messages execution is no longer working for some reason. counter_for_logging += 1 if counter_for_logging >= logging_interval: counter_for_logging = 0 log_message = ( "#monitor_current_session method has been verifying if this session is active " f"(session: {session_id}, logging interval: {logging_interval})" ) self.logger.debug(log_message) await asyncio.sleep(self.ping_interval) if session is not None and session.closed is False: t = time.time() if self.last_ping_pong_time is None: self.last_ping_pong_time = float(t) try: await session.ping(f"sdk-ping-pong:{t}".encode("utf-8")) except Exception as e: # The ping() method can fail for some reason. # To establish a new connection even in this scenario, # we ignore the exception here. self.logger.warning(f"Failed to send a ping message ({session_id}): {e}") if self.auto_reconnect_enabled: should_reconnect = False if session is None or session.closed: self.logger.info(f"The session ({session_id}) seems to be already closed. Reconnecting...") should_reconnect = True if await self.is_ping_pong_failing(): disconnected_seconds = int(time.time() - self.last_ping_pong_time) # type: ignore[operator] self.logger.info( f"The session ({session_id}) seems to be stale. Reconnecting..." f" reason: disconnected for {disconnected_seconds}+ seconds)" ) self.stale = True self.last_ping_pong_time = None should_reconnect = True if should_reconnect is True or not await self.is_connected(): await self.connect_to_new_endpoint() except Exception as e: self.logger.error( f"Failed to check the current session ({session_id}) or reconnect to the server " f"(error: {type(e).__name__}, message: {e})" ) except asyncio.CancelledError: if self.logger.level <= logging.DEBUG: self.logger.debug(f"The monitor_current_session task for {session_id} is now cancelled") raise ``` `async def receive_messages(self) ‑> None` Expand source code ``` async def receive_messages(self) -> None: # In the asyncio runtime, accessing a shared object (self.current_session here) from # multiple tasks can cause race conditions and errors. # To avoid such, we access only the session that is active when this loop starts. session = self.current_session session_id = self.build_session_id(session) # type: ignore[arg-type] if self.logger.level <= logging.DEBUG: self.logger.debug(f"A new receive_messages() execution loop with {session_id} started") try: consecutive_error_count = 0 logging_interval = 100 counter_for_logging = 0 while not self.closed: if session != self.current_session: if self.logger.level <= logging.DEBUG: self.logger.debug(f"The running receive_messages task for {session_id} is now cancelled") break try: message: WSMessage = await session.receive() # type: ignore[union-attr] # just in case, checking if the value is not None if message is not None: if self.logger.level <= logging.DEBUG: # The following logging prints every single received message # except empty message data ones. m_type = WSMsgType(message.type) message_type = m_type.name if m_type is not None else message.type message_data = message.data if isinstance(message_data, bytes): message_data = message_data.decode("utf-8") if message_data is not None and isinstance(message_data, (str, bytes)) and len(message_data) > 0: # To skip the empty message that Slack server-side often sends self.logger.debug( f"Received message " f"(type: {message_type}, " f"data: {message_data}, " f"extra: {message.extra}, " f"session: {session_id})" ) if self.trace_enabled: # The logging here is for detailed trouble shooting of potential issues in this client. # If you don't see this log for a while, it can mean that # this receive_messages execution is no longer working for some reason. counter_for_logging += 1 if counter_for_logging >= logging_interval: counter_for_logging = 0 log_message = ( "#receive_messages method has been working without any issues " f"(session: {session_id}, logging interval: {logging_interval})" ) self.logger.debug(log_message) if message.type == WSMsgType.TEXT: message_data = message.data await self.enqueue_message(message_data) for listener in self.on_message_listeners: await listener(message) elif message.type == WSMsgType.CLOSE: if self.auto_reconnect_enabled: self.logger.info(f"Received CLOSE event from {session_id}. Reconnecting...") await self.connect_to_new_endpoint() for listener in self.on_close_listeners: await listener(message) elif message.type == WSMsgType.ERROR: for listener in self.on_error_listeners: await listener(message) elif message.type == WSMsgType.CLOSED: await asyncio.sleep(self.ping_interval) continue elif message.type == WSMsgType.PING: await session.pong(message.data) # type: ignore[union-attr] continue elif message.type == WSMsgType.PONG: if message.data is not None: str_message_data = message.data.decode("utf-8") elements = str_message_data.split(":") if len(elements) == 2 and elements[0] == "sdk-ping-pong": try: self.last_ping_pong_time = float(elements[1]) except Exception as e: self.logger.warning( f"Failed to parse the last_ping_pong_time value from {str_message_data}" f" - error : {e}, session: {session_id}" ) continue consecutive_error_count = 0 except Exception as e: consecutive_error_count += 1 self.logger.error(f"Failed to receive or enqueue a message: {type(e).__name__}, {e} ({session_id})") if isinstance(e, ClientConnectionError): await asyncio.sleep(self.ping_interval) else: await asyncio.sleep(consecutive_error_count) except asyncio.CancelledError: if self.logger.level <= logging.DEBUG: self.logger.debug(f"The running receive_messages task for {session_id} is now cancelled") raise ``` `async def send_message(self, message: str)` Expand source code ``` async def send_message(self, message: str): session_id = await self.session_id() if self.logger.level <= logging.DEBUG: self.logger.debug(f"Sending a message: {message} from session: {session_id}") try: await self.current_session.send_str(message) # type: ignore[union-attr] except ConnectionError as e: # We rarely get this exception while replacing the underlying WebSocket connections. # We can do one more try here as the self.current_session should be ready now. if self.logger.level <= logging.DEBUG: self.logger.debug( f"Failed to send a message (error: {e}, message: {message}, session: {session_id})" " as the underlying connection was replaced. Retrying the same request only one time..." ) # Although acquiring self.connect_operation_lock also for the first method call is the safest way, # we avoid synchronizing a lot for better performance. That's why we are doing a retry here. try: await self.connect_operation_lock.acquire() if await self.is_connected(): await self.current_session.send_str(message) # type: ignore[union-attr] else: self.logger.warning( f"The current session ({session_id}) is no longer active. " "Failed to send a message" ) raise e finally: if self.connect_operation_lock.locked() is True: self.connect_operation_lock.release() ``` `async def session_id(self) ‑> str` Expand source code ``` async def session_id(self) -> str: return self.build_session_id(self.current_session) # type: ignore[arg-type] ``` ### Inherited members * `**[AsyncBaseSocketModeClient](../async_client.html#slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient "slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient")**`: * `[app_token](../async_client.html#slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.app_token "slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.app_token")` * `[auto_reconnect_enabled](../async_client.html#slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.auto_reconnect_enabled "slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.auto_reconnect_enabled")` * `[closed](../async_client.html#slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.closed "slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.closed")` * `[connect_operation_lock](../async_client.html#slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.connect_operation_lock "slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.connect_operation_lock")` * `[logger](../async_client.html#slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.logger "slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.logger")` * `[message_listeners](../async_client.html#slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.message_listeners "slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.message_listeners")` * `[message_queue](../async_client.html#slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.message_queue "slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.message_queue")` * `[socket_mode_request_listeners](../async_client.html#slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.socket_mode_request_listeners "slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.socket_mode_request_listeners")` * `[trace_enabled](../async_client.html#slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.trace_enabled "slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.trace_enabled")` * `[web_client](../async_client.html#slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.web_client "slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.web_client")` * `[wss_uri](../async_client.html#slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.wss_uri "slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.wss_uri")` --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/socket_mode/builtin # Module slack_sdk.socket_mode.builtin ## Sub-modules `[slack_sdk.socket_mode.builtin.client](client.html "slack_sdk.socket_mode.builtin.client")` The built-in Socket Mode client … `[slack_sdk.socket_mode.builtin.connection](connection.html "slack_sdk.socket_mode.builtin.connection")` `[slack_sdk.socket_mode.builtin.frame_header](frame_header.html "slack_sdk.socket_mode.builtin.frame_header")` `[slack_sdk.socket_mode.builtin.internals](internals.html "slack_sdk.socket_mode.builtin.internals")` ## Classes `class SocketModeClient (app_token: str, logger: logging.Logger | None = None, web_client: [WebClient](../../web/client.html#slack_sdk.web.client.WebClient "slack_sdk.web.client.WebClient") | None = None, auto_reconnect_enabled: bool = True, trace_enabled: bool = False, all_message_trace_enabled: bool = False, ping_pong_trace_enabled: bool = False, ping_interval: float = 5, receive_buffer_size: int = 1024, concurrency: int = 10, proxy: str | None = None, proxy_headers: Dict[str, str] | None = None, on_message_listeners: List[Callable[[str], None]] | None = None, on_error_listeners: List[Callable[[Exception], None]] | None = None, on_close_listeners: List[Callable[[int, str | None], None]] | None = None)` Expand source code ``` class SocketModeClient(BaseSocketModeClient): logger: Logger web_client: WebClient app_token: str wss_uri: Optional[str] # type: ignore[assignment] message_queue: Queue message_listeners: List[ Union[ WebSocketMessageListener, Callable[["BaseSocketModeClient", dict, Optional[str]], None], ] ] socket_mode_request_listeners: List[ Union[ SocketModeRequestListener, Callable[["BaseSocketModeClient", SocketModeRequest], None], ] ] current_session: Optional[Connection] current_session_state: ConnectionState current_session_runner: IntervalRunner current_app_monitor: IntervalRunner current_app_monitor_started: bool message_processor: IntervalRunner message_workers: ThreadPoolExecutor auto_reconnect_enabled: bool default_auto_reconnect_enabled: bool trace_enabled: bool receive_buffer_size: int # bytes size connect_operation_lock: Lock on_message_listeners: List[Callable[[str], None]] on_error_listeners: List[Callable[[Exception], None]] on_close_listeners: List[Callable[[int, Optional[str]], None]] def __init__( self, app_token: str, logger: Optional[Logger] = None, web_client: Optional[WebClient] = None, auto_reconnect_enabled: bool = True, trace_enabled: bool = False, all_message_trace_enabled: bool = False, ping_pong_trace_enabled: bool = False, ping_interval: float = 5, receive_buffer_size: int = 1024, concurrency: int = 10, proxy: Optional[str] = None, proxy_headers: Optional[Dict[str, str]] = None, on_message_listeners: Optional[List[Callable[[str], None]]] = None, on_error_listeners: Optional[List[Callable[[Exception], None]]] = None, on_close_listeners: Optional[List[Callable[[int, Optional[str]], None]]] = None, ): """Socket Mode client Args: app_token: App-level token logger: Custom logger web_client: Web API client auto_reconnect_enabled: True if automatic reconnection is enabled (default: True) trace_enabled: True if more detailed debug-logging is enabled (default: False) all_message_trace_enabled: True if all message dump in debug logs is enabled (default: False) ping_pong_trace_enabled: True if trace logging for all ping-pong communications is enabled (default: False) ping_interval: interval for ping-pong with Slack servers (seconds) receive_buffer_size: the chunk size of a single socket recv operation (default: 1024) concurrency: the size of thread pool (default: 10) proxy: the HTTP proxy URL proxy_headers: additional HTTP header for proxy connection on_message_listeners: listener functions for on_message on_error_listeners: listener functions for on_error on_close_listeners: listener functions for on_close """ self.app_token = app_token self.logger = logger or logging.getLogger(__name__) self.web_client = web_client or WebClient() self.default_auto_reconnect_enabled = auto_reconnect_enabled self.auto_reconnect_enabled = self.default_auto_reconnect_enabled self.trace_enabled = trace_enabled self.all_message_trace_enabled = all_message_trace_enabled self.ping_pong_trace_enabled = ping_pong_trace_enabled self.ping_interval = ping_interval self.receive_buffer_size = receive_buffer_size if self.receive_buffer_size < 16: raise SlackClientConfigurationError("Too small receive_buffer_size detected.") self.wss_uri = None self.message_queue = Queue() self.message_listeners = [] self.socket_mode_request_listeners = [] self.current_session = None self.current_session_state = ConnectionState() self.current_session_runner = IntervalRunner(self._run_current_session, 0.1).start() self.current_app_monitor_started = False self.current_app_monitor = IntervalRunner(self._monitor_current_session, self.ping_interval) self.closed = False self.connect_operation_lock = Lock() self.message_processor = IntervalRunner(self.process_messages, 0.001).start() self.message_workers = ThreadPoolExecutor(max_workers=concurrency) self.proxy = proxy if self.proxy is None or len(self.proxy.strip()) == 0: env_variable = load_http_proxy_from_env(self.logger) if env_variable is not None: self.proxy = env_variable self.proxy_headers = proxy_headers self.on_message_listeners = on_message_listeners or [] self.on_error_listeners = on_error_listeners or [] self.on_close_listeners = on_close_listeners or [] def session_id(self) -> Optional[str]: if self.current_session is not None: return self.current_session.session_id return None def is_connected(self) -> bool: return self.current_session is not None and self.current_session.is_active() def connect(self) -> None: old_session: Optional[Connection] = self.current_session old_current_session_state: ConnectionState = self.current_session_state if self.wss_uri is None: self.wss_uri = self.issue_new_wss_url() current_session = Connection( url=self.wss_uri, logger=self.logger, ping_interval=self.ping_interval, trace_enabled=self.trace_enabled, all_message_trace_enabled=self.all_message_trace_enabled, ping_pong_trace_enabled=self.ping_pong_trace_enabled, receive_buffer_size=self.receive_buffer_size, proxy=self.proxy, proxy_headers=self.proxy_headers, on_message_listener=self._on_message, on_error_listener=self._on_error, on_close_listener=self._on_close, ssl_context=self.web_client.ssl, ) current_session.connect() if old_current_session_state is not None: old_current_session_state.terminated = True if old_session is not None: old_session.close() self.current_session = current_session self.current_session_state = ConnectionState() self.auto_reconnect_enabled = self.default_auto_reconnect_enabled if not self.current_app_monitor_started: self.current_app_monitor_started = True self.current_app_monitor.start() self.logger.info(f"A new session has been established (session id: {self.session_id()})") def disconnect(self) -> None: if self.current_session is not None: self.current_session.close() def send_message(self, message: str) -> None: if self.logger.level <= logging.DEBUG: self.logger.debug(f"Sending a message (session id: {self.session_id()}, message: {message})") try: self.current_session.send(message) # type: ignore[union-attr] except SlackClientNotConnectedError as e: # We rarely get this exception while replacing the underlying WebSocket connections. # We can do one more try here as the self.current_session should be ready now. if self.logger.level <= logging.DEBUG: self.logger.debug( f"Failed to send a message (session id: {self.session_id()}, error: {e}, message: {message})" " as the underlying connection was replaced. Retrying the same request only one time..." ) # Although acquiring self.connect_operation_lock also for the first method call is the safest way, # we avoid synchronizing a lot for better performance. That's why we are doing a retry here. with self.connect_operation_lock: if self.is_connected(): self.current_session.send(message) # type: ignore[union-attr] else: self.logger.warning( f"The current session (session id: {self.session_id()}) is no longer active. " "Failed to send a message" ) raise e def close(self): self.closed = True self.auto_reconnect_enabled = False self.disconnect() if self.current_app_monitor.is_alive(): self.current_app_monitor.shutdown() if self.message_processor.is_alive(): self.message_processor.shutdown() self.message_workers.shutdown() def _on_message(self, message: str): if self.logger.level <= logging.DEBUG: self.logger.debug(f"on_message invoked: (message: {debug_redacted_message_string(message)})") self.enqueue_message(message) for listener in self.on_message_listeners: listener(message) def _on_error(self, error: Exception): error_message = ( f"on_error invoked (session id: {self.session_id()}, " f"error: {type(error).__name__}, message: {error})" ) if self.trace_enabled: self.logger.exception(error_message) else: self.logger.error(error_message) for listener in self.on_error_listeners: listener(error) def _on_close(self, code: int, reason: Optional[str] = None): if self.logger.level <= logging.DEBUG: self.logger.debug(f"on_close invoked (session id: {self.session_id()})") if self.auto_reconnect_enabled: self.logger.info("Received CLOSE event. Reconnecting... " f"(session id: {self.session_id()})") self.connect_to_new_endpoint() for listener in self.on_close_listeners: listener(code, reason) def _run_current_session(self): if self.current_session is not None and self.current_session.is_active(): session_id = self.session_id() try: self.logger.info("Starting to receive messages from a new connection" f" (session id: {session_id})") self.current_session_state.terminated = False self.current_session.run_until_completion(self.current_session_state) self.logger.info("Stopped receiving messages from a connection" f" (session id: {session_id})") except Exception as e: error_message = "Failed to start or stop the current session" f" (session id: {session_id}, error: {e})" if self.trace_enabled: self.logger.exception(error_message) else: self.logger.error(error_message) def _monitor_current_session(self): if self.current_app_monitor_started: try: self.current_session.check_state() if self.auto_reconnect_enabled and (self.current_session is None or not self.current_session.is_active()): self.logger.info( "The session seems to be already closed. Reconnecting... " f"(session id: {self.session_id()})" ) self.connect_to_new_endpoint() except Exception as e: self.logger.error( "Failed to check the current session or reconnect to the server " f"(session id: {self.session_id()}, error: {type(e).__name__}, message: {e})" ) ``` Socket Mode client ## Args **`app_token`** App-level token **`logger`** Custom logger **`web_client`** Web API client **`auto_reconnect_enabled`** True if automatic reconnection is enabled (default: True) **`trace_enabled`** True if more detailed debug-logging is enabled (default: False) **`all_message_trace_enabled`** True if all message dump in debug logs is enabled (default: False) **`ping_pong_trace_enabled`** True if trace logging for all ping-pong communications is enabled (default: False) **`ping_interval`** interval for ping-pong with Slack servers (seconds) **`receive_buffer_size`** the chunk size of a single socket recv operation (default: 1024) **`concurrency`** the size of thread pool (default: 10) **`proxy`** the HTTP proxy URL **`proxy_headers`** additional HTTP header for proxy connection **`on_message_listeners`** listener functions for on\_message **`on_error_listeners`** listener functions for on\_error **`on_close_listeners`** listener functions for on\_close ### Ancestors * [BaseSocketModeClient](../client.html#slack_sdk.socket_mode.client.BaseSocketModeClient "slack_sdk.socket_mode.client.BaseSocketModeClient") ### Class variables `var auto_reconnect_enabled : bool` The type of the None singleton. `var current_app_monitor : [IntervalRunner](../interval_runner.html#slack_sdk.socket_mode.interval_runner.IntervalRunner "slack_sdk.socket_mode.interval_runner.IntervalRunner")` The type of the None singleton. `var current_app_monitor_started : bool` The type of the None singleton. `var current_session : [Connection](connection.html#slack_sdk.socket_mode.builtin.connection.Connection "slack_sdk.socket_mode.builtin.connection.Connection") | None` The type of the None singleton. `var current_session_runner : [IntervalRunner](../interval_runner.html#slack_sdk.socket_mode.interval_runner.IntervalRunner "slack_sdk.socket_mode.interval_runner.IntervalRunner")` The type of the None singleton. `var current_session_state : [ConnectionState](connection.html#slack_sdk.socket_mode.builtin.connection.ConnectionState "slack_sdk.socket_mode.builtin.connection.ConnectionState")` The type of the None singleton. `var default_auto_reconnect_enabled : bool` The type of the None singleton. `var on_close_listeners : List[Callable[[int, str | None], None]]` The type of the None singleton. `var on_error_listeners : List[Callable[[Exception], None]]` The type of the None singleton. `var on_message_listeners : List[Callable[[str], None]]` The type of the None singleton. `var receive_buffer_size : int` The type of the None singleton. `var trace_enabled : bool` The type of the None singleton. ### Methods `def close(self)` Expand source code ``` def close(self): self.closed = True self.auto_reconnect_enabled = False self.disconnect() if self.current_app_monitor.is_alive(): self.current_app_monitor.shutdown() if self.message_processor.is_alive(): self.message_processor.shutdown() self.message_workers.shutdown() ``` `def connect(self) ‑> None` Expand source code ``` def connect(self) -> None: old_session: Optional[Connection] = self.current_session old_current_session_state: ConnectionState = self.current_session_state if self.wss_uri is None: self.wss_uri = self.issue_new_wss_url() current_session = Connection( url=self.wss_uri, logger=self.logger, ping_interval=self.ping_interval, trace_enabled=self.trace_enabled, all_message_trace_enabled=self.all_message_trace_enabled, ping_pong_trace_enabled=self.ping_pong_trace_enabled, receive_buffer_size=self.receive_buffer_size, proxy=self.proxy, proxy_headers=self.proxy_headers, on_message_listener=self._on_message, on_error_listener=self._on_error, on_close_listener=self._on_close, ssl_context=self.web_client.ssl, ) current_session.connect() if old_current_session_state is not None: old_current_session_state.terminated = True if old_session is not None: old_session.close() self.current_session = current_session self.current_session_state = ConnectionState() self.auto_reconnect_enabled = self.default_auto_reconnect_enabled if not self.current_app_monitor_started: self.current_app_monitor_started = True self.current_app_monitor.start() self.logger.info(f"A new session has been established (session id: {self.session_id()})") ``` `def disconnect(self) ‑> None` Expand source code ``` def disconnect(self) -> None: if self.current_session is not None: self.current_session.close() ``` `def is_connected(self) ‑> bool` Expand source code ``` def is_connected(self) -> bool: return self.current_session is not None and self.current_session.is_active() ``` `def send_message(self, message: str) ‑> None` Expand source code ``` def send_message(self, message: str) -> None: if self.logger.level <= logging.DEBUG: self.logger.debug(f"Sending a message (session id: {self.session_id()}, message: {message})") try: self.current_session.send(message) # type: ignore[union-attr] except SlackClientNotConnectedError as e: # We rarely get this exception while replacing the underlying WebSocket connections. # We can do one more try here as the self.current_session should be ready now. if self.logger.level <= logging.DEBUG: self.logger.debug( f"Failed to send a message (session id: {self.session_id()}, error: {e}, message: {message})" " as the underlying connection was replaced. Retrying the same request only one time..." ) # Although acquiring self.connect_operation_lock also for the first method call is the safest way, # we avoid synchronizing a lot for better performance. That's why we are doing a retry here. with self.connect_operation_lock: if self.is_connected(): self.current_session.send(message) # type: ignore[union-attr] else: self.logger.warning( f"The current session (session id: {self.session_id()}) is no longer active. " "Failed to send a message" ) raise e ``` `def session_id(self) ‑> str | None` Expand source code ``` def session_id(self) -> Optional[str]: if self.current_session is not None: return self.current_session.session_id return None ``` ### Inherited members * `**[BaseSocketModeClient](../client.html#slack_sdk.socket_mode.client.BaseSocketModeClient "slack_sdk.socket_mode.client.BaseSocketModeClient")**`: * `[app_token](../client.html#slack_sdk.socket_mode.client.BaseSocketModeClient.app_token "slack_sdk.socket_mode.client.BaseSocketModeClient.app_token")` * `[closed](../client.html#slack_sdk.socket_mode.client.BaseSocketModeClient.closed "slack_sdk.socket_mode.client.BaseSocketModeClient.closed")` * `[connect_operation_lock](../client.html#slack_sdk.socket_mode.client.BaseSocketModeClient.connect_operation_lock "slack_sdk.socket_mode.client.BaseSocketModeClient.connect_operation_lock")` * `[logger](../client.html#slack_sdk.socket_mode.client.BaseSocketModeClient.logger "slack_sdk.socket_mode.client.BaseSocketModeClient.logger")` * `[message_listeners](../client.html#slack_sdk.socket_mode.client.BaseSocketModeClient.message_listeners "slack_sdk.socket_mode.client.BaseSocketModeClient.message_listeners")` * `[message_processor](../client.html#slack_sdk.socket_mode.client.BaseSocketModeClient.message_processor "slack_sdk.socket_mode.client.BaseSocketModeClient.message_processor")` * `[message_queue](../client.html#slack_sdk.socket_mode.client.BaseSocketModeClient.message_queue "slack_sdk.socket_mode.client.BaseSocketModeClient.message_queue")` * `[message_workers](../client.html#slack_sdk.socket_mode.client.BaseSocketModeClient.message_workers "slack_sdk.socket_mode.client.BaseSocketModeClient.message_workers")` * `[socket_mode_request_listeners](../client.html#slack_sdk.socket_mode.client.BaseSocketModeClient.socket_mode_request_listeners "slack_sdk.socket_mode.client.BaseSocketModeClient.socket_mode_request_listeners")` * `[web_client](../client.html#slack_sdk.socket_mode.client.BaseSocketModeClient.web_client "slack_sdk.socket_mode.client.BaseSocketModeClient.web_client")` * `[wss_uri](../client.html#slack_sdk.socket_mode.client.BaseSocketModeClient.wss_uri "slack_sdk.socket_mode.client.BaseSocketModeClient.wss_uri")` --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/socket_mode/logger # Module slack_sdk.socket_mode.logger ## Sub-modules `[slack_sdk.socket_mode.logger.messages](messages.html "slack_sdk.socket_mode.logger.messages")` --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/socket_mode/websocket_client # Module slack_sdk.socket_mode.websocket_client websocket-client based Socket Mode client * [https://docs.slack.dev/apis/events-api/using-socket-mode/](https://docs.slack.dev/apis/events-api/using-socket-mode/) * [https://docs.slack.dev/tools/python-slack-sdk/socket-mode/](https://docs.slack.dev/tools/python-slack-sdk/socket-mode/) * [https://pypi.org/project/websocket-client/](https://pypi.org/project/websocket-client/) ## Classes `class SocketModeClient (app_token: str, logger: logging.Logger | None = None, web_client: [WebClient](../../web/client.html#slack_sdk.web.client.WebClient "slack_sdk.web.client.WebClient") | None = None, auto_reconnect_enabled: bool = True, ping_interval: float = 10, concurrency: int = 10, trace_enabled: bool = False, http_proxy_host: str | None = None, http_proxy_port: int | None = None, http_proxy_auth: Tuple[str, str] | None = None, proxy_type: str | None = None, on_open_listeners: List[Callable[[websocket._app.WebSocketApp], None]] | None = None, on_message_listeners: List[Callable[[websocket._app.WebSocketApp, str], None]] | None = None, on_error_listeners: List[Callable[[websocket._app.WebSocketApp, Exception], None]] | None = None, on_close_listeners: List[Callable[[websocket._app.WebSocketApp], None]] | None = None)` Expand source code ``` class SocketModeClient(BaseSocketModeClient): logger: Logger web_client: WebClient app_token: str wss_uri: Optional[str] # type: ignore[assignment] message_queue: Queue message_listeners: List[ Union[ WebSocketMessageListener, Callable[["BaseSocketModeClient", dict, Optional[str]], None], ] ] socket_mode_request_listeners: List[ Union[ SocketModeRequestListener, Callable[["BaseSocketModeClient", SocketModeRequest], None], ] ] current_app_monitor: IntervalRunner current_app_monitor_started: bool message_processor: IntervalRunner message_workers: ThreadPoolExecutor current_session: Optional[WebSocketApp] current_session_runner: IntervalRunner auto_reconnect_enabled: bool default_auto_reconnect_enabled: bool closed: bool connect_operation_lock: Lock on_open_listeners: List[Callable[[WebSocketApp], None]] on_message_listeners: List[Callable[[WebSocketApp, str], None]] on_error_listeners: List[Callable[[WebSocketApp, Exception], None]] on_close_listeners: List[Callable[[WebSocketApp], None]] def __init__( self, app_token: str, logger: Optional[Logger] = None, web_client: Optional[WebClient] = None, auto_reconnect_enabled: bool = True, ping_interval: float = 10, concurrency: int = 10, trace_enabled: bool = False, http_proxy_host: Optional[str] = None, http_proxy_port: Optional[int] = None, http_proxy_auth: Optional[Tuple[str, str]] = None, proxy_type: Optional[str] = None, on_open_listeners: Optional[List[Callable[[WebSocketApp], None]]] = None, on_message_listeners: Optional[List[Callable[[WebSocketApp, str], None]]] = None, on_error_listeners: Optional[List[Callable[[WebSocketApp, Exception], None]]] = None, on_close_listeners: Optional[List[Callable[[WebSocketApp], None]]] = None, ): """ Args: app_token: App-level token logger: Custom logger web_client: Web API client auto_reconnect_enabled: True if automatic reconnection is enabled (default: True) ping_interval: interval for ping-pong with Slack servers (seconds) concurrency: the size of thread pool (default: 10) http_proxy_host: the HTTP proxy host http_proxy_port: the HTTP proxy port http_proxy_auth: the HTTP proxy username & password proxy_type: the HTTP proxy type on_open_listeners: listener functions for on_open on_message_listeners: listener functions for on_message on_error_listeners: listener functions for on_error on_close_listeners: listener functions for on_close """ self.app_token = app_token self.logger = logger or logging.getLogger(__name__) self.web_client = web_client or WebClient() self.default_auto_reconnect_enabled = auto_reconnect_enabled self.auto_reconnect_enabled = self.default_auto_reconnect_enabled self.ping_interval = ping_interval self.wss_uri = None self.message_queue = Queue() self.message_listeners = [] self.socket_mode_request_listeners = [] self.current_session = None self.current_session_runner = IntervalRunner(self._run_current_session, 0.5).start() self.current_app_monitor_started = False self.current_app_monitor = IntervalRunner(self._monitor_current_session, self.ping_interval) self.closed = False self.connect_operation_lock = Lock() self.message_processor = IntervalRunner(self.process_messages, 0.001).start() self.message_workers = ThreadPoolExecutor(max_workers=concurrency) # NOTE: only global settings is provided by the library websocket.enableTrace(trace_enabled) self.http_proxy_host = http_proxy_host self.http_proxy_port = http_proxy_port self.http_proxy_auth = http_proxy_auth self.proxy_type = proxy_type self.on_open_listeners = on_open_listeners or [] self.on_message_listeners = on_message_listeners or [] self.on_error_listeners = on_error_listeners or [] self.on_close_listeners = on_close_listeners or [] def is_connected(self) -> bool: return self.current_session is not None and self.current_session.sock is not None def connect(self) -> None: def on_open(ws: WebSocketApp): if self.logger.level <= logging.DEBUG: self.logger.debug("on_open invoked") for listener in self.on_open_listeners: listener(ws) def on_message(ws: WebSocketApp, message: str): if self.logger.level <= logging.DEBUG: self.logger.debug(f"on_message invoked: (message: {debug_redacted_message_string(message)})") self.enqueue_message(message) for listener in self.on_message_listeners: listener(ws, message) def on_error(ws: WebSocketApp, error: Exception): self.logger.error(f"on_error invoked (error: {type(error).__name__}, message: {error})") for listener in self.on_error_listeners: listener(ws, error) def on_close( ws: WebSocketApp, close_status_code: Optional[int] = None, close_msg: Optional[str] = None, ): if self.logger.level <= logging.DEBUG: self.logger.debug(f"on_close invoked: (code: {close_status_code}, message: {close_msg})") if self.auto_reconnect_enabled: self.logger.info("Received CLOSE event. Reconnecting...") self.connect_to_new_endpoint() for listener in self.on_close_listeners: listener(ws) old_session: Optional[WebSocketApp] = self.current_session if self.wss_uri is None: self.wss_uri = self.issue_new_wss_url() self.current_session = websocket.WebSocketApp( self.wss_uri, on_open=on_open, on_message=on_message, on_error=on_error, on_close=on_close, ) self.auto_reconnect_enabled = self.default_auto_reconnect_enabled if not self.current_app_monitor_started: self.current_app_monitor_started = True self.current_app_monitor.start() if old_session is not None: old_session.close() self.logger.info("A new session has been established") def disconnect(self) -> None: if self.current_session is not None: self.current_session.close() def send_message(self, message: str) -> None: if self.logger.level <= logging.DEBUG: self.logger.debug(f"Sending a message: {message}") try: self.current_session.send(message) # type: ignore[union-attr] except WebSocketException as e: # We rarely get this exception while replacing the underlying WebSocket connections. # We can do one more try here as the self.current_session should be ready now. if self.logger.level <= logging.DEBUG: self.logger.debug( f"Failed to send a message (error: {e}, message: {message})" " as the underlying connection was replaced. Retrying the same request only one time..." ) # Although acquiring self.connect_operation_lock also for the first method call is the safest way, # we avoid synchronizing a lot for better performance. That's why we are doing a retry here. with self.connect_operation_lock: if self.is_connected(): self.current_session.send(message) # type: ignore[union-attr] else: self.logger.warning( f"The current session (session id: {self.session_id()}) is no longer active. " # type: ignore[attr-defined] # noqa: E501 "Failed to send a message" ) raise e def close(self) -> None: self.closed = True self.auto_reconnect_enabled = False self.disconnect() self.current_session_runner.shutdown() self.current_app_monitor.shutdown() self.message_processor.shutdown() self.message_workers.shutdown() def _run_current_session(self): if self.current_session is not None: try: self.logger.info("Starting to receive messages from a new connection") self.current_session.run_forever( ping_interval=self.ping_interval, http_proxy_host=self.http_proxy_host, http_proxy_port=self.http_proxy_port, http_proxy_auth=self.http_proxy_auth, proxy_type=self.proxy_type, ) self.logger.info("Stopped receiving messages from a connection") except Exception as e: self.logger.exception(f"Failed to start or stop the current session: {e}") # To let the monitoring job detect the connection issue, closing this session if self.current_session is not None: self.current_session.close() def _monitor_current_session(self): if self.current_app_monitor_started: try: if self.auto_reconnect_enabled and (self.current_session is None or self.current_session.sock is None): self.logger.info("The session seems to be already closed. Reconnecting...") self.connect_to_new_endpoint() except Exception as e: self.logger.error( "Failed to check the current session or reconnect to the server " f"(error: {type(e).__name__}, message: {e})" ) ``` ## Args **`app_token`** App-level token **`logger`** Custom logger **`web_client`** Web API client **`auto_reconnect_enabled`** True if automatic reconnection is enabled (default: True) **`ping_interval`** interval for ping-pong with Slack servers (seconds) **`concurrency`** the size of thread pool (default: 10) **`http_proxy_host`** the HTTP proxy host **`http_proxy_port`** the HTTP proxy port **`http_proxy_auth`** the HTTP proxy username & password **`proxy_type`** the HTTP proxy type **`on_open_listeners`** listener functions for on\_open **`on_message_listeners`** listener functions for on\_message **`on_error_listeners`** listener functions for on\_error **`on_close_listeners`** listener functions for on\_close ### Ancestors * [BaseSocketModeClient](../client.html#slack_sdk.socket_mode.client.BaseSocketModeClient "slack_sdk.socket_mode.client.BaseSocketModeClient") ### Class variables `var auto_reconnect_enabled : bool` The type of the None singleton. `var current_app_monitor : [IntervalRunner](../interval_runner.html#slack_sdk.socket_mode.interval_runner.IntervalRunner "slack_sdk.socket_mode.interval_runner.IntervalRunner")` The type of the None singleton. `var current_app_monitor_started : bool` The type of the None singleton. `var current_session : websocket._app.WebSocketApp | None` The type of the None singleton. `var current_session_runner : [IntervalRunner](../interval_runner.html#slack_sdk.socket_mode.interval_runner.IntervalRunner "slack_sdk.socket_mode.interval_runner.IntervalRunner")` The type of the None singleton. `var default_auto_reconnect_enabled : bool` The type of the None singleton. `var on_close_listeners : List[Callable[[websocket._app.WebSocketApp], None]]` The type of the None singleton. `var on_error_listeners : List[Callable[[websocket._app.WebSocketApp, Exception], None]]` The type of the None singleton. `var on_message_listeners : List[Callable[[websocket._app.WebSocketApp, str], None]]` The type of the None singleton. `var on_open_listeners : List[Callable[[websocket._app.WebSocketApp], None]]` The type of the None singleton. ### Methods `def close(self) ‑> None` Expand source code ``` def close(self) -> None: self.closed = True self.auto_reconnect_enabled = False self.disconnect() self.current_session_runner.shutdown() self.current_app_monitor.shutdown() self.message_processor.shutdown() self.message_workers.shutdown() ``` `def connect(self) ‑> None` Expand source code ``` def connect(self) -> None: def on_open(ws: WebSocketApp): if self.logger.level <= logging.DEBUG: self.logger.debug("on_open invoked") for listener in self.on_open_listeners: listener(ws) def on_message(ws: WebSocketApp, message: str): if self.logger.level <= logging.DEBUG: self.logger.debug(f"on_message invoked: (message: {debug_redacted_message_string(message)})") self.enqueue_message(message) for listener in self.on_message_listeners: listener(ws, message) def on_error(ws: WebSocketApp, error: Exception): self.logger.error(f"on_error invoked (error: {type(error).__name__}, message: {error})") for listener in self.on_error_listeners: listener(ws, error) def on_close( ws: WebSocketApp, close_status_code: Optional[int] = None, close_msg: Optional[str] = None, ): if self.logger.level <= logging.DEBUG: self.logger.debug(f"on_close invoked: (code: {close_status_code}, message: {close_msg})") if self.auto_reconnect_enabled: self.logger.info("Received CLOSE event. Reconnecting...") self.connect_to_new_endpoint() for listener in self.on_close_listeners: listener(ws) old_session: Optional[WebSocketApp] = self.current_session if self.wss_uri is None: self.wss_uri = self.issue_new_wss_url() self.current_session = websocket.WebSocketApp( self.wss_uri, on_open=on_open, on_message=on_message, on_error=on_error, on_close=on_close, ) self.auto_reconnect_enabled = self.default_auto_reconnect_enabled if not self.current_app_monitor_started: self.current_app_monitor_started = True self.current_app_monitor.start() if old_session is not None: old_session.close() self.logger.info("A new session has been established") ``` `def disconnect(self) ‑> None` Expand source code ``` def disconnect(self) -> None: if self.current_session is not None: self.current_session.close() ``` `def is_connected(self) ‑> bool` Expand source code ``` def is_connected(self) -> bool: return self.current_session is not None and self.current_session.sock is not None ``` `def send_message(self, message: str) ‑> None` Expand source code ``` def send_message(self, message: str) -> None: if self.logger.level <= logging.DEBUG: self.logger.debug(f"Sending a message: {message}") try: self.current_session.send(message) # type: ignore[union-attr] except WebSocketException as e: # We rarely get this exception while replacing the underlying WebSocket connections. # We can do one more try here as the self.current_session should be ready now. if self.logger.level <= logging.DEBUG: self.logger.debug( f"Failed to send a message (error: {e}, message: {message})" " as the underlying connection was replaced. Retrying the same request only one time..." ) # Although acquiring self.connect_operation_lock also for the first method call is the safest way, # we avoid synchronizing a lot for better performance. That's why we are doing a retry here. with self.connect_operation_lock: if self.is_connected(): self.current_session.send(message) # type: ignore[union-attr] else: self.logger.warning( f"The current session (session id: {self.session_id()}) is no longer active. " # type: ignore[attr-defined] # noqa: E501 "Failed to send a message" ) raise e ``` ### Inherited members * `**[BaseSocketModeClient](../client.html#slack_sdk.socket_mode.client.BaseSocketModeClient "slack_sdk.socket_mode.client.BaseSocketModeClient")**`: * `[app_token](../client.html#slack_sdk.socket_mode.client.BaseSocketModeClient.app_token "slack_sdk.socket_mode.client.BaseSocketModeClient.app_token")` * `[closed](../client.html#slack_sdk.socket_mode.client.BaseSocketModeClient.closed "slack_sdk.socket_mode.client.BaseSocketModeClient.closed")` * `[connect_operation_lock](../client.html#slack_sdk.socket_mode.client.BaseSocketModeClient.connect_operation_lock "slack_sdk.socket_mode.client.BaseSocketModeClient.connect_operation_lock")` * `[logger](../client.html#slack_sdk.socket_mode.client.BaseSocketModeClient.logger "slack_sdk.socket_mode.client.BaseSocketModeClient.logger")` * `[message_listeners](../client.html#slack_sdk.socket_mode.client.BaseSocketModeClient.message_listeners "slack_sdk.socket_mode.client.BaseSocketModeClient.message_listeners")` * `[message_processor](../client.html#slack_sdk.socket_mode.client.BaseSocketModeClient.message_processor "slack_sdk.socket_mode.client.BaseSocketModeClient.message_processor")` * `[message_queue](../client.html#slack_sdk.socket_mode.client.BaseSocketModeClient.message_queue "slack_sdk.socket_mode.client.BaseSocketModeClient.message_queue")` * `[message_workers](../client.html#slack_sdk.socket_mode.client.BaseSocketModeClient.message_workers "slack_sdk.socket_mode.client.BaseSocketModeClient.message_workers")` * `[socket_mode_request_listeners](../client.html#slack_sdk.socket_mode.client.BaseSocketModeClient.socket_mode_request_listeners "slack_sdk.socket_mode.client.BaseSocketModeClient.socket_mode_request_listeners")` * `[web_client](../client.html#slack_sdk.socket_mode.client.BaseSocketModeClient.web_client "slack_sdk.socket_mode.client.BaseSocketModeClient.web_client")` * `[wss_uri](../client.html#slack_sdk.socket_mode.client.BaseSocketModeClient.wss_uri "slack_sdk.socket_mode.client.BaseSocketModeClient.wss_uri")` --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/socket_mode/websockets # Module slack_sdk.socket_mode.websockets websockets based Socket Mode client * [https://docs.slack.dev/apis/events-api/using-socket-mode/](https://docs.slack.dev/apis/events-api/using-socket-mode/) * [https://docs.slack.dev/tools/python-slack-sdk/socket-mode/](https://docs.slack.dev/tools/python-slack-sdk/socket-mode/) * [https://pypi.org/project/websockets/](https://pypi.org/project/websockets/) ## Classes `class SocketModeClient (app_token: str, logger: logging.Logger | None = None, web_client: [AsyncWebClient](../../web/async_client.html#slack_sdk.web.async_client.AsyncWebClient "slack_sdk.web.async_client.AsyncWebClient") | None = None, auto_reconnect_enabled: bool = True, ping_interval: float = 10, trace_enabled: bool = False)` Expand source code ``` class SocketModeClient(AsyncBaseSocketModeClient): logger: Logger web_client: AsyncWebClient app_token: str wss_uri: Optional[str] # type: ignore[assignment] auto_reconnect_enabled: bool message_queue: Queue message_listeners: List[ Union[ AsyncWebSocketMessageListener, Callable[["AsyncBaseSocketModeClient", dict, Optional[str]], Awaitable[None]], ] ] socket_mode_request_listeners: List[ Union[ AsyncSocketModeRequestListener, Callable[["AsyncBaseSocketModeClient", SocketModeRequest], Awaitable[None]], ] ] message_receiver: Optional[Future] message_processor: Future ping_interval: float trace_enabled: bool current_session: Optional[ClientConnection] current_session_monitor: Optional[Future] default_auto_reconnect_enabled: bool closed: bool connect_operation_lock: Lock def __init__( self, app_token: str, logger: Optional[Logger] = None, web_client: Optional[AsyncWebClient] = None, auto_reconnect_enabled: bool = True, ping_interval: float = 10, trace_enabled: bool = False, ): """Socket Mode client Args: app_token: App-level token logger: Custom logger web_client: Web API client auto_reconnect_enabled: True if automatic reconnection is enabled (default: True) ping_interval: interval for ping-pong with Slack servers (seconds) trace_enabled: True if more verbose logs to see what's happening under the hood """ self.app_token = app_token self.logger = logger or logging.getLogger(__name__) self.web_client = web_client or AsyncWebClient() self.closed = False self.connect_operation_lock = Lock() self.default_auto_reconnect_enabled = auto_reconnect_enabled self.auto_reconnect_enabled = self.default_auto_reconnect_enabled self.ping_interval = ping_interval self.trace_enabled = trace_enabled self.wss_uri = None self.message_queue = Queue() self.message_listeners = [] self.socket_mode_request_listeners = [] self.current_session = None self.current_session_monitor = None self.message_receiver = None self.message_processor = asyncio.ensure_future(self.process_messages()) async def monitor_current_session(self) -> None: # In the asyncio runtime, accessing a shared object (self.current_session here) from # multiple tasks can cause race conditions and errors. # To avoid such, we access only the session that is active when this loop starts. session: ClientConnection = self.current_session # type: ignore[assignment] session_id: str = await self.session_id() if self.logger.level <= logging.DEBUG: self.logger.debug(f"A new monitor_current_session() execution loop for {session_id} started") try: while not self.closed: if session != self.current_session: if self.logger.level <= logging.DEBUG: self.logger.debug(f"The monitor_current_session task for {session_id} is now cancelled") break await asyncio.sleep(self.ping_interval) try: if self.auto_reconnect_enabled and _session_closed(session=session): self.logger.info(f"The session ({session_id}) seems to be already closed. Reconnecting...") await self.connect_to_new_endpoint() except Exception as e: self.logger.error( "Failed to check the current session or reconnect to the server " f"(error: {type(e).__name__}, message: {e}, session: {session_id})" ) except asyncio.CancelledError: if self.logger.level <= logging.DEBUG: self.logger.debug(f"The monitor_current_session task for {session_id} is now cancelled") raise async def receive_messages(self) -> None: # In the asyncio runtime, accessing a shared object (self.current_session here) from # multiple tasks can cause race conditions and errors. # To avoid such, we access only the session that is active when this loop starts. session: ClientConnection = self.current_session # type: ignore[assignment] session_id: str = await self.session_id() consecutive_error_count = 0 if self.logger.level <= logging.DEBUG: self.logger.debug(f"A new receive_messages() execution loop with {session_id} started") try: while not self.closed: if session != self.current_session: if self.logger.level <= logging.DEBUG: self.logger.debug(f"The running receive_messages task for {session_id} is now cancelled") break try: message = await session.recv() if message is not None: if isinstance(message, bytes): message = message.decode("utf-8") if self.logger.level <= logging.DEBUG: self.logger.debug( f"Received message: {debug_redacted_message_string(message)}, session: {session_id}" ) await self.enqueue_message(message) consecutive_error_count = 0 except Exception as e: consecutive_error_count += 1 self.logger.error( f"Failed to receive or enqueue a message: {type(e).__name__}, error: {e}, session: {session_id}" ) if isinstance(e, websockets.ConnectionClosedError): await asyncio.sleep(self.ping_interval) else: await asyncio.sleep(consecutive_error_count) except asyncio.CancelledError: if self.logger.level <= logging.DEBUG: self.logger.debug(f"The running receive_messages task for {session_id} is now cancelled") raise async def is_connected(self) -> bool: return not self.closed and not _session_closed(self.current_session) async def session_id(self) -> str: return self.build_session_id(self.current_session) # type: ignore[arg-type] async def connect(self): if self.wss_uri is None: self.wss_uri = await self.issue_new_wss_url() old_session: Optional[ClientConnection] = None if self.current_session is None else self.current_session # NOTE: websockets does not support proxy settings self.current_session = await websockets.connect( uri=self.wss_uri, ping_interval=self.ping_interval, ) session_id = await self.session_id() self.auto_reconnect_enabled = self.default_auto_reconnect_enabled self.logger.info(f"A new session ({session_id}) has been established") if self.current_session_monitor is not None: self.current_session_monitor.cancel() self.current_session_monitor = asyncio.ensure_future(self.monitor_current_session()) if self.logger.level <= logging.DEBUG: self.logger.debug(f"A new monitor_current_session() executor has been recreated for {session_id}") if self.message_receiver is not None: self.message_receiver.cancel() self.message_receiver = asyncio.ensure_future(self.receive_messages()) if self.logger.level <= logging.DEBUG: self.logger.debug(f"A new receive_messages() executor has been recreated for {session_id}") if old_session is not None: await old_session.close() old_session_id = self.build_session_id(old_session) self.logger.info(f"The old session ({old_session_id}) has been abandoned") async def disconnect(self): if self.current_session is not None: await self.current_session.close() async def send_message(self, message: str): session = self.current_session session_id = self.build_session_id(session) # type: ignore[arg-type] if self.logger.level <= logging.DEBUG: self.logger.debug(f"Sending a message: {message}, session: {session_id}") try: await session.send(message) # type: ignore[union-attr] except WebSocketException as e: # We rarely get this exception while replacing the underlying WebSocket connections. # We can do one more try here as the self.current_session should be ready now. if self.logger.level <= logging.DEBUG: self.logger.debug( f"Failed to send a message (error: {e}, message: {message}, session: {session_id})" " as the underlying connection was replaced. Retrying the same request only one time..." ) # Although acquiring self.connect_operation_lock also for the first method call is the safest way, # we avoid synchronizing a lot for better performance. That's why we are doing a retry here. try: if await self.is_connected(): await self.current_session.send(message) # type: ignore[union-attr] else: self.logger.warning(f"The current session ({session_id}) is no longer active. Failed to send a message") raise e finally: if self.connect_operation_lock.locked() is True: self.connect_operation_lock.release() async def close(self): self.closed = True self.auto_reconnect_enabled = False await self.disconnect() self.message_processor.cancel() if self.current_session_monitor is not None: self.current_session_monitor.cancel() if self.message_receiver is not None: self.message_receiver.cancel() @classmethod def build_session_id(cls, session: ClientConnection) -> str: if session is None: return "" return "s_" + str(hash(session)) ``` Socket Mode client ## Args **`app_token`** App-level token **`logger`** Custom logger **`web_client`** Web API client **`auto_reconnect_enabled`** True if automatic reconnection is enabled (default: True) **`ping_interval`** interval for ping-pong with Slack servers (seconds) **`trace_enabled`** True if more verbose logs to see what's happening under the hood ### Ancestors * [AsyncBaseSocketModeClient](../async_client.html#slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient "slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient") ### Class variables `var current_session : websockets.asyncio.client.ClientConnection | None` The type of the None singleton. `var current_session_monitor : _asyncio.Future | None` The type of the None singleton. `var default_auto_reconnect_enabled : bool` The type of the None singleton. `var message_processor : _asyncio.Future` The type of the None singleton. `var message_receiver : _asyncio.Future | None` The type of the None singleton. `var ping_interval : float` The type of the None singleton. ### Static methods `def build_session_id(session: websockets.asyncio.client.ClientConnection) ‑> str` ### Methods `async def close(self)` Expand source code ``` async def close(self): self.closed = True self.auto_reconnect_enabled = False await self.disconnect() self.message_processor.cancel() if self.current_session_monitor is not None: self.current_session_monitor.cancel() if self.message_receiver is not None: self.message_receiver.cancel() ``` `async def connect(self)` Expand source code ``` async def connect(self): if self.wss_uri is None: self.wss_uri = await self.issue_new_wss_url() old_session: Optional[ClientConnection] = None if self.current_session is None else self.current_session # NOTE: websockets does not support proxy settings self.current_session = await websockets.connect( uri=self.wss_uri, ping_interval=self.ping_interval, ) session_id = await self.session_id() self.auto_reconnect_enabled = self.default_auto_reconnect_enabled self.logger.info(f"A new session ({session_id}) has been established") if self.current_session_monitor is not None: self.current_session_monitor.cancel() self.current_session_monitor = asyncio.ensure_future(self.monitor_current_session()) if self.logger.level <= logging.DEBUG: self.logger.debug(f"A new monitor_current_session() executor has been recreated for {session_id}") if self.message_receiver is not None: self.message_receiver.cancel() self.message_receiver = asyncio.ensure_future(self.receive_messages()) if self.logger.level <= logging.DEBUG: self.logger.debug(f"A new receive_messages() executor has been recreated for {session_id}") if old_session is not None: await old_session.close() old_session_id = self.build_session_id(old_session) self.logger.info(f"The old session ({old_session_id}) has been abandoned") ``` `async def disconnect(self)` Expand source code ``` async def disconnect(self): if self.current_session is not None: await self.current_session.close() ``` `async def is_connected(self) ‑> bool` Expand source code ``` async def is_connected(self) -> bool: return not self.closed and not _session_closed(self.current_session) ``` `async def monitor_current_session(self) ‑> None` Expand source code ``` async def monitor_current_session(self) -> None: # In the asyncio runtime, accessing a shared object (self.current_session here) from # multiple tasks can cause race conditions and errors. # To avoid such, we access only the session that is active when this loop starts. session: ClientConnection = self.current_session # type: ignore[assignment] session_id: str = await self.session_id() if self.logger.level <= logging.DEBUG: self.logger.debug(f"A new monitor_current_session() execution loop for {session_id} started") try: while not self.closed: if session != self.current_session: if self.logger.level <= logging.DEBUG: self.logger.debug(f"The monitor_current_session task for {session_id} is now cancelled") break await asyncio.sleep(self.ping_interval) try: if self.auto_reconnect_enabled and _session_closed(session=session): self.logger.info(f"The session ({session_id}) seems to be already closed. Reconnecting...") await self.connect_to_new_endpoint() except Exception as e: self.logger.error( "Failed to check the current session or reconnect to the server " f"(error: {type(e).__name__}, message: {e}, session: {session_id})" ) except asyncio.CancelledError: if self.logger.level <= logging.DEBUG: self.logger.debug(f"The monitor_current_session task for {session_id} is now cancelled") raise ``` `async def receive_messages(self) ‑> None` Expand source code ``` async def receive_messages(self) -> None: # In the asyncio runtime, accessing a shared object (self.current_session here) from # multiple tasks can cause race conditions and errors. # To avoid such, we access only the session that is active when this loop starts. session: ClientConnection = self.current_session # type: ignore[assignment] session_id: str = await self.session_id() consecutive_error_count = 0 if self.logger.level <= logging.DEBUG: self.logger.debug(f"A new receive_messages() execution loop with {session_id} started") try: while not self.closed: if session != self.current_session: if self.logger.level <= logging.DEBUG: self.logger.debug(f"The running receive_messages task for {session_id} is now cancelled") break try: message = await session.recv() if message is not None: if isinstance(message, bytes): message = message.decode("utf-8") if self.logger.level <= logging.DEBUG: self.logger.debug( f"Received message: {debug_redacted_message_string(message)}, session: {session_id}" ) await self.enqueue_message(message) consecutive_error_count = 0 except Exception as e: consecutive_error_count += 1 self.logger.error( f"Failed to receive or enqueue a message: {type(e).__name__}, error: {e}, session: {session_id}" ) if isinstance(e, websockets.ConnectionClosedError): await asyncio.sleep(self.ping_interval) else: await asyncio.sleep(consecutive_error_count) except asyncio.CancelledError: if self.logger.level <= logging.DEBUG: self.logger.debug(f"The running receive_messages task for {session_id} is now cancelled") raise ``` `async def send_message(self, message: str)` Expand source code ``` async def send_message(self, message: str): session = self.current_session session_id = self.build_session_id(session) # type: ignore[arg-type] if self.logger.level <= logging.DEBUG: self.logger.debug(f"Sending a message: {message}, session: {session_id}") try: await session.send(message) # type: ignore[union-attr] except WebSocketException as e: # We rarely get this exception while replacing the underlying WebSocket connections. # We can do one more try here as the self.current_session should be ready now. if self.logger.level <= logging.DEBUG: self.logger.debug( f"Failed to send a message (error: {e}, message: {message}, session: {session_id})" " as the underlying connection was replaced. Retrying the same request only one time..." ) # Although acquiring self.connect_operation_lock also for the first method call is the safest way, # we avoid synchronizing a lot for better performance. That's why we are doing a retry here. try: if await self.is_connected(): await self.current_session.send(message) # type: ignore[union-attr] else: self.logger.warning(f"The current session ({session_id}) is no longer active. Failed to send a message") raise e finally: if self.connect_operation_lock.locked() is True: self.connect_operation_lock.release() ``` `async def session_id(self) ‑> str` Expand source code ``` async def session_id(self) -> str: return self.build_session_id(self.current_session) # type: ignore[arg-type] ``` ### Inherited members * `**[AsyncBaseSocketModeClient](../async_client.html#slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient "slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient")**`: * `[app_token](../async_client.html#slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.app_token "slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.app_token")` * `[auto_reconnect_enabled](../async_client.html#slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.auto_reconnect_enabled "slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.auto_reconnect_enabled")` * `[closed](../async_client.html#slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.closed "slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.closed")` * `[connect_operation_lock](../async_client.html#slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.connect_operation_lock "slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.connect_operation_lock")` * `[logger](../async_client.html#slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.logger "slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.logger")` * `[message_listeners](../async_client.html#slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.message_listeners "slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.message_listeners")` * `[message_queue](../async_client.html#slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.message_queue "slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.message_queue")` * `[socket_mode_request_listeners](../async_client.html#slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.socket_mode_request_listeners "slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.socket_mode_request_listeners")` * `[trace_enabled](../async_client.html#slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.trace_enabled "slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.trace_enabled")` * `[web_client](../async_client.html#slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.web_client "slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.web_client")` * `[wss_uri](../async_client.html#slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.wss_uri "slack_sdk.socket_mode.async_client.AsyncBaseSocketModeClient.wss_uri")` --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/web # Module slack_sdk.web The Slack Web API allows you to build applications that interact with Slack in more complex ways than the integrations we provide out of the box. ## Sub-modules `[slack_sdk.web.async_base_client](async_base_client.html "slack_sdk.web.async_base_client")` `[slack_sdk.web.async_chat_stream](async_chat_stream.html "slack_sdk.web.async_chat_stream")` `[slack_sdk.web.async_client](async_client.html "slack_sdk.web.async_client")` A Python module for interacting with Slack's Web API. `[slack_sdk.web.async_internal_utils](async_internal_utils.html "slack_sdk.web.async_internal_utils")` `[slack_sdk.web.async_slack_response](async_slack_response.html "slack_sdk.web.async_slack_response")` A Python module for interacting and consuming responses from Slack. `[slack_sdk.web.base_client](base_client.html "slack_sdk.web.base_client")` A Python module for interacting with Slack's Web API. `[slack_sdk.web.chat_stream](chat_stream.html "slack_sdk.web.chat_stream")` `[slack_sdk.web.client](client.html "slack_sdk.web.client")` A Python module for interacting with Slack's Web API. `[slack_sdk.web.deprecation](deprecation.html "slack_sdk.web.deprecation")` `[slack_sdk.web.file_upload_v2_result](file_upload_v2_result.html "slack_sdk.web.file_upload_v2_result")` `[slack_sdk.web.internal_utils](internal_utils.html "slack_sdk.web.internal_utils")` `[slack_sdk.web.legacy_base_client](legacy_base_client.html "slack_sdk.web.legacy_base_client")` A Python module for interacting with Slack's Web API. `[slack_sdk.web.legacy_client](legacy_client.html "slack_sdk.web.legacy_client")` `[slack_sdk.web.legacy_slack_response](legacy_slack_response.html "slack_sdk.web.legacy_slack_response")` A Python module for interacting and consuming responses from Slack. `[slack_sdk.web.slack_response](slack_response.html "slack_sdk.web.slack_response")` A Python module for interacting and consuming responses from Slack. ## Classes `class SlackResponse (*, client, http_verb: str, api_url: str, req_args: dict, data: dict | bytes, headers: dict, status_code: int)` Expand source code ``` class SlackResponse: """An iterable container of response data. Attributes: data (dict): The json-encoded content of the response. Along with the headers and status code information. Methods: validate: Check if the response from Slack was successful. get: Retrieves any key from the response data. next: Retrieves the next portion of results, if 'next_cursor' is present. Example: ```python import os import slack client = slack.WebClient(token=os.environ['SLACK_API_TOKEN']) response1 = client.auth_revoke(test='true') assert not response1['revoked'] response2 = client.auth_test() assert response2.get('ok', False) users = [] for page in client.users_list(limit=2): users = users + page['members'] ``` Note: Some responses return collections of information like channel and user lists. If they do it's likely that you'll only receive a portion of results. This object allows you to iterate over the response which makes subsequent API requests until your code hits 'break' or there are no more results to be found. Any attributes or methods prefixed with _underscores are intended to be "private" internal use only. They may be changed or removed at anytime. """ def __init__( self, *, client, http_verb: str, api_url: str, req_args: dict, data: Union[dict, bytes], # data can be binary data headers: dict, status_code: int, ): self.http_verb = http_verb self.api_url = api_url self.req_args = req_args self.data = data self.headers = headers self.status_code = status_code self._initial_data = data self._iteration = None # for __iter__ & __next__ self._client = client self._logger = logging.getLogger(__name__) def __str__(self): """Return the Response data if object is converted to a string.""" if isinstance(self.data, bytes): raise ValueError("As the response.data is binary data, this operation is unsupported") return f"{self.data}" def __contains__(self, key: str) -> bool: return self.get(key) is not None def __getitem__(self, key): """Retrieves any key from the data store. Note: This is implemented so users can reference the SlackResponse object like a dictionary. e.g. response["ok"] Returns: The value from data or None. """ if isinstance(self.data, bytes): raise ValueError("As the response.data is binary data, this operation is unsupported") if self.data is None: raise ValueError("As the response.data is empty, this operation is unsupported") return self.data.get(key, None) def __iter__(self): """Enables the ability to iterate over the response. It's required for the iterator protocol. Note: This enables Slack cursor-based pagination. Returns: (SlackResponse) self """ self._iteration = 0 self.data = self._initial_data return self def __next__(self): """Retrieves the next portion of results, if 'next_cursor' is present. Note: Some responses return collections of information like channel and user lists. If they do it's likely that you'll only receive a portion of results. This method allows you to iterate over the response until your code hits 'break' or there are no more results to be found. Returns: (SlackResponse) self With the new response data now attached to this object. Raises: SlackApiError: If the request to the Slack API failed. StopIteration: If 'next_cursor' is not present or empty. """ if isinstance(self.data, bytes): raise ValueError("As the response.data is binary data, this operation is unsupported") self._iteration += 1 if self._iteration == 1: return self if _next_cursor_is_present(self.data): params = self.req_args.get("params", {}) if params is None: params = {} next_cursor = self.data.get("response_metadata", {}).get("next_cursor") or self.data.get("next_cursor") params.update({"cursor": next_cursor}) self.req_args.update({"params": params}) # This method sends a request in a synchronous way response = self._client._request_for_pagination(api_url=self.api_url, req_args=self.req_args) self.data = response["data"] self.headers = response["headers"] self.status_code = response["status_code"] return self.validate() else: raise StopIteration @overload def get(self, key: str, default: None = None) -> Optional[Any]: ... @overload def get(self, key: str, default: T) -> T: ... def get(self, key, default=None): """Retrieves any key from the response data. Note: This is implemented so users can reference the SlackResponse object like a dictionary. e.g. response.get("ok", False) Returns: The value from data or the specified default. """ if isinstance(self.data, bytes): raise ValueError("As the response.data is binary data, this operation is unsupported") if self.data is None: return None return self.data.get(key, default) def validate(self): """Check if the response from Slack was successful. Returns: (SlackResponse) This method returns it's own object. e.g. 'self' Raises: SlackApiError: The request to the Slack API failed. """ if self.status_code == 200 and self.data and (isinstance(self.data, bytes) or self.data.get("ok", False)): return self msg = f"The request to the Slack API failed. (url: {self.api_url})" raise e.SlackApiError(message=msg, response=self) ``` An iterable container of response data. ## Attributes **`data`** : `dict` The json-encoded content of the response. Along with the headers and status code information. ## Methods validate: Check if the response from Slack was successful. get: Retrieves any key from the response data. next: Retrieves the next portion of results, if 'next\_cursor' is present. Example: ```python import os import slack client = slack.WebClient(token=os.environ['SLACK_API_TOKEN']) response1 = client.auth_revoke(test='true') assert not response1['revoked'] response2 = client.auth_test() assert response2.get('ok', False) users = [] for page in client.users_list(limit=2): users = users + page['members'] ``` ## Note Some responses return collections of information like channel and user lists. If they do it's likely that you'll only receive a portion of results. This object allows you to iterate over the response which makes subsequent API requests until your code hits 'break' or there are no more results to be found. Any attributes or methods prefixed with \_underscores are intended to be "private" internal use only. They may be changed or removed at anytime. ### Methods `def get(self, key, default=None)` Expand source code ``` def get(self, key, default=None): """Retrieves any key from the response data. Note: This is implemented so users can reference the SlackResponse object like a dictionary. e.g. response.get("ok", False) Returns: The value from data or the specified default. """ if isinstance(self.data, bytes): raise ValueError("As the response.data is binary data, this operation is unsupported") if self.data is None: return None return self.data.get(key, default) ``` Retrieves any key from the response data. ## Note This is implemented so users can reference the SlackResponse object like a dictionary. e.g. response.get("ok", False) ## Returns The value from data or the specified default. `def validate(self)` Expand source code ``` def validate(self): """Check if the response from Slack was successful. Returns: (SlackResponse) This method returns it's own object. e.g. 'self' Raises: SlackApiError: The request to the Slack API failed. """ if self.status_code == 200 and self.data and (isinstance(self.data, bytes) or self.data.get("ok", False)): return self msg = f"The request to the Slack API failed. (url: {self.api_url})" raise e.SlackApiError(message=msg, response=self) ``` Check if the response from Slack was successful. ## Returns (SlackResponse) This method returns it's own object. e.g. 'self' ## Raises `SlackApiError` The request to the Slack API failed. `class WebClient (token: str | None = None, base_url: str = 'https://slack.com/api/', timeout: int = 30, ssl: ssl.SSLContext | None = None, proxy: str | None = None, headers: dict | None = None, user_agent_prefix: str | None = None, user_agent_suffix: str | None = None, team_id: str | None = None, logger: logging.Logger | None = None, retry_handlers: List[[RetryHandler](../http_retry/handler.html#slack_sdk.http_retry.handler.RetryHandler "slack_sdk.http_retry.handler.RetryHandler")] | None = None)` Expand source code ``` class WebClient(BaseClient): """A WebClient allows apps to communicate with the Slack Platform's Web API. https://docs.slack.dev/reference/methods The Slack Web API is an interface for querying information from and enacting change in a Slack workspace. This client handles constructing and sending HTTP requests to Slack as well as parsing any responses received into a `SlackResponse`. Attributes: token (str): A string specifying an `xoxp-*` or `xoxb-*` token. base_url (str): A string representing the Slack API base URL. Default is `'https://slack.com/api/'` timeout (int): The maximum number of seconds the client will wait to connect and receive a response from Slack. Default is 30 seconds. ssl (SSLContext): An [`ssl.SSLContext`][1] instance, helpful for specifying your own custom certificate chain. proxy (str): String representing a fully-qualified URL to a proxy through which to route all requests to the Slack API. Even if this parameter is not specified, if any of the following environment variables are present, they will be loaded into this parameter: `HTTPS_PROXY`, `https_proxy`, `HTTP_PROXY` or `http_proxy`. headers (dict): Additional request headers to attach to all requests. Methods: `api_call`: Constructs a request and executes the API call to Slack. Example of recommended usage: ```python import os from slack_sdk import WebClient client = WebClient(token=os.environ['SLACK_API_TOKEN']) response = client.chat_postMessage( channel='#random', text="Hello world!") assert response["ok"] assert response["message"]["text"] == "Hello world!" ``` Example manually creating an API request: ```python import os from slack_sdk import WebClient client = WebClient(token=os.environ['SLACK_API_TOKEN']) response = client.api_call( api_method='chat.postMessage', json={'channel': '#random','text': "Hello world!"} ) assert response["ok"] assert response["message"]["text"] == "Hello world!" ``` Note: Any attributes or methods prefixed with _underscores are intended to be "private" internal use only. They may be changed or removed at anytime. [1]: https://docs.python.org/3/library/ssl.html#ssl.SSLContext """ def admin_analytics_getFile( self, *, type: str, date: Optional[str] = None, metadata_only: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Retrieve analytics data for a given date, presented as a compressed JSON file https://docs.slack.dev/reference/methods/admin.analytics.getFile """ kwargs.update({"type": type}) if date is not None: kwargs.update({"date": date}) if metadata_only is not None: kwargs.update({"metadata_only": metadata_only}) return self.api_call("admin.analytics.getFile", params=kwargs) def admin_apps_approve( self, *, app_id: Optional[str] = None, request_id: Optional[str] = None, enterprise_id: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Approve an app for installation on a workspace. Either app_id or request_id is required. These IDs can be obtained either directly via the app_requested event, or by the admin.apps.requests.list method. https://docs.slack.dev/reference/methods/admin.apps.approve """ if app_id: kwargs.update({"app_id": app_id}) elif request_id: kwargs.update({"request_id": request_id}) else: raise e.SlackRequestError("The app_id or request_id argument must be specified.") kwargs.update( { "enterprise_id": enterprise_id, "team_id": team_id, } ) return self.api_call("admin.apps.approve", params=kwargs) def admin_apps_approved_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, enterprise_id: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List approved apps for an org or workspace. https://docs.slack.dev/reference/methods/admin.apps.approved.list """ kwargs.update( { "cursor": cursor, "limit": limit, "enterprise_id": enterprise_id, "team_id": team_id, } ) return self.api_call("admin.apps.approved.list", http_verb="GET", params=kwargs) def admin_apps_clearResolution( self, *, app_id: str, enterprise_id: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Clear an app resolution https://docs.slack.dev/reference/methods/admin.apps.clearResolution """ kwargs.update( { "app_id": app_id, "enterprise_id": enterprise_id, "team_id": team_id, } ) return self.api_call("admin.apps.clearResolution", http_verb="POST", params=kwargs) def admin_apps_requests_cancel( self, *, request_id: str, enterprise_id: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List app requests for a team/workspace. https://docs.slack.dev/reference/methods/admin.apps.requests.cancel """ kwargs.update( { "request_id": request_id, "enterprise_id": enterprise_id, "team_id": team_id, } ) return self.api_call("admin.apps.requests.cancel", http_verb="POST", params=kwargs) def admin_apps_requests_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List app requests for a team/workspace. https://docs.slack.dev/reference/methods/admin.apps.requests.list """ kwargs.update( { "cursor": cursor, "limit": limit, "team_id": team_id, } ) return self.api_call("admin.apps.requests.list", http_verb="GET", params=kwargs) def admin_apps_restrict( self, *, app_id: Optional[str] = None, request_id: Optional[str] = None, enterprise_id: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Restrict an app for installation on a workspace. Exactly one of the team_id or enterprise_id arguments is required, not both. Either app_id or request_id is required. These IDs can be obtained either directly via the app_requested event, or by the admin.apps.requests.list method. https://docs.slack.dev/reference/methods/admin.apps.restrict """ if app_id: kwargs.update({"app_id": app_id}) elif request_id: kwargs.update({"request_id": request_id}) else: raise e.SlackRequestError("The app_id or request_id argument must be specified.") kwargs.update( { "enterprise_id": enterprise_id, "team_id": team_id, } ) return self.api_call("admin.apps.restrict", params=kwargs) def admin_apps_restricted_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, enterprise_id: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List restricted apps for an org or workspace. https://docs.slack.dev/reference/methods/admin.apps.restricted.list """ kwargs.update( { "cursor": cursor, "limit": limit, "enterprise_id": enterprise_id, "team_id": team_id, } ) return self.api_call("admin.apps.restricted.list", http_verb="GET", params=kwargs) def admin_apps_uninstall( self, *, app_id: str, enterprise_id: Optional[str] = None, team_ids: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """Uninstall an app from one or many workspaces, or an entire enterprise organization. With an org-level token, enterprise_id or team_ids is required. https://docs.slack.dev/reference/methods/admin.apps.uninstall """ kwargs.update({"app_id": app_id}) if enterprise_id is not None: kwargs.update({"enterprise_id": enterprise_id}) if team_ids is not None: if isinstance(team_ids, (list, tuple)): kwargs.update({"team_ids": ",".join(team_ids)}) else: kwargs.update({"team_ids": team_ids}) return self.api_call("admin.apps.uninstall", http_verb="POST", params=kwargs) def admin_apps_activities_list( self, *, app_id: Optional[str] = None, component_id: Optional[str] = None, component_type: Optional[str] = None, log_event_type: Optional[str] = None, max_date_created: Optional[int] = None, min_date_created: Optional[int] = None, min_log_level: Optional[str] = None, sort_direction: Optional[str] = None, source: Optional[str] = None, team_id: Optional[str] = None, trace_id: Optional[str] = None, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Get logs for a specified team/org https://docs.slack.dev/reference/methods/admin.apps.activities.list """ kwargs.update( { "app_id": app_id, "component_id": component_id, "component_type": component_type, "log_event_type": log_event_type, "max_date_created": max_date_created, "min_date_created": min_date_created, "min_log_level": min_log_level, "sort_direction": sort_direction, "source": source, "team_id": team_id, "trace_id": trace_id, "cursor": cursor, "limit": limit, } ) return self.api_call("admin.apps.activities.list", params=kwargs) def admin_apps_config_lookup( self, *, app_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Look up the app config for connectors by their IDs https://docs.slack.dev/reference/methods/admin.apps.config.lookup """ if isinstance(app_ids, (list, tuple)): kwargs.update({"app_ids": ",".join(app_ids)}) else: kwargs.update({"app_ids": app_ids}) return self.api_call("admin.apps.config.lookup", params=kwargs) def admin_apps_config_set( self, *, app_id: str, domain_restrictions: Optional[Dict[str, Any]] = None, workflow_auth_strategy: Optional[str] = None, **kwargs, ) -> SlackResponse: """Set the app config for a connector https://docs.slack.dev/reference/methods/admin.apps.config.set """ kwargs.update( { "app_id": app_id, "workflow_auth_strategy": workflow_auth_strategy, } ) if domain_restrictions is not None: kwargs.update({"domain_restrictions": json.dumps(domain_restrictions)}) return self.api_call("admin.apps.config.set", params=kwargs) def admin_auth_policy_getEntities( self, *, policy_name: str, cursor: Optional[str] = None, entity_type: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Fetch all the entities assigned to a particular authentication policy by name. https://docs.slack.dev/reference/methods/admin.auth.policy.getEntities """ kwargs.update({"policy_name": policy_name}) if cursor is not None: kwargs.update({"cursor": cursor}) if entity_type is not None: kwargs.update({"entity_type": entity_type}) if limit is not None: kwargs.update({"limit": limit}) return self.api_call("admin.auth.policy.getEntities", http_verb="POST", params=kwargs) def admin_auth_policy_assignEntities( self, *, entity_ids: Union[str, Sequence[str]], policy_name: str, entity_type: str, **kwargs, ) -> SlackResponse: """Assign entities to a particular authentication policy. https://docs.slack.dev/reference/methods/admin.auth.policy.assignEntities """ if isinstance(entity_ids, (list, tuple)): kwargs.update({"entity_ids": ",".join(entity_ids)}) else: kwargs.update({"entity_ids": entity_ids}) kwargs.update({"policy_name": policy_name}) kwargs.update({"entity_type": entity_type}) return self.api_call("admin.auth.policy.assignEntities", http_verb="POST", params=kwargs) def admin_auth_policy_removeEntities( self, *, entity_ids: Union[str, Sequence[str]], policy_name: str, entity_type: str, **kwargs, ) -> SlackResponse: """Remove specified entities from a specified authentication policy. https://docs.slack.dev/reference/methods/admin.auth.policy.removeEntities """ if isinstance(entity_ids, (list, tuple)): kwargs.update({"entity_ids": ",".join(entity_ids)}) else: kwargs.update({"entity_ids": entity_ids}) kwargs.update({"policy_name": policy_name}) kwargs.update({"entity_type": entity_type}) return self.api_call("admin.auth.policy.removeEntities", http_verb="POST", params=kwargs) def admin_conversations_createForObjects( self, *, object_id: str, salesforce_org_id: str, invite_object_team: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Create a Salesforce channel for the corresponding object provided. https://docs.slack.dev/reference/methods/admin.conversations.createForObjects """ kwargs.update( {"object_id": object_id, "salesforce_org_id": salesforce_org_id, "invite_object_team": invite_object_team} ) return self.api_call("admin.conversations.createForObjects", params=kwargs) def admin_conversations_linkObjects( self, *, channel: str, record_id: str, salesforce_org_id: str, **kwargs, ) -> SlackResponse: """Link a Salesforce record to a channel. https://docs.slack.dev/reference/methods/admin.conversations.linkObjects """ kwargs.update( { "channel": channel, "record_id": record_id, "salesforce_org_id": salesforce_org_id, } ) return self.api_call("admin.conversations.linkObjects", params=kwargs) def admin_conversations_unlinkObjects( self, *, channel: str, new_name: str, **kwargs, ) -> SlackResponse: """Unlink a Salesforce record from a channel. https://docs.slack.dev/reference/methods/admin.conversations.unlinkObjects """ kwargs.update( { "channel": channel, "new_name": new_name, } ) return self.api_call("admin.conversations.unlinkObjects", params=kwargs) def admin_barriers_create( self, *, barriered_from_usergroup_ids: Union[str, Sequence[str]], primary_usergroup_id: str, restricted_subjects: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Create an Information Barrier https://docs.slack.dev/reference/methods/admin.barriers.create """ kwargs.update({"primary_usergroup_id": primary_usergroup_id}) if isinstance(barriered_from_usergroup_ids, (list, tuple)): kwargs.update({"barriered_from_usergroup_ids": ",".join(barriered_from_usergroup_ids)}) else: kwargs.update({"barriered_from_usergroup_ids": barriered_from_usergroup_ids}) if isinstance(restricted_subjects, (list, tuple)): kwargs.update({"restricted_subjects": ",".join(restricted_subjects)}) else: kwargs.update({"restricted_subjects": restricted_subjects}) return self.api_call("admin.barriers.create", http_verb="POST", params=kwargs) def admin_barriers_delete( self, *, barrier_id: str, **kwargs, ) -> SlackResponse: """Delete an existing Information Barrier https://docs.slack.dev/reference/methods/admin.barriers.delete """ kwargs.update({"barrier_id": barrier_id}) return self.api_call("admin.barriers.delete", http_verb="POST", params=kwargs) def admin_barriers_update( self, *, barrier_id: str, barriered_from_usergroup_ids: Union[str, Sequence[str]], primary_usergroup_id: str, restricted_subjects: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Update an existing Information Barrier https://docs.slack.dev/reference/methods/admin.barriers.update """ kwargs.update({"barrier_id": barrier_id, "primary_usergroup_id": primary_usergroup_id}) if isinstance(barriered_from_usergroup_ids, (list, tuple)): kwargs.update({"barriered_from_usergroup_ids": ",".join(barriered_from_usergroup_ids)}) else: kwargs.update({"barriered_from_usergroup_ids": barriered_from_usergroup_ids}) if isinstance(restricted_subjects, (list, tuple)): kwargs.update({"restricted_subjects": ",".join(restricted_subjects)}) else: kwargs.update({"restricted_subjects": restricted_subjects}) return self.api_call("admin.barriers.update", http_verb="POST", params=kwargs) def admin_barriers_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Get all Information Barriers for your organization https://docs.slack.dev/reference/methods/admin.barriers.list""" kwargs.update( { "cursor": cursor, "limit": limit, } ) return self.api_call("admin.barriers.list", http_verb="GET", params=kwargs) def admin_conversations_create( self, *, is_private: bool, name: str, description: Optional[str] = None, org_wide: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Create a public or private channel-based conversation. https://docs.slack.dev/reference/methods/admin.conversations.create """ kwargs.update( { "is_private": is_private, "name": name, "description": description, "org_wide": org_wide, "team_id": team_id, } ) return self.api_call("admin.conversations.create", params=kwargs) def admin_conversations_delete( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """Delete a public or private channel. https://docs.slack.dev/reference/methods/admin.conversations.delete """ kwargs.update({"channel_id": channel_id}) return self.api_call("admin.conversations.delete", params=kwargs) def admin_conversations_invite( self, *, channel_id: str, user_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Invite a user to a public or private channel. https://docs.slack.dev/reference/methods/admin.conversations.invite """ kwargs.update({"channel_id": channel_id}) if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) # NOTE: the endpoint is unable to handle Content-Type: application/json as of Sep 3, 2020. return self.api_call("admin.conversations.invite", params=kwargs) def admin_conversations_archive( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """Archive a public or private channel. https://docs.slack.dev/reference/methods/admin.conversations.archive """ kwargs.update({"channel_id": channel_id}) return self.api_call("admin.conversations.archive", params=kwargs) def admin_conversations_unarchive( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """Unarchive a public or private channel. https://docs.slack.dev/reference/methods/admin.conversations.archive """ kwargs.update({"channel_id": channel_id}) return self.api_call("admin.conversations.unarchive", params=kwargs) def admin_conversations_rename( self, *, channel_id: str, name: str, **kwargs, ) -> SlackResponse: """Rename a public or private channel. https://docs.slack.dev/reference/methods/admin.conversations.rename """ kwargs.update({"channel_id": channel_id, "name": name}) return self.api_call("admin.conversations.rename", params=kwargs) def admin_conversations_search( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, query: Optional[str] = None, search_channel_types: Optional[Union[str, Sequence[str]]] = None, sort: Optional[str] = None, sort_dir: Optional[str] = None, team_ids: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """Search for public or private channels in an Enterprise organization. https://docs.slack.dev/reference/methods/admin.conversations.search """ kwargs.update( { "cursor": cursor, "limit": limit, "query": query, "sort": sort, "sort_dir": sort_dir, } ) if isinstance(search_channel_types, (list, tuple)): kwargs.update({"search_channel_types": ",".join(search_channel_types)}) else: kwargs.update({"search_channel_types": search_channel_types}) if isinstance(team_ids, (list, tuple)): kwargs.update({"team_ids": ",".join(team_ids)}) else: kwargs.update({"team_ids": team_ids}) return self.api_call("admin.conversations.search", params=kwargs) def admin_conversations_convertToPrivate( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """Convert a public channel to a private channel. https://docs.slack.dev/reference/methods/admin.conversations.convertToPrivate """ kwargs.update({"channel_id": channel_id}) return self.api_call("admin.conversations.convertToPrivate", params=kwargs) def admin_conversations_convertToPublic( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """Convert a privte channel to a public channel. https://docs.slack.dev/reference/methods/admin.conversations.convertToPublic """ kwargs.update({"channel_id": channel_id}) return self.api_call("admin.conversations.convertToPublic", params=kwargs) def admin_conversations_setConversationPrefs( self, *, channel_id: str, prefs: Union[str, Dict[str, str]], **kwargs, ) -> SlackResponse: """Set the posting permissions for a public or private channel. https://docs.slack.dev/reference/methods/admin.conversations.setConversationPrefs """ kwargs.update({"channel_id": channel_id}) if isinstance(prefs, dict): kwargs.update({"prefs": json.dumps(prefs)}) else: kwargs.update({"prefs": prefs}) return self.api_call("admin.conversations.setConversationPrefs", params=kwargs) def admin_conversations_getConversationPrefs( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """Get conversation preferences for a public or private channel. https://docs.slack.dev/reference/methods/admin.conversations.getConversationPrefs """ kwargs.update({"channel_id": channel_id}) return self.api_call("admin.conversations.getConversationPrefs", params=kwargs) def admin_conversations_disconnectShared( self, *, channel_id: str, leaving_team_ids: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """Disconnect a connected channel from one or more workspaces. https://docs.slack.dev/reference/methods/admin.conversations.disconnectShared """ kwargs.update({"channel_id": channel_id}) if isinstance(leaving_team_ids, (list, tuple)): kwargs.update({"leaving_team_ids": ",".join(leaving_team_ids)}) else: kwargs.update({"leaving_team_ids": leaving_team_ids}) return self.api_call("admin.conversations.disconnectShared", params=kwargs) def admin_conversations_lookup( self, *, last_message_activity_before: int, team_ids: Union[str, Sequence[str]], cursor: Optional[str] = None, limit: Optional[int] = None, max_member_count: Optional[int] = None, **kwargs, ) -> SlackResponse: """Returns channels on the given team using the filters. https://docs.slack.dev/reference/methods/admin.conversations.lookup """ kwargs.update( { "last_message_activity_before": last_message_activity_before, "cursor": cursor, "limit": limit, "max_member_count": max_member_count, } ) if isinstance(team_ids, (list, tuple)): kwargs.update({"team_ids": ",".join(team_ids)}) else: kwargs.update({"team_ids": team_ids}) return self.api_call("admin.conversations.lookup", params=kwargs) def admin_conversations_ekm_listOriginalConnectedChannelInfo( self, *, channel_ids: Optional[Union[str, Sequence[str]]] = None, cursor: Optional[str] = None, limit: Optional[int] = None, team_ids: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """List all disconnected channels—i.e., channels that were once connected to other workspaces and then disconnected—and the corresponding original channel IDs for key revocation with EKM. https://docs.slack.dev/reference/methods/admin.conversations.ekm.listOriginalConnectedChannelInfo """ kwargs.update( { "cursor": cursor, "limit": limit, } ) if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) if isinstance(team_ids, (list, tuple)): kwargs.update({"team_ids": ",".join(team_ids)}) else: kwargs.update({"team_ids": team_ids}) return self.api_call("admin.conversations.ekm.listOriginalConnectedChannelInfo", params=kwargs) def admin_conversations_restrictAccess_addGroup( self, *, channel_id: str, group_id: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Add an allowlist of IDP groups for accessing a channel. https://docs.slack.dev/reference/methods/admin.conversations.restrictAccess.addGroup """ kwargs.update( { "channel_id": channel_id, "group_id": group_id, "team_id": team_id, } ) return self.api_call( "admin.conversations.restrictAccess.addGroup", http_verb="GET", params=kwargs, ) def admin_conversations_restrictAccess_listGroups( self, *, channel_id: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List all IDP Groups linked to a channel. https://docs.slack.dev/reference/methods/admin.conversations.restrictAccess.listGroups """ kwargs.update( { "channel_id": channel_id, "team_id": team_id, } ) return self.api_call( "admin.conversations.restrictAccess.listGroups", http_verb="GET", params=kwargs, ) def admin_conversations_restrictAccess_removeGroup( self, *, channel_id: str, group_id: str, team_id: str, **kwargs, ) -> SlackResponse: """Remove a linked IDP group linked from a private channel. https://docs.slack.dev/reference/methods/admin.conversations.restrictAccess.removeGroup """ kwargs.update( { "channel_id": channel_id, "group_id": group_id, "team_id": team_id, } ) return self.api_call( "admin.conversations.restrictAccess.removeGroup", http_verb="GET", params=kwargs, ) def admin_conversations_setTeams( self, *, channel_id: str, org_channel: Optional[bool] = None, target_team_ids: Optional[Union[str, Sequence[str]]] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Set the workspaces in an Enterprise grid org that connect to a public or private channel. https://docs.slack.dev/reference/methods/admin.conversations.setTeams """ kwargs.update( { "channel_id": channel_id, "org_channel": org_channel, "team_id": team_id, } ) if isinstance(target_team_ids, (list, tuple)): kwargs.update({"target_team_ids": ",".join(target_team_ids)}) else: kwargs.update({"target_team_ids": target_team_ids}) return self.api_call("admin.conversations.setTeams", params=kwargs) def admin_conversations_getTeams( self, *, channel_id: str, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Set the workspaces in an Enterprise grid org that connect to a channel. https://docs.slack.dev/reference/methods/admin.conversations.getTeams """ kwargs.update( { "channel_id": channel_id, "cursor": cursor, "limit": limit, } ) return self.api_call("admin.conversations.getTeams", params=kwargs) def admin_conversations_getCustomRetention( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """Get a channel's retention policy https://docs.slack.dev/reference/methods/admin.conversations.getCustomRetention """ kwargs.update({"channel_id": channel_id}) return self.api_call("admin.conversations.getCustomRetention", params=kwargs) def admin_conversations_removeCustomRetention( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """Remove a channel's retention policy https://docs.slack.dev/reference/methods/admin.conversations.removeCustomRetention """ kwargs.update({"channel_id": channel_id}) return self.api_call("admin.conversations.removeCustomRetention", params=kwargs) def admin_conversations_setCustomRetention( self, *, channel_id: str, duration_days: int, **kwargs, ) -> SlackResponse: """Set a channel's retention policy https://docs.slack.dev/reference/methods/admin.conversations.setCustomRetention """ kwargs.update({"channel_id": channel_id, "duration_days": duration_days}) return self.api_call("admin.conversations.setCustomRetention", params=kwargs) def admin_conversations_bulkArchive( self, *, channel_ids: Union[Sequence[str], str], **kwargs, ) -> SlackResponse: """Archive public or private channels in bulk. https://docs.slack.dev/reference/methods/admin.conversations.bulkArchive """ kwargs.update({"channel_ids": ",".join(channel_ids) if isinstance(channel_ids, (list, tuple)) else channel_ids}) return self.api_call("admin.conversations.bulkArchive", params=kwargs) def admin_conversations_bulkDelete( self, *, channel_ids: Union[Sequence[str], str], **kwargs, ) -> SlackResponse: """Delete public or private channels in bulk. https://slack.com/api/admin.conversations.bulkDelete """ kwargs.update({"channel_ids": ",".join(channel_ids) if isinstance(channel_ids, (list, tuple)) else channel_ids}) return self.api_call("admin.conversations.bulkDelete", params=kwargs) def admin_conversations_bulkMove( self, *, channel_ids: Union[Sequence[str], str], target_team_id: str, **kwargs, ) -> SlackResponse: """Move public or private channels in bulk. https://docs.slack.dev/reference/methods/admin.conversations.bulkMove """ kwargs.update( { "target_team_id": target_team_id, "channel_ids": ",".join(channel_ids) if isinstance(channel_ids, (list, tuple)) else channel_ids, } ) return self.api_call("admin.conversations.bulkMove", params=kwargs) def admin_emoji_add( self, *, name: str, url: str, **kwargs, ) -> SlackResponse: """Add an emoji. https://docs.slack.dev/reference/methods/admin.emoji.add """ kwargs.update({"name": name, "url": url}) return self.api_call("admin.emoji.add", http_verb="GET", params=kwargs) def admin_emoji_addAlias( self, *, alias_for: str, name: str, **kwargs, ) -> SlackResponse: """Add an emoji alias. https://docs.slack.dev/reference/methods/admin.emoji.addAlias """ kwargs.update({"alias_for": alias_for, "name": name}) return self.api_call("admin.emoji.addAlias", http_verb="GET", params=kwargs) def admin_emoji_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """List emoji for an Enterprise Grid organization. https://docs.slack.dev/reference/methods/admin.emoji.list """ kwargs.update({"cursor": cursor, "limit": limit}) return self.api_call("admin.emoji.list", http_verb="GET", params=kwargs) def admin_emoji_remove( self, *, name: str, **kwargs, ) -> SlackResponse: """Remove an emoji across an Enterprise Grid organization. https://docs.slack.dev/reference/methods/admin.emoji.remove """ kwargs.update({"name": name}) return self.api_call("admin.emoji.remove", http_verb="GET", params=kwargs) def admin_emoji_rename( self, *, name: str, new_name: str, **kwargs, ) -> SlackResponse: """Rename an emoji. https://docs.slack.dev/reference/methods/admin.emoji.rename """ kwargs.update({"name": name, "new_name": new_name}) return self.api_call("admin.emoji.rename", http_verb="GET", params=kwargs) def admin_functions_list( self, *, app_ids: Union[str, Sequence[str]], team_id: Optional[str] = None, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Look up functions by a set of apps https://docs.slack.dev/reference/methods/admin.functions.list """ if isinstance(app_ids, (list, tuple)): kwargs.update({"app_ids": ",".join(app_ids)}) else: kwargs.update({"app_ids": app_ids}) kwargs.update( { "team_id": team_id, "cursor": cursor, "limit": limit, } ) return self.api_call("admin.functions.list", params=kwargs) def admin_functions_permissions_lookup( self, *, function_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Lookup the visibility of multiple Slack functions and include the users if it is limited to particular named entities. https://docs.slack.dev/reference/methods/admin.functions.permissions.lookup """ if isinstance(function_ids, (list, tuple)): kwargs.update({"function_ids": ",".join(function_ids)}) else: kwargs.update({"function_ids": function_ids}) return self.api_call("admin.functions.permissions.lookup", params=kwargs) def admin_functions_permissions_set( self, *, function_id: str, visibility: str, user_ids: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """Set the visibility of a Slack function and define the users or workspaces if it is set to named_entities https://docs.slack.dev/reference/methods/admin.functions.permissions.set """ kwargs.update( { "function_id": function_id, "visibility": visibility, } ) if user_ids is not None: if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) return self.api_call("admin.functions.permissions.set", params=kwargs) def admin_roles_addAssignments( self, *, role_id: str, entity_ids: Union[str, Sequence[str]], user_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Adds members to the specified role with the specified scopes https://docs.slack.dev/reference/methods/admin.roles.addAssignments """ kwargs.update({"role_id": role_id}) if isinstance(entity_ids, (list, tuple)): kwargs.update({"entity_ids": ",".join(entity_ids)}) else: kwargs.update({"entity_ids": entity_ids}) if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) return self.api_call("admin.roles.addAssignments", params=kwargs) def admin_roles_listAssignments( self, *, role_ids: Optional[Union[str, Sequence[str]]] = None, entity_ids: Optional[Union[str, Sequence[str]]] = None, cursor: Optional[str] = None, limit: Optional[Union[str, int]] = None, sort_dir: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists assignments for all roles across entities. Options to scope results by any combination of roles or entities https://docs.slack.dev/reference/methods/admin.roles.listAssignments """ kwargs.update({"cursor": cursor, "limit": limit, "sort_dir": sort_dir}) if isinstance(entity_ids, (list, tuple)): kwargs.update({"entity_ids": ",".join(entity_ids)}) else: kwargs.update({"entity_ids": entity_ids}) if isinstance(role_ids, (list, tuple)): kwargs.update({"role_ids": ",".join(role_ids)}) else: kwargs.update({"role_ids": role_ids}) return self.api_call("admin.roles.listAssignments", params=kwargs) def admin_roles_removeAssignments( self, *, role_id: str, entity_ids: Union[str, Sequence[str]], user_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Removes a set of users from a role for the given scopes and entities https://docs.slack.dev/reference/methods/admin.roles.removeAssignments """ kwargs.update({"role_id": role_id}) if isinstance(entity_ids, (list, tuple)): kwargs.update({"entity_ids": ",".join(entity_ids)}) else: kwargs.update({"entity_ids": entity_ids}) if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) return self.api_call("admin.roles.removeAssignments", params=kwargs) def admin_users_session_reset( self, *, user_id: str, mobile_only: Optional[bool] = None, web_only: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Wipes all valid sessions on all devices for a given user. https://docs.slack.dev/reference/methods/admin.users.session.reset """ kwargs.update( { "user_id": user_id, "mobile_only": mobile_only, "web_only": web_only, } ) return self.api_call("admin.users.session.reset", params=kwargs) def admin_users_session_resetBulk( self, *, user_ids: Union[str, Sequence[str]], mobile_only: Optional[bool] = None, web_only: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Enqueues an asynchronous job to wipe all valid sessions on all devices for a given list of users https://docs.slack.dev/reference/methods/admin.users.session.resetBulk """ if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) kwargs.update( { "mobile_only": mobile_only, "web_only": web_only, } ) return self.api_call("admin.users.session.resetBulk", params=kwargs) def admin_users_session_invalidate( self, *, session_id: str, team_id: str, **kwargs, ) -> SlackResponse: """Invalidate a single session for a user by session_id. https://docs.slack.dev/reference/methods/admin.users.session.invalidate """ kwargs.update({"session_id": session_id, "team_id": team_id}) return self.api_call("admin.users.session.invalidate", params=kwargs) def admin_users_session_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, team_id: Optional[str] = None, user_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists all active user sessions for an organization https://docs.slack.dev/reference/methods/admin.users.session.list """ kwargs.update( { "cursor": cursor, "limit": limit, "team_id": team_id, "user_id": user_id, } ) return self.api_call("admin.users.session.list", params=kwargs) def admin_teams_settings_setDefaultChannels( self, *, team_id: str, channel_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Set the default channels of a workspace. https://docs.slack.dev/reference/methods/admin.teams.settings.setDefaultChannels """ kwargs.update({"team_id": team_id}) if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) return self.api_call("admin.teams.settings.setDefaultChannels", http_verb="GET", params=kwargs) def admin_users_session_getSettings( self, *, user_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Get user-specific session settings—the session duration and what happens when the client closes—given a list of users. https://docs.slack.dev/reference/methods/admin.users.session.getSettings """ if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) return self.api_call("admin.users.session.getSettings", params=kwargs) def admin_users_session_setSettings( self, *, user_ids: Union[str, Sequence[str]], desktop_app_browser_quit: Optional[bool] = None, duration: Optional[int] = None, **kwargs, ) -> SlackResponse: """Configure the user-level session settings—the session duration and what happens when the client closes—for one or more users. https://docs.slack.dev/reference/methods/admin.users.session.setSettings """ if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) kwargs.update( { "desktop_app_browser_quit": desktop_app_browser_quit, "duration": duration, } ) return self.api_call("admin.users.session.setSettings", params=kwargs) def admin_users_session_clearSettings( self, *, user_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Clear user-specific session settings—the session duration and what happens when the client closes—for a list of users. https://docs.slack.dev/reference/methods/admin.users.session.clearSettings """ if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) return self.api_call("admin.users.session.clearSettings", params=kwargs) def admin_users_unsupportedVersions_export( self, *, date_end_of_support: Optional[Union[str, int]] = None, date_sessions_started: Optional[Union[str, int]] = None, **kwargs, ) -> SlackResponse: """Ask Slackbot to send you an export listing all workspace members using unsupported software, presented as a zipped CSV file. https://docs.slack.dev/reference/methods/admin.users.unsupportedVersions.export """ kwargs.update( { "date_end_of_support": date_end_of_support, "date_sessions_started": date_sessions_started, } ) return self.api_call("admin.users.unsupportedVersions.export", params=kwargs) def admin_inviteRequests_approve( self, *, invite_request_id: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Approve a workspace invite request. https://docs.slack.dev/reference/methods/admin.inviteRequests.approve """ kwargs.update({"invite_request_id": invite_request_id, "team_id": team_id}) return self.api_call("admin.inviteRequests.approve", params=kwargs) def admin_inviteRequests_approved_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List all approved workspace invite requests. https://docs.slack.dev/reference/methods/admin.inviteRequests.approved.list """ kwargs.update( { "cursor": cursor, "limit": limit, "team_id": team_id, } ) return self.api_call("admin.inviteRequests.approved.list", params=kwargs) def admin_inviteRequests_denied_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List all denied workspace invite requests. https://docs.slack.dev/reference/methods/admin.inviteRequests.denied.list """ kwargs.update( { "cursor": cursor, "limit": limit, "team_id": team_id, } ) return self.api_call("admin.inviteRequests.denied.list", params=kwargs) def admin_inviteRequests_deny( self, *, invite_request_id: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Deny a workspace invite request. https://docs.slack.dev/reference/methods/admin.inviteRequests.deny """ kwargs.update({"invite_request_id": invite_request_id, "team_id": team_id}) return self.api_call("admin.inviteRequests.deny", params=kwargs) def admin_inviteRequests_list( self, **kwargs, ) -> SlackResponse: """List all pending workspace invite requests.""" return self.api_call("admin.inviteRequests.list", params=kwargs) def admin_teams_admins_list( self, *, team_id: str, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """List all of the admins on a given workspace. https://docs.slack.dev/reference/methods/admin.inviteRequests.list """ kwargs.update( { "cursor": cursor, "limit": limit, "team_id": team_id, } ) return self.api_call("admin.teams.admins.list", http_verb="GET", params=kwargs) def admin_teams_create( self, *, team_domain: str, team_name: str, team_description: Optional[str] = None, team_discoverability: Optional[str] = None, **kwargs, ) -> SlackResponse: """Create an Enterprise team. https://docs.slack.dev/reference/methods/admin.teams.create """ kwargs.update( { "team_domain": team_domain, "team_name": team_name, "team_description": team_description, "team_discoverability": team_discoverability, } ) return self.api_call("admin.teams.create", params=kwargs) def admin_teams_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """List all teams on an Enterprise organization. https://docs.slack.dev/reference/methods/admin.teams.list """ kwargs.update({"cursor": cursor, "limit": limit}) return self.api_call("admin.teams.list", params=kwargs) def admin_teams_owners_list( self, *, team_id: str, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """List all of the admins on a given workspace. https://docs.slack.dev/reference/methods/admin.teams.owners.list """ kwargs.update({"team_id": team_id, "cursor": cursor, "limit": limit}) return self.api_call("admin.teams.owners.list", http_verb="GET", params=kwargs) def admin_teams_settings_info( self, *, team_id: str, **kwargs, ) -> SlackResponse: """Fetch information about settings in a workspace https://docs.slack.dev/reference/methods/admin.teams.settings.info """ kwargs.update({"team_id": team_id}) return self.api_call("admin.teams.settings.info", params=kwargs) def admin_teams_settings_setDescription( self, *, team_id: str, description: str, **kwargs, ) -> SlackResponse: """Set the description of a given workspace. https://docs.slack.dev/reference/methods/admin.teams.settings.setDescription """ kwargs.update({"team_id": team_id, "description": description}) return self.api_call("admin.teams.settings.setDescription", params=kwargs) def admin_teams_settings_setDiscoverability( self, *, team_id: str, discoverability: str, **kwargs, ) -> SlackResponse: """Sets the icon of a workspace. https://docs.slack.dev/reference/methods/admin.teams.settings.setDiscoverability """ kwargs.update({"team_id": team_id, "discoverability": discoverability}) return self.api_call("admin.teams.settings.setDiscoverability", params=kwargs) def admin_teams_settings_setIcon( self, *, team_id: str, image_url: str, **kwargs, ) -> SlackResponse: """Sets the icon of a workspace. https://docs.slack.dev/reference/methods/admin.teams.settings.setIcon """ kwargs.update({"team_id": team_id, "image_url": image_url}) return self.api_call("admin.teams.settings.setIcon", http_verb="GET", params=kwargs) def admin_teams_settings_setName( self, *, team_id: str, name: str, **kwargs, ) -> SlackResponse: """Sets the icon of a workspace. https://docs.slack.dev/reference/methods/admin.teams.settings.setName """ kwargs.update({"team_id": team_id, "name": name}) return self.api_call("admin.teams.settings.setName", params=kwargs) def admin_usergroups_addChannels( self, *, channel_ids: Union[str, Sequence[str]], usergroup_id: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Add one or more default channels to an IDP group. https://docs.slack.dev/reference/methods/admin.usergroups.addChannels """ kwargs.update({"team_id": team_id, "usergroup_id": usergroup_id}) if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) return self.api_call("admin.usergroups.addChannels", params=kwargs) def admin_usergroups_addTeams( self, *, usergroup_id: str, team_ids: Union[str, Sequence[str]], auto_provision: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Associate one or more default workspaces with an organization-wide IDP group. https://docs.slack.dev/reference/methods/admin.usergroups.addTeams """ kwargs.update({"usergroup_id": usergroup_id, "auto_provision": auto_provision}) if isinstance(team_ids, (list, tuple)): kwargs.update({"team_ids": ",".join(team_ids)}) else: kwargs.update({"team_ids": team_ids}) return self.api_call("admin.usergroups.addTeams", params=kwargs) def admin_usergroups_listChannels( self, *, usergroup_id: str, include_num_members: Optional[bool] = None, team_id: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Add one or more default channels to an IDP group. https://docs.slack.dev/reference/methods/admin.usergroups.listChannels """ kwargs.update( { "usergroup_id": usergroup_id, "include_num_members": include_num_members, "team_id": team_id, } ) return self.api_call("admin.usergroups.listChannels", params=kwargs) def admin_usergroups_removeChannels( self, *, usergroup_id: str, channel_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Add one or more default channels to an IDP group. https://docs.slack.dev/reference/methods/admin.usergroups.removeChannels """ kwargs.update({"usergroup_id": usergroup_id}) if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) return self.api_call("admin.usergroups.removeChannels", params=kwargs) def admin_users_assign( self, *, team_id: str, user_id: str, channel_ids: Optional[Union[str, Sequence[str]]] = None, is_restricted: Optional[bool] = None, is_ultra_restricted: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Add an Enterprise user to a workspace. https://docs.slack.dev/reference/methods/admin.users.assign """ kwargs.update( { "team_id": team_id, "user_id": user_id, "is_restricted": is_restricted, "is_ultra_restricted": is_ultra_restricted, } ) if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) return self.api_call("admin.users.assign", params=kwargs) def admin_users_invite( self, *, team_id: str, email: str, channel_ids: Union[str, Sequence[str]], custom_message: Optional[str] = None, email_password_policy_enabled: Optional[bool] = None, guest_expiration_ts: Optional[Union[str, float]] = None, is_restricted: Optional[bool] = None, is_ultra_restricted: Optional[bool] = None, real_name: Optional[str] = None, resend: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Invite a user to a workspace. https://docs.slack.dev/reference/methods/admin.users.invite """ kwargs.update( { "team_id": team_id, "email": email, "custom_message": custom_message, "email_password_policy_enabled": email_password_policy_enabled, "guest_expiration_ts": str(guest_expiration_ts) if guest_expiration_ts is not None else None, "is_restricted": is_restricted, "is_ultra_restricted": is_ultra_restricted, "real_name": real_name, "resend": resend, } ) if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) return self.api_call("admin.users.invite", params=kwargs) def admin_users_list( self, *, team_id: Optional[str] = None, include_deactivated_user_workspaces: Optional[bool] = None, is_active: Optional[bool] = None, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """List users on a workspace https://docs.slack.dev/reference/methods/admin.users.list """ kwargs.update( { "team_id": team_id, "include_deactivated_user_workspaces": include_deactivated_user_workspaces, "is_active": is_active, "cursor": cursor, "limit": limit, } ) return self.api_call("admin.users.list", params=kwargs) def admin_users_remove( self, *, team_id: str, user_id: str, **kwargs, ) -> SlackResponse: """Remove a user from a workspace. https://docs.slack.dev/reference/methods/admin.users.remove """ kwargs.update({"team_id": team_id, "user_id": user_id}) return self.api_call("admin.users.remove", params=kwargs) def admin_users_setAdmin( self, *, team_id: str, user_id: str, **kwargs, ) -> SlackResponse: """Set an existing guest, regular user, or owner to be an admin user. https://docs.slack.dev/reference/methods/admin.users.setAdmin """ kwargs.update({"team_id": team_id, "user_id": user_id}) return self.api_call("admin.users.setAdmin", params=kwargs) def admin_users_setExpiration( self, *, expiration_ts: int, user_id: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Set an expiration for a guest user. https://docs.slack.dev/reference/methods/admin.users.setExpiration """ kwargs.update({"expiration_ts": expiration_ts, "team_id": team_id, "user_id": user_id}) return self.api_call("admin.users.setExpiration", params=kwargs) def admin_users_setOwner( self, *, team_id: str, user_id: str, **kwargs, ) -> SlackResponse: """Set an existing guest, regular user, or admin user to be a workspace owner. https://docs.slack.dev/reference/methods/admin.users.setOwner """ kwargs.update({"team_id": team_id, "user_id": user_id}) return self.api_call("admin.users.setOwner", params=kwargs) def admin_users_setRegular( self, *, team_id: str, user_id: str, **kwargs, ) -> SlackResponse: """Set an existing guest user, admin user, or owner to be a regular user. https://docs.slack.dev/reference/methods/admin.users.setRegular """ kwargs.update({"team_id": team_id, "user_id": user_id}) return self.api_call("admin.users.setRegular", params=kwargs) def admin_workflows_search( self, *, app_id: Optional[str] = None, collaborator_ids: Optional[Union[str, Sequence[str]]] = None, cursor: Optional[str] = None, limit: Optional[int] = None, no_collaborators: Optional[bool] = None, num_trigger_ids: Optional[int] = None, query: Optional[str] = None, sort: Optional[str] = None, sort_dir: Optional[str] = None, source: Optional[str] = None, **kwargs, ) -> SlackResponse: """Search workflows within the team or enterprise https://docs.slack.dev/reference/methods/admin.workflows.search """ if collaborator_ids is not None: if isinstance(collaborator_ids, (list, tuple)): kwargs.update({"collaborator_ids": ",".join(collaborator_ids)}) else: kwargs.update({"collaborator_ids": collaborator_ids}) kwargs.update( { "app_id": app_id, "cursor": cursor, "limit": limit, "no_collaborators": no_collaborators, "num_trigger_ids": num_trigger_ids, "query": query, "sort": sort, "sort_dir": sort_dir, "source": source, } ) return self.api_call("admin.workflows.search", params=kwargs) def admin_workflows_permissions_lookup( self, *, workflow_ids: Union[str, Sequence[str]], max_workflow_triggers: Optional[int] = None, **kwargs, ) -> SlackResponse: """Look up the permissions for a set of workflows https://docs.slack.dev/reference/methods/admin.workflows.permissions.lookup """ if isinstance(workflow_ids, (list, tuple)): kwargs.update({"workflow_ids": ",".join(workflow_ids)}) else: kwargs.update({"workflow_ids": workflow_ids}) kwargs.update( { "max_workflow_triggers": max_workflow_triggers, } ) return self.api_call("admin.workflows.permissions.lookup", params=kwargs) def admin_workflows_collaborators_add( self, *, collaborator_ids: Union[str, Sequence[str]], workflow_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Add collaborators to workflows within the team or enterprise https://docs.slack.dev/reference/methods/admin.workflows.collaborators.add """ if isinstance(collaborator_ids, (list, tuple)): kwargs.update({"collaborator_ids": ",".join(collaborator_ids)}) else: kwargs.update({"collaborator_ids": collaborator_ids}) if isinstance(workflow_ids, (list, tuple)): kwargs.update({"workflow_ids": ",".join(workflow_ids)}) else: kwargs.update({"workflow_ids": workflow_ids}) return self.api_call("admin.workflows.collaborators.add", params=kwargs) def admin_workflows_collaborators_remove( self, *, collaborator_ids: Union[str, Sequence[str]], workflow_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Remove collaborators from workflows within the team or enterprise https://docs.slack.dev/reference/methods/admin.workflows.collaborators.remove """ if isinstance(collaborator_ids, (list, tuple)): kwargs.update({"collaborator_ids": ",".join(collaborator_ids)}) else: kwargs.update({"collaborator_ids": collaborator_ids}) if isinstance(workflow_ids, (list, tuple)): kwargs.update({"workflow_ids": ",".join(workflow_ids)}) else: kwargs.update({"workflow_ids": workflow_ids}) return self.api_call("admin.workflows.collaborators.remove", params=kwargs) def admin_workflows_unpublish( self, *, workflow_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Unpublish workflows within the team or enterprise https://docs.slack.dev/reference/methods/admin.workflows.unpublish """ if isinstance(workflow_ids, (list, tuple)): kwargs.update({"workflow_ids": ",".join(workflow_ids)}) else: kwargs.update({"workflow_ids": workflow_ids}) return self.api_call("admin.workflows.unpublish", params=kwargs) def api_test( self, *, error: Optional[str] = None, **kwargs, ) -> SlackResponse: """Checks API calling code. https://docs.slack.dev/reference/methods/api.test """ kwargs.update({"error": error}) return self.api_call("api.test", params=kwargs) def apps_connections_open( self, *, app_token: str, **kwargs, ) -> SlackResponse: """Generate a temporary Socket Mode WebSocket URL that your app can connect to in order to receive events and interactive payloads https://docs.slack.dev/reference/methods/apps.connections.open """ kwargs.update({"token": app_token}) return self.api_call("apps.connections.open", http_verb="POST", params=kwargs) def apps_event_authorizations_list( self, *, event_context: str, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Get a list of authorizations for the given event context. Each authorization represents an app installation that the event is visible to. https://docs.slack.dev/reference/methods/apps.event.authorizations.list """ kwargs.update({"event_context": event_context, "cursor": cursor, "limit": limit}) return self.api_call("apps.event.authorizations.list", params=kwargs) def apps_uninstall( self, *, client_id: str, client_secret: str, **kwargs, ) -> SlackResponse: """Uninstalls your app from a workspace. https://docs.slack.dev/reference/methods/apps.uninstall """ kwargs.update({"client_id": client_id, "client_secret": client_secret}) return self.api_call("apps.uninstall", params=kwargs) def apps_manifest_create( self, *, manifest: Union[str, Dict[str, Any]], **kwargs, ) -> SlackResponse: """Create an app from an app manifest https://docs.slack.dev/reference/methods/apps.manifest.create """ if isinstance(manifest, str): kwargs.update({"manifest": manifest}) else: kwargs.update({"manifest": json.dumps(manifest)}) return self.api_call("apps.manifest.create", params=kwargs) def apps_manifest_delete( self, *, app_id: str, **kwargs, ) -> SlackResponse: """Permanently deletes an app created through app manifests https://docs.slack.dev/reference/methods/apps.manifest.delete """ kwargs.update({"app_id": app_id}) return self.api_call("apps.manifest.delete", params=kwargs) def apps_manifest_export( self, *, app_id: str, **kwargs, ) -> SlackResponse: """Export an app manifest from an existing app https://docs.slack.dev/reference/methods/apps.manifest.export """ kwargs.update({"app_id": app_id}) return self.api_call("apps.manifest.export", params=kwargs) def apps_manifest_update( self, *, app_id: str, manifest: Union[str, Dict[str, Any]], **kwargs, ) -> SlackResponse: """Update an app from an app manifest https://docs.slack.dev/reference/methods/apps.manifest.update """ if isinstance(manifest, str): kwargs.update({"manifest": manifest}) else: kwargs.update({"manifest": json.dumps(manifest)}) kwargs.update({"app_id": app_id}) return self.api_call("apps.manifest.update", params=kwargs) def apps_manifest_validate( self, *, manifest: Union[str, Dict[str, Any]], app_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Validate an app manifest https://docs.slack.dev/reference/methods/apps.manifest.validate """ if isinstance(manifest, str): kwargs.update({"manifest": manifest}) else: kwargs.update({"manifest": json.dumps(manifest)}) kwargs.update({"app_id": app_id}) return self.api_call("apps.manifest.validate", params=kwargs) def apps_user_connection_update( self, *, user_id: str, status: str, **kwargs, ) -> SlackResponse: """Updates the connection status between a user and an app. https://docs.slack.dev/reference/methods/apps.user.connection.update """ kwargs.update({"user_id": user_id, "status": status}) return self.api_call("apps.user.connection.update", params=kwargs) def tooling_tokens_rotate( self, *, refresh_token: str, **kwargs, ) -> SlackResponse: """Exchanges a refresh token for a new app configuration token https://docs.slack.dev/reference/methods/tooling.tokens.rotate """ kwargs.update({"refresh_token": refresh_token}) return self.api_call("tooling.tokens.rotate", params=kwargs) def assistant_threads_setStatus( self, *, channel_id: str, thread_ts: str, status: str, loading_messages: Optional[List[str]] = None, icon_emoji: Optional[str] = None, icon_url: Optional[str] = None, username: Optional[str] = None, **kwargs, ) -> SlackResponse: """Set the status for an AI assistant thread. https://docs.slack.dev/reference/methods/assistant.threads.setStatus """ kwargs.update( { "channel_id": channel_id, "thread_ts": thread_ts, "status": status, "loading_messages": loading_messages, "icon_emoji": icon_emoji, "icon_url": icon_url, "username": username, } ) kwargs = _remove_none_values(kwargs) return self.api_call("assistant.threads.setStatus", json=kwargs) def assistant_threads_setTitle( self, *, channel_id: str, thread_ts: str, title: str, **kwargs, ) -> SlackResponse: """Set the title for the given assistant thread. https://docs.slack.dev/reference/methods/assistant.threads.setTitle """ kwargs.update({"channel_id": channel_id, "thread_ts": thread_ts, "title": title}) return self.api_call("assistant.threads.setTitle", params=kwargs) def assistant_threads_setSuggestedPrompts( self, *, channel_id: str, thread_ts: Optional[str] = None, title: Optional[str] = None, prompts: List[Dict[str, str]], **kwargs, ) -> SlackResponse: """Set suggested prompts for the given assistant thread. https://docs.slack.dev/reference/methods/assistant.threads.setSuggestedPrompts """ kwargs.update({"channel_id": channel_id, "prompts": prompts}) if thread_ts is not None: kwargs.update({"thread_ts": thread_ts}) if title is not None: kwargs.update({"title": title}) return self.api_call("assistant.threads.setSuggestedPrompts", json=kwargs) def auth_revoke( self, *, test: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Revokes a token. https://docs.slack.dev/reference/methods/auth.revoke """ kwargs.update({"test": test}) return self.api_call("auth.revoke", http_verb="GET", params=kwargs) def auth_test( self, **kwargs, ) -> SlackResponse: """Checks authentication & identity. https://docs.slack.dev/reference/methods/auth.test """ return self.api_call("auth.test", params=kwargs) def auth_teams_list( self, cursor: Optional[str] = None, limit: Optional[int] = None, include_icon: Optional[bool] = None, **kwargs, ) -> SlackResponse: """List the workspaces a token can access. https://docs.slack.dev/reference/methods/auth.teams.list """ kwargs.update({"cursor": cursor, "limit": limit, "include_icon": include_icon}) return self.api_call("auth.teams.list", params=kwargs) def bookmarks_add( self, *, channel_id: str, title: str, type: str, emoji: Optional[str] = None, entity_id: Optional[str] = None, link: Optional[str] = None, # include when type is 'link' parent_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Add bookmark to a channel. https://docs.slack.dev/reference/methods/bookmarks.add """ kwargs.update( { "channel_id": channel_id, "title": title, "type": type, "emoji": emoji, "entity_id": entity_id, "link": link, "parent_id": parent_id, } ) return self.api_call("bookmarks.add", http_verb="POST", params=kwargs) def bookmarks_edit( self, *, bookmark_id: str, channel_id: str, emoji: Optional[str] = None, link: Optional[str] = None, title: Optional[str] = None, **kwargs, ) -> SlackResponse: """Edit bookmark. https://docs.slack.dev/reference/methods/bookmarks.edit """ kwargs.update( { "bookmark_id": bookmark_id, "channel_id": channel_id, "emoji": emoji, "link": link, "title": title, } ) return self.api_call("bookmarks.edit", http_verb="POST", params=kwargs) def bookmarks_list( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """List bookmark for the channel. https://docs.slack.dev/reference/methods/bookmarks.list """ kwargs.update({"channel_id": channel_id}) return self.api_call("bookmarks.list", http_verb="POST", params=kwargs) def bookmarks_remove( self, *, bookmark_id: str, channel_id: str, **kwargs, ) -> SlackResponse: """Remove bookmark from the channel. https://docs.slack.dev/reference/methods/bookmarks.remove """ kwargs.update({"bookmark_id": bookmark_id, "channel_id": channel_id}) return self.api_call("bookmarks.remove", http_verb="POST", params=kwargs) def bots_info( self, *, bot: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Gets information about a bot user. https://docs.slack.dev/reference/methods/bots.info """ kwargs.update({"bot": bot, "team_id": team_id}) return self.api_call("bots.info", http_verb="GET", params=kwargs) def calls_add( self, *, external_unique_id: str, join_url: str, created_by: Optional[str] = None, date_start: Optional[int] = None, desktop_app_join_url: Optional[str] = None, external_display_id: Optional[str] = None, title: Optional[str] = None, users: Optional[Union[str, Sequence[Dict[str, str]]]] = None, **kwargs, ) -> SlackResponse: """Registers a new Call. https://docs.slack.dev/reference/methods/calls.add """ kwargs.update( { "external_unique_id": external_unique_id, "join_url": join_url, "created_by": created_by, "date_start": date_start, "desktop_app_join_url": desktop_app_join_url, "external_display_id": external_display_id, "title": title, } ) _update_call_participants( kwargs, users if users is not None else kwargs.get("users"), # type: ignore[arg-type] ) return self.api_call("calls.add", http_verb="POST", params=kwargs) def calls_end( self, *, id: str, duration: Optional[int] = None, **kwargs, ) -> SlackResponse: """Ends a Call. https://docs.slack.dev/reference/methods/calls.end """ kwargs.update({"id": id, "duration": duration}) return self.api_call("calls.end", http_verb="POST", params=kwargs) def calls_info( self, *, id: str, **kwargs, ) -> SlackResponse: """Returns information about a Call. https://docs.slack.dev/reference/methods/calls.info """ kwargs.update({"id": id}) return self.api_call("calls.info", http_verb="POST", params=kwargs) def calls_participants_add( self, *, id: str, users: Union[str, Sequence[Dict[str, str]]], **kwargs, ) -> SlackResponse: """Registers new participants added to a Call. https://docs.slack.dev/reference/methods/calls.participants.add """ kwargs.update({"id": id}) _update_call_participants(kwargs, users) return self.api_call("calls.participants.add", http_verb="POST", params=kwargs) def calls_participants_remove( self, *, id: str, users: Union[str, Sequence[Dict[str, str]]], **kwargs, ) -> SlackResponse: """Registers participants removed from a Call. https://docs.slack.dev/reference/methods/calls.participants.remove """ kwargs.update({"id": id}) _update_call_participants(kwargs, users) return self.api_call("calls.participants.remove", http_verb="POST", params=kwargs) def calls_update( self, *, id: str, desktop_app_join_url: Optional[str] = None, join_url: Optional[str] = None, title: Optional[str] = None, **kwargs, ) -> SlackResponse: """Updates information about a Call. https://docs.slack.dev/reference/methods/calls.update """ kwargs.update( { "id": id, "desktop_app_join_url": desktop_app_join_url, "join_url": join_url, "title": title, } ) return self.api_call("calls.update", http_verb="POST", params=kwargs) def canvases_create( self, *, title: Optional[str] = None, document_content: Dict[str, str], **kwargs, ) -> SlackResponse: """Create Canvas for a user https://docs.slack.dev/reference/methods/canvases.create """ kwargs.update({"title": title, "document_content": document_content}) return self.api_call("canvases.create", json=kwargs) def canvases_edit( self, *, canvas_id: str, changes: Sequence[Dict[str, Any]], **kwargs, ) -> SlackResponse: """Update an existing canvas https://docs.slack.dev/reference/methods/canvases.edit """ kwargs.update({"canvas_id": canvas_id, "changes": changes}) return self.api_call("canvases.edit", json=kwargs) def canvases_delete( self, *, canvas_id: str, **kwargs, ) -> SlackResponse: """Deletes a canvas https://docs.slack.dev/reference/methods/canvases.delete """ kwargs.update({"canvas_id": canvas_id}) return self.api_call("canvases.delete", params=kwargs) def canvases_access_set( self, *, canvas_id: str, access_level: str, channel_ids: Optional[Union[Sequence[str], str]] = None, user_ids: Optional[Union[Sequence[str], str]] = None, **kwargs, ) -> SlackResponse: """Sets the access level to a canvas for specified entities https://docs.slack.dev/reference/methods/canvases.access.set """ kwargs.update({"canvas_id": canvas_id, "access_level": access_level}) if channel_ids is not None: if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) if user_ids is not None: if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) return self.api_call("canvases.access.set", params=kwargs) def canvases_access_delete( self, *, canvas_id: str, channel_ids: Optional[Union[Sequence[str], str]] = None, user_ids: Optional[Union[Sequence[str], str]] = None, **kwargs, ) -> SlackResponse: """Create a Channel Canvas for a channel https://docs.slack.dev/reference/methods/canvases.access.delete """ kwargs.update({"canvas_id": canvas_id}) if channel_ids is not None: if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) if user_ids is not None: if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) return self.api_call("canvases.access.delete", params=kwargs) def canvases_sections_lookup( self, *, canvas_id: str, criteria: Dict[str, Any], **kwargs, ) -> SlackResponse: """Find sections matching the provided criteria https://docs.slack.dev/reference/methods/canvases.sections.lookup """ kwargs.update({"canvas_id": canvas_id, "criteria": json.dumps(criteria)}) return self.api_call("canvases.sections.lookup", params=kwargs) # -------------------------- # Deprecated: channels.* # You can use conversations.* APIs instead. # https://docs.slack.dev/changelog/2020-01-deprecating-antecedents-to-the-conversations-api/ # -------------------------- def channels_archive( self, *, channel: str, **kwargs, ) -> SlackResponse: """Archives a channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.archive", json=kwargs) def channels_create( self, *, name: str, **kwargs, ) -> SlackResponse: """Creates a channel.""" kwargs.update({"name": name}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.create", json=kwargs) def channels_history( self, *, channel: str, **kwargs, ) -> SlackResponse: """Fetches history of messages and events from a channel.""" kwargs.update({"channel": channel}) return self.api_call("channels.history", http_verb="GET", params=kwargs) def channels_info( self, *, channel: str, **kwargs, ) -> SlackResponse: """Gets information about a channel.""" kwargs.update({"channel": channel}) return self.api_call("channels.info", http_verb="GET", params=kwargs) def channels_invite( self, *, channel: str, user: str, **kwargs, ) -> SlackResponse: """Invites a user to a channel.""" kwargs.update({"channel": channel, "user": user}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.invite", json=kwargs) def channels_join( self, *, name: str, **kwargs, ) -> SlackResponse: """Joins a channel, creating it if needed.""" kwargs.update({"name": name}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.join", json=kwargs) def channels_kick( self, *, channel: str, user: str, **kwargs, ) -> SlackResponse: """Removes a user from a channel.""" kwargs.update({"channel": channel, "user": user}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.kick", json=kwargs) def channels_leave( self, *, channel: str, **kwargs, ) -> SlackResponse: """Leaves a channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.leave", json=kwargs) def channels_list( self, **kwargs, ) -> SlackResponse: """Lists all channels in a Slack team.""" return self.api_call("channels.list", http_verb="GET", params=kwargs) def channels_mark( self, *, channel: str, ts: str, **kwargs, ) -> SlackResponse: """Sets the read cursor in a channel.""" kwargs.update({"channel": channel, "ts": ts}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.mark", json=kwargs) def channels_rename( self, *, channel: str, name: str, **kwargs, ) -> SlackResponse: """Renames a channel.""" kwargs.update({"channel": channel, "name": name}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.rename", json=kwargs) def channels_replies( self, *, channel: str, thread_ts: str, **kwargs, ) -> SlackResponse: """Retrieve a thread of messages posted to a channel""" kwargs.update({"channel": channel, "thread_ts": thread_ts}) return self.api_call("channels.replies", http_verb="GET", params=kwargs) def channels_setPurpose( self, *, channel: str, purpose: str, **kwargs, ) -> SlackResponse: """Sets the purpose for a channel.""" kwargs.update({"channel": channel, "purpose": purpose}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.setPurpose", json=kwargs) def channels_setTopic( self, *, channel: str, topic: str, **kwargs, ) -> SlackResponse: """Sets the topic for a channel.""" kwargs.update({"channel": channel, "topic": topic}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.setTopic", json=kwargs) def channels_unarchive( self, *, channel: str, **kwargs, ) -> SlackResponse: """Unarchives a channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.unarchive", json=kwargs) # -------------------------- def chat_appendStream( self, *, channel: str, ts: str, markdown_text: Optional[str] = None, chunks: Optional[Sequence[Union[Dict, Chunk]]] = None, **kwargs, ) -> SlackResponse: """Appends text to an existing streaming conversation. https://docs.slack.dev/reference/methods/chat.appendStream """ kwargs.update( { "channel": channel, "ts": ts, "markdown_text": markdown_text, "chunks": chunks, } ) _parse_web_class_objects(kwargs) kwargs = _remove_none_values(kwargs) return self.api_call("chat.appendStream", json=kwargs) def chat_delete( self, *, channel: str, ts: str, as_user: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Deletes a message. https://docs.slack.dev/reference/methods/chat.delete """ kwargs.update({"channel": channel, "ts": ts, "as_user": as_user}) return self.api_call("chat.delete", params=kwargs) def chat_deleteScheduledMessage( self, *, channel: str, scheduled_message_id: str, as_user: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Deletes a scheduled message. https://docs.slack.dev/reference/methods/chat.deleteScheduledMessage """ kwargs.update( { "channel": channel, "scheduled_message_id": scheduled_message_id, "as_user": as_user, } ) return self.api_call("chat.deleteScheduledMessage", params=kwargs) def chat_getPermalink( self, *, channel: str, message_ts: str, **kwargs, ) -> SlackResponse: """Retrieve a permalink URL for a specific extant message https://docs.slack.dev/reference/methods/chat.getPermalink """ kwargs.update({"channel": channel, "message_ts": message_ts}) return self.api_call("chat.getPermalink", http_verb="GET", params=kwargs) def chat_meMessage( self, *, channel: str, text: str, **kwargs, ) -> SlackResponse: """Share a me message into a channel. https://docs.slack.dev/reference/methods/chat.meMessage """ kwargs.update({"channel": channel, "text": text}) return self.api_call("chat.meMessage", params=kwargs) def chat_postEphemeral( self, *, channel: str, user: str, text: Optional[str] = None, as_user: Optional[bool] = None, attachments: Optional[Union[str, Sequence[Union[Dict, Attachment]]]] = None, blocks: Optional[Union[str, Sequence[Union[Dict, Block]]]] = None, thread_ts: Optional[str] = None, icon_emoji: Optional[str] = None, icon_url: Optional[str] = None, link_names: Optional[bool] = None, username: Optional[str] = None, parse: Optional[str] = None, markdown_text: Optional[str] = None, **kwargs, ) -> SlackResponse: """Sends an ephemeral message to a user in a channel. https://docs.slack.dev/reference/methods/chat.postEphemeral """ kwargs.update( { "channel": channel, "user": user, "text": text, "as_user": as_user, "attachments": attachments, "blocks": blocks, "thread_ts": thread_ts, "icon_emoji": icon_emoji, "icon_url": icon_url, "link_names": link_names, "username": username, "parse": parse, "markdown_text": markdown_text, } ) _parse_web_class_objects(kwargs) kwargs = _remove_none_values(kwargs) _warn_if_message_text_content_is_missing("chat.postEphemeral", kwargs) # NOTE: intentionally using json over params for the API methods using blocks/attachments return self.api_call("chat.postEphemeral", json=kwargs) def chat_postMessage( self, *, channel: str, text: Optional[str] = None, as_user: Optional[bool] = None, attachments: Optional[Union[str, Sequence[Union[Dict, Attachment]]]] = None, blocks: Optional[Union[str, Sequence[Union[Dict, Block]]]] = None, thread_ts: Optional[str] = None, reply_broadcast: Optional[bool] = None, unfurl_links: Optional[bool] = None, unfurl_media: Optional[bool] = None, container_id: Optional[str] = None, icon_emoji: Optional[str] = None, icon_url: Optional[str] = None, mrkdwn: Optional[bool] = None, link_names: Optional[bool] = None, username: Optional[str] = None, parse: Optional[str] = None, # none, full metadata: Optional[Union[Dict, Metadata, EventAndEntityMetadata]] = None, markdown_text: Optional[str] = None, **kwargs, ) -> SlackResponse: """Sends a message to a channel. https://docs.slack.dev/reference/methods/chat.postMessage """ kwargs.update( { "channel": channel, "text": text, "as_user": as_user, "attachments": attachments, "blocks": blocks, "thread_ts": thread_ts, "reply_broadcast": reply_broadcast, "unfurl_links": unfurl_links, "unfurl_media": unfurl_media, "container_id": container_id, "icon_emoji": icon_emoji, "icon_url": icon_url, "mrkdwn": mrkdwn, "link_names": link_names, "username": username, "parse": parse, "metadata": metadata, "markdown_text": markdown_text, } ) _parse_web_class_objects(kwargs) kwargs = _remove_none_values(kwargs) _warn_if_message_text_content_is_missing("chat.postMessage", kwargs) # NOTE: intentionally using json over params for the API methods using blocks/attachments return self.api_call("chat.postMessage", json=kwargs) def chat_scheduleMessage( self, *, channel: str, post_at: Union[str, int], text: Optional[str] = None, as_user: Optional[bool] = None, attachments: Optional[Union[str, Sequence[Union[Dict, Attachment]]]] = None, blocks: Optional[Union[str, Sequence[Union[Dict, Block]]]] = None, thread_ts: Optional[str] = None, parse: Optional[str] = None, reply_broadcast: Optional[bool] = None, unfurl_links: Optional[bool] = None, unfurl_media: Optional[bool] = None, link_names: Optional[bool] = None, metadata: Optional[Union[Dict, Metadata]] = None, markdown_text: Optional[str] = None, **kwargs, ) -> SlackResponse: """Schedules a message. https://docs.slack.dev/reference/methods/chat.scheduleMessage """ kwargs.update( { "channel": channel, "post_at": post_at, "text": text, "as_user": as_user, "attachments": attachments, "blocks": blocks, "thread_ts": thread_ts, "reply_broadcast": reply_broadcast, "parse": parse, "unfurl_links": unfurl_links, "unfurl_media": unfurl_media, "link_names": link_names, "metadata": metadata, "markdown_text": markdown_text, } ) _parse_web_class_objects(kwargs) kwargs = _remove_none_values(kwargs) _warn_if_message_text_content_is_missing("chat.scheduleMessage", kwargs) # NOTE: intentionally using json over params for the API methods using blocks/attachments return self.api_call("chat.scheduleMessage", json=kwargs) def chat_scheduledMessages_list( self, *, channel: Optional[str] = None, cursor: Optional[str] = None, latest: Optional[str] = None, limit: Optional[int] = None, oldest: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists all scheduled messages. https://docs.slack.dev/reference/methods/chat.scheduledMessages.list """ kwargs.update( { "channel": channel, "cursor": cursor, "latest": latest, "limit": limit, "oldest": oldest, "team_id": team_id, } ) return self.api_call("chat.scheduledMessages.list", params=kwargs) def chat_startStream( self, *, channel: str, thread_ts: str, markdown_text: Optional[str] = None, recipient_team_id: Optional[str] = None, recipient_user_id: Optional[str] = None, chunks: Optional[Sequence[Union[Dict, Chunk]]] = None, task_display_mode: Optional[str] = None, # timeline, plan icon_emoji: Optional[str] = None, icon_url: Optional[str] = None, username: Optional[str] = None, **kwargs, ) -> SlackResponse: """Starts a new streaming conversation. https://docs.slack.dev/reference/methods/chat.startStream """ kwargs.update( { "channel": channel, "thread_ts": thread_ts, "markdown_text": markdown_text, "recipient_team_id": recipient_team_id, "recipient_user_id": recipient_user_id, "chunks": chunks, "task_display_mode": task_display_mode, "icon_emoji": icon_emoji, "icon_url": icon_url, "username": username, } ) _parse_web_class_objects(kwargs) kwargs = _remove_none_values(kwargs) return self.api_call("chat.startStream", json=kwargs) def chat_stopStream( self, *, channel: str, ts: str, markdown_text: Optional[str] = None, blocks: Optional[Union[str, Sequence[Union[Dict, Block]]]] = None, metadata: Optional[Union[Dict, Metadata]] = None, chunks: Optional[Sequence[Union[Dict, Chunk]]] = None, **kwargs, ) -> SlackResponse: """Stops a streaming conversation. https://docs.slack.dev/reference/methods/chat.stopStream """ kwargs.update( { "channel": channel, "ts": ts, "markdown_text": markdown_text, "blocks": blocks, "metadata": metadata, "chunks": chunks, } ) _parse_web_class_objects(kwargs) kwargs = _remove_none_values(kwargs) return self.api_call("chat.stopStream", json=kwargs) def chat_stream( self, *, buffer_size: int = 256, channel: str, thread_ts: str, recipient_team_id: Optional[str] = None, recipient_user_id: Optional[str] = None, task_display_mode: Optional[str] = None, icon_emoji: Optional[str] = None, icon_url: Optional[str] = None, username: Optional[str] = None, **kwargs, ) -> ChatStream: """Stream markdown text into a conversation. This method starts a new chat stream in a conversation that can be appended to. After appending an entire message, the stream can be stopped with concluding arguments such as "blocks" for gathering feedback. The following methods are used: - chat.startStream: Starts a new streaming conversation. [Reference](https://docs.slack.dev/reference/methods/chat.startStream). - chat.appendStream: Appends text to an existing streaming conversation. [Reference](https://docs.slack.dev/reference/methods/chat.appendStream). - chat.stopStream: Stops a streaming conversation. [Reference](https://docs.slack.dev/reference/methods/chat.stopStream). Args: buffer_size: The length of markdown_text to buffer in-memory before calling a stream method. Increasing this value decreases the number of method calls made for the same amount of text, which is useful to avoid rate limits. Default: 256. channel: An encoded ID that represents a channel, private group, or DM. thread_ts: Provide another message's ts value to reply to. Streamed messages should always be replies to a user request. recipient_team_id: The encoded ID of the team the user receiving the streaming text belongs to. Required when streaming to channels. recipient_user_id: The encoded ID of the user to receive the streaming text. Required when streaming to channels. task_display_mode: Specifies how tasks are displayed in the message. A "timeline" displays individual tasks with text and "plan" displays all tasks together. icon_emoji: Emoji to use as the icon for this message. Overrides icon_url. icon_url: Image URL to use as the icon for this message. username: The bot's username to display. **kwargs: Additional arguments passed to the underlying API calls. Returns: ChatStream instance for managing the stream Example: ```python streamer = client.chat_stream( channel="C0123456789", thread_ts="1700000001.123456", recipient_team_id="T0123456789", recipient_user_id="U0123456789", ) streamer.append(markdown_text="**hello wo") streamer.append(markdown_text="rld!**") streamer.stop() ``` """ return ChatStream( self, logger=self._logger, channel=channel, thread_ts=thread_ts, recipient_team_id=recipient_team_id, recipient_user_id=recipient_user_id, task_display_mode=task_display_mode, icon_emoji=icon_emoji, icon_url=icon_url, username=username, buffer_size=buffer_size, **kwargs, ) def chat_unfurl( self, *, channel: Optional[str] = None, ts: Optional[str] = None, source: Optional[str] = None, unfurl_id: Optional[str] = None, unfurls: Optional[Dict[str, Dict]] = None, # or user_auth_* metadata: Optional[Union[Dict, EventAndEntityMetadata]] = None, user_auth_blocks: Optional[Union[str, Sequence[Union[Dict, Block]]]] = None, user_auth_message: Optional[str] = None, user_auth_required: Optional[bool] = None, user_auth_url: Optional[str] = None, **kwargs, ) -> SlackResponse: """Provide custom unfurl behavior for user-posted URLs. https://docs.slack.dev/reference/methods/chat.unfurl """ kwargs.update( { "channel": channel, "ts": ts, "source": source, "unfurl_id": unfurl_id, "unfurls": unfurls, "metadata": metadata, "user_auth_blocks": user_auth_blocks, "user_auth_message": user_auth_message, "user_auth_required": user_auth_required, "user_auth_url": user_auth_url, } ) _parse_web_class_objects(kwargs) # for user_auth_blocks kwargs = _remove_none_values(kwargs) # NOTE: intentionally using json over params for API methods using blocks/attachments return self.api_call("chat.unfurl", json=kwargs) def chat_update( self, *, channel: str, ts: str, text: Optional[str] = None, attachments: Optional[Union[str, Sequence[Union[Dict, Attachment]]]] = None, blocks: Optional[Union[str, Sequence[Union[Dict, Block]]]] = None, as_user: Optional[bool] = None, file_ids: Optional[Union[str, Sequence[str]]] = None, link_names: Optional[bool] = None, parse: Optional[str] = None, # none, full reply_broadcast: Optional[bool] = None, metadata: Optional[Union[Dict, Metadata]] = None, markdown_text: Optional[str] = None, **kwargs, ) -> SlackResponse: """Updates a message in a channel. https://docs.slack.dev/reference/methods/chat.update """ kwargs.update( { "channel": channel, "ts": ts, "text": text, "attachments": attachments, "blocks": blocks, "as_user": as_user, "link_names": link_names, "parse": parse, "reply_broadcast": reply_broadcast, "metadata": metadata, "markdown_text": markdown_text, } ) if isinstance(file_ids, (list, tuple)): kwargs.update({"file_ids": ",".join(file_ids)}) else: kwargs.update({"file_ids": file_ids}) _parse_web_class_objects(kwargs) kwargs = _remove_none_values(kwargs) _warn_if_message_text_content_is_missing("chat.update", kwargs) # NOTE: intentionally using json over params for API methods using blocks/attachments return self.api_call("chat.update", json=kwargs) def conversations_acceptSharedInvite( self, *, channel_name: str, channel_id: Optional[str] = None, invite_id: Optional[str] = None, free_trial_accepted: Optional[bool] = None, is_private: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Accepts an invitation to a Slack Connect channel. https://docs.slack.dev/reference/methods/conversations.acceptSharedInvite """ if channel_id is None and invite_id is None: raise e.SlackRequestError("Either channel_id or invite_id must be provided.") kwargs.update( { "channel_name": channel_name, "channel_id": channel_id, "invite_id": invite_id, "free_trial_accepted": free_trial_accepted, "is_private": is_private, "team_id": team_id, } ) return self.api_call("conversations.acceptSharedInvite", http_verb="POST", params=kwargs) def conversations_approveSharedInvite( self, *, invite_id: str, target_team: Optional[str] = None, **kwargs, ) -> SlackResponse: """Approves an invitation to a Slack Connect channel. https://docs.slack.dev/reference/methods/conversations.approveSharedInvite """ kwargs.update({"invite_id": invite_id, "target_team": target_team}) return self.api_call("conversations.approveSharedInvite", http_verb="POST", params=kwargs) def conversations_archive( self, *, channel: str, **kwargs, ) -> SlackResponse: """Archives a conversation. https://docs.slack.dev/reference/methods/conversations.archive """ kwargs.update({"channel": channel}) return self.api_call("conversations.archive", params=kwargs) def conversations_close( self, *, channel: str, **kwargs, ) -> SlackResponse: """Closes a direct message or multi-person direct message. https://docs.slack.dev/reference/methods/conversations.close """ kwargs.update({"channel": channel}) return self.api_call("conversations.close", params=kwargs) def conversations_create( self, *, name: str, is_private: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Initiates a public or private channel-based conversation https://docs.slack.dev/reference/methods/conversations.create """ kwargs.update({"name": name, "is_private": is_private, "team_id": team_id}) return self.api_call("conversations.create", params=kwargs) def conversations_declineSharedInvite( self, *, invite_id: str, target_team: Optional[str] = None, **kwargs, ) -> SlackResponse: """Declines a Slack Connect channel invite. https://docs.slack.dev/reference/methods/conversations.declineSharedInvite """ kwargs.update({"invite_id": invite_id, "target_team": target_team}) return self.api_call("conversations.declineSharedInvite", http_verb="GET", params=kwargs) def conversations_externalInvitePermissions_set( self, *, action: str, channel: str, target_team: str, **kwargs ) -> SlackResponse: """Sets a team in a shared External Limited channel to a shared Slack Connect channel or vice versa. https://docs.slack.dev/reference/methods/conversations.externalInvitePermissions.set """ kwargs.update( { "action": action, "channel": channel, "target_team": target_team, } ) return self.api_call("conversations.externalInvitePermissions.set", params=kwargs) def conversations_history( self, *, channel: str, cursor: Optional[str] = None, inclusive: Optional[bool] = None, include_all_metadata: Optional[bool] = None, latest: Optional[str] = None, limit: Optional[int] = None, oldest: Optional[str] = None, **kwargs, ) -> SlackResponse: """Fetches a conversation's history of messages and events. https://docs.slack.dev/reference/methods/conversations.history """ kwargs.update( { "channel": channel, "cursor": cursor, "inclusive": inclusive, "include_all_metadata": include_all_metadata, "limit": limit, "latest": latest, "oldest": oldest, } ) return self.api_call("conversations.history", http_verb="GET", params=kwargs) def conversations_info( self, *, channel: str, include_locale: Optional[bool] = None, include_num_members: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Retrieve information about a conversation. https://docs.slack.dev/reference/methods/conversations.info """ kwargs.update( { "channel": channel, "include_locale": include_locale, "include_num_members": include_num_members, } ) return self.api_call("conversations.info", http_verb="GET", params=kwargs) def conversations_invite( self, *, channel: str, users: Union[str, Sequence[str]], force: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Invites users to a channel. https://docs.slack.dev/reference/methods/conversations.invite """ kwargs.update( { "channel": channel, "force": force, } ) if isinstance(users, (list, tuple)): kwargs.update({"users": ",".join(users)}) else: kwargs.update({"users": users}) return self.api_call("conversations.invite", params=kwargs) def conversations_inviteShared( self, *, channel: str, emails: Optional[Union[str, Sequence[str]]] = None, user_ids: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """Sends an invitation to a Slack Connect channel. https://docs.slack.dev/reference/methods/conversations.inviteShared """ if emails is None and user_ids is None: raise e.SlackRequestError("Either emails or user ids must be provided.") kwargs.update({"channel": channel}) if isinstance(emails, (list, tuple)): kwargs.update({"emails": ",".join(emails)}) else: kwargs.update({"emails": emails}) if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) return self.api_call("conversations.inviteShared", http_verb="GET", params=kwargs) def conversations_join( self, *, channel: str, **kwargs, ) -> SlackResponse: """Joins an existing conversation. https://docs.slack.dev/reference/methods/conversations.join """ kwargs.update({"channel": channel}) return self.api_call("conversations.join", params=kwargs) def conversations_kick( self, *, channel: str, user: str, **kwargs, ) -> SlackResponse: """Removes a user from a conversation. https://docs.slack.dev/reference/methods/conversations.kick """ kwargs.update({"channel": channel, "user": user}) return self.api_call("conversations.kick", params=kwargs) def conversations_leave( self, *, channel: str, **kwargs, ) -> SlackResponse: """Leaves a conversation. https://docs.slack.dev/reference/methods/conversations.leave """ kwargs.update({"channel": channel}) return self.api_call("conversations.leave", params=kwargs) def conversations_list( self, *, cursor: Optional[str] = None, exclude_archived: Optional[bool] = None, limit: Optional[int] = None, team_id: Optional[str] = None, types: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """Lists all channels in a Slack team. https://docs.slack.dev/reference/methods/conversations.list """ kwargs.update( { "cursor": cursor, "exclude_archived": exclude_archived, "limit": limit, "team_id": team_id, } ) if isinstance(types, (list, tuple)): kwargs.update({"types": ",".join(types)}) else: kwargs.update({"types": types}) return self.api_call("conversations.list", http_verb="GET", params=kwargs) def conversations_listConnectInvites( self, *, count: Optional[int] = None, cursor: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List shared channel invites that have been generated or received but have not yet been approved by all parties. https://docs.slack.dev/reference/methods/conversations.listConnectInvites """ kwargs.update({"count": count, "cursor": cursor, "team_id": team_id}) return self.api_call("conversations.listConnectInvites", params=kwargs) def conversations_mark( self, *, channel: str, ts: str, **kwargs, ) -> SlackResponse: """Sets the read cursor in a channel. https://docs.slack.dev/reference/methods/conversations.mark """ kwargs.update({"channel": channel, "ts": ts}) return self.api_call("conversations.mark", params=kwargs) def conversations_members( self, *, channel: str, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Retrieve members of a conversation. https://docs.slack.dev/reference/methods/conversations.members """ kwargs.update({"channel": channel, "cursor": cursor, "limit": limit}) return self.api_call("conversations.members", http_verb="GET", params=kwargs) def conversations_open( self, *, channel: Optional[str] = None, return_im: Optional[bool] = None, users: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """Opens or resumes a direct message or multi-person direct message. https://docs.slack.dev/reference/methods/conversations.open """ if channel is None and users is None: raise e.SlackRequestError("Either channel or users must be provided.") kwargs.update({"channel": channel, "return_im": return_im}) if isinstance(users, (list, tuple)): kwargs.update({"users": ",".join(users)}) else: kwargs.update({"users": users}) return self.api_call("conversations.open", params=kwargs) def conversations_rename( self, *, channel: str, name: str, **kwargs, ) -> SlackResponse: """Renames a conversation. https://docs.slack.dev/reference/methods/conversations.rename """ kwargs.update({"channel": channel, "name": name}) return self.api_call("conversations.rename", params=kwargs) def conversations_replies( self, *, channel: str, ts: str, cursor: Optional[str] = None, inclusive: Optional[bool] = None, include_all_metadata: Optional[bool] = None, latest: Optional[str] = None, limit: Optional[int] = None, oldest: Optional[str] = None, **kwargs, ) -> SlackResponse: """Retrieve a thread of messages posted to a conversation https://docs.slack.dev/reference/methods/conversations.replies """ kwargs.update( { "channel": channel, "ts": ts, "cursor": cursor, "inclusive": inclusive, "include_all_metadata": include_all_metadata, "limit": limit, "latest": latest, "oldest": oldest, } ) return self.api_call("conversations.replies", http_verb="GET", params=kwargs) def conversations_requestSharedInvite_approve( self, *, invite_id: str, channel_id: Optional[str] = None, is_external_limited: Optional[str] = None, message: Optional[Dict[str, Any]] = None, **kwargs, ) -> SlackResponse: """Approve a request to add an external user to a channel. This also sends them a Slack Connect invite. https://docs.slack.dev/reference/methods/conversations.requestSharedInvite.approve """ kwargs.update( { "invite_id": invite_id, "channel_id": channel_id, "is_external_limited": is_external_limited, } ) if message is not None: kwargs.update({"message": json.dumps(message)}) return self.api_call("conversations.requestSharedInvite.approve", params=kwargs) def conversations_requestSharedInvite_deny( self, *, invite_id: str, message: Optional[str] = None, **kwargs, ) -> SlackResponse: """Deny a request to invite an external user to a channel. https://docs.slack.dev/reference/methods/conversations.requestSharedInvite.deny """ kwargs.update({"invite_id": invite_id, "message": message}) return self.api_call("conversations.requestSharedInvite.deny", params=kwargs) def conversations_requestSharedInvite_list( self, *, cursor: Optional[str] = None, include_approved: Optional[bool] = None, include_denied: Optional[bool] = None, include_expired: Optional[bool] = None, invite_ids: Optional[Union[str, Sequence[str]]] = None, limit: Optional[int] = None, user_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists requests to add external users to channels with ability to filter. https://docs.slack.dev/reference/methods/conversations.requestSharedInvite.list """ kwargs.update( { "cursor": cursor, "include_approved": include_approved, "include_denied": include_denied, "include_expired": include_expired, "limit": limit, "user_id": user_id, } ) if invite_ids is not None: if isinstance(invite_ids, (list, tuple)): kwargs.update({"invite_ids": ",".join(invite_ids)}) else: kwargs.update({"invite_ids": invite_ids}) return self.api_call("conversations.requestSharedInvite.list", params=kwargs) def conversations_setPurpose( self, *, channel: str, purpose: str, **kwargs, ) -> SlackResponse: """Sets the purpose for a conversation. https://docs.slack.dev/reference/methods/conversations.setPurpose """ kwargs.update({"channel": channel, "purpose": purpose}) return self.api_call("conversations.setPurpose", params=kwargs) def conversations_setTopic( self, *, channel: str, topic: str, **kwargs, ) -> SlackResponse: """Sets the topic for a conversation. https://docs.slack.dev/reference/methods/conversations.setTopic """ kwargs.update({"channel": channel, "topic": topic}) return self.api_call("conversations.setTopic", params=kwargs) def conversations_unarchive( self, *, channel: str, **kwargs, ) -> SlackResponse: """Reverses conversation archival. https://docs.slack.dev/reference/methods/conversations.unarchive """ kwargs.update({"channel": channel}) return self.api_call("conversations.unarchive", params=kwargs) def conversations_canvases_create( self, *, channel_id: str, document_content: Dict[str, str], **kwargs, ) -> SlackResponse: """Create a Channel Canvas for a channel https://docs.slack.dev/reference/methods/conversations.canvases.create """ kwargs.update({"channel_id": channel_id, "document_content": document_content}) return self.api_call("conversations.canvases.create", json=kwargs) def dialog_open( self, *, dialog: Dict[str, Any], trigger_id: str, **kwargs, ) -> SlackResponse: """Open a dialog with a user. https://docs.slack.dev/reference/methods/dialog.open """ kwargs.update({"dialog": dialog, "trigger_id": trigger_id}) kwargs = _remove_none_values(kwargs) # NOTE: As the dialog can be a dict, this API call works only with json format. return self.api_call("dialog.open", json=kwargs) def dnd_endDnd( self, **kwargs, ) -> SlackResponse: """Ends the current user's Do Not Disturb session immediately. https://docs.slack.dev/reference/methods/dnd.endDnd """ return self.api_call("dnd.endDnd", params=kwargs) def dnd_endSnooze( self, **kwargs, ) -> SlackResponse: """Ends the current user's snooze mode immediately. https://docs.slack.dev/reference/methods/dnd.endSnooze """ return self.api_call("dnd.endSnooze", params=kwargs) def dnd_info( self, *, team_id: Optional[str] = None, user: Optional[str] = None, **kwargs, ) -> SlackResponse: """Retrieves a user's current Do Not Disturb status. https://docs.slack.dev/reference/methods/dnd.info """ kwargs.update({"team_id": team_id, "user": user}) return self.api_call("dnd.info", http_verb="GET", params=kwargs) def dnd_setSnooze( self, *, num_minutes: Union[int, str], **kwargs, ) -> SlackResponse: """Turns on Do Not Disturb mode for the current user, or changes its duration. https://docs.slack.dev/reference/methods/dnd.setSnooze """ kwargs.update({"num_minutes": num_minutes}) return self.api_call("dnd.setSnooze", http_verb="GET", params=kwargs) def dnd_teamInfo( self, users: Union[str, Sequence[str]], team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Retrieves the Do Not Disturb status for users on a team. https://docs.slack.dev/reference/methods/dnd.teamInfo """ if isinstance(users, (list, tuple)): kwargs.update({"users": ",".join(users)}) else: kwargs.update({"users": users}) kwargs.update({"team_id": team_id}) return self.api_call("dnd.teamInfo", http_verb="GET", params=kwargs) def emoji_list( self, include_categories: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Lists custom emoji for a team. https://docs.slack.dev/reference/methods/emoji.list """ kwargs.update({"include_categories": include_categories}) return self.api_call("emoji.list", http_verb="GET", params=kwargs) def entity_presentDetails( self, trigger_id: str, metadata: Optional[Union[Dict, EntityMetadata]] = None, user_auth_required: Optional[bool] = None, user_auth_url: Optional[str] = None, error: Optional[Dict[str, Any]] = None, **kwargs, ) -> SlackResponse: """Provides entity details for the flexpane. https://docs.slack.dev/reference/methods/entity.presentDetails/ """ kwargs.update({"trigger_id": trigger_id}) if metadata is not None: kwargs.update({"metadata": metadata}) if user_auth_required is not None: kwargs.update({"user_auth_required": user_auth_required}) if user_auth_url is not None: kwargs.update({"user_auth_url": user_auth_url}) if error is not None: kwargs.update({"error": error}) _parse_web_class_objects(kwargs) return self.api_call("entity.presentDetails", json=kwargs) def files_comments_delete( self, *, file: str, id: str, **kwargs, ) -> SlackResponse: """Deletes an existing comment on a file. https://docs.slack.dev/reference/methods/files.comments.delete """ kwargs.update({"file": file, "id": id}) return self.api_call("files.comments.delete", params=kwargs) def files_delete( self, *, file: str, **kwargs, ) -> SlackResponse: """Deletes a file. https://docs.slack.dev/reference/methods/files.delete """ kwargs.update({"file": file}) return self.api_call("files.delete", params=kwargs) def files_info( self, *, file: str, count: Optional[int] = None, cursor: Optional[str] = None, limit: Optional[int] = None, page: Optional[int] = None, **kwargs, ) -> SlackResponse: """Gets information about a team file. https://docs.slack.dev/reference/methods/files.info """ kwargs.update( { "file": file, "count": count, "cursor": cursor, "limit": limit, "page": page, } ) return self.api_call("files.info", http_verb="GET", params=kwargs) def files_list( self, *, channel: Optional[str] = None, count: Optional[int] = None, page: Optional[int] = None, show_files_hidden_by_limit: Optional[bool] = None, team_id: Optional[str] = None, ts_from: Optional[str] = None, ts_to: Optional[str] = None, types: Optional[Union[str, Sequence[str]]] = None, user: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists & filters team files. https://docs.slack.dev/reference/methods/files.list """ kwargs.update( { "channel": channel, "count": count, "page": page, "show_files_hidden_by_limit": show_files_hidden_by_limit, "team_id": team_id, "ts_from": ts_from, "ts_to": ts_to, "user": user, } ) if isinstance(types, (list, tuple)): kwargs.update({"types": ",".join(types)}) else: kwargs.update({"types": types}) return self.api_call("files.list", http_verb="GET", params=kwargs) def files_remote_info( self, *, external_id: Optional[str] = None, file: Optional[str] = None, **kwargs, ) -> SlackResponse: """Retrieve information about a remote file added to Slack. https://docs.slack.dev/reference/methods/files.remote.info """ kwargs.update({"external_id": external_id, "file": file}) return self.api_call("files.remote.info", http_verb="GET", params=kwargs) def files_remote_list( self, *, channel: Optional[str] = None, cursor: Optional[str] = None, limit: Optional[int] = None, ts_from: Optional[str] = None, ts_to: Optional[str] = None, **kwargs, ) -> SlackResponse: """Retrieve information about a remote file added to Slack. https://docs.slack.dev/reference/methods/files.remote.list """ kwargs.update( { "channel": channel, "cursor": cursor, "limit": limit, "ts_from": ts_from, "ts_to": ts_to, } ) return self.api_call("files.remote.list", http_verb="GET", params=kwargs) def files_remote_add( self, *, external_id: str, external_url: str, title: str, filetype: Optional[str] = None, indexable_file_contents: Optional[Union[str, bytes, IOBase]] = None, preview_image: Optional[Union[str, bytes, IOBase]] = None, **kwargs, ) -> SlackResponse: """Adds a file from a remote service. https://docs.slack.dev/reference/methods/files.remote.add """ kwargs.update( { "external_id": external_id, "external_url": external_url, "title": title, "filetype": filetype, } ) files = None # preview_image (file): Preview of the document via multipart/form-data. if preview_image is not None or indexable_file_contents is not None: files = { "preview_image": preview_image, "indexable_file_contents": indexable_file_contents, } return self.api_call( # Intentionally using "POST" method over "GET" here "files.remote.add", http_verb="POST", data=kwargs, files=files, ) def files_remote_update( self, *, external_id: Optional[str] = None, external_url: Optional[str] = None, file: Optional[str] = None, title: Optional[str] = None, filetype: Optional[str] = None, indexable_file_contents: Optional[str] = None, preview_image: Optional[str] = None, **kwargs, ) -> SlackResponse: """Updates an existing remote file. https://docs.slack.dev/reference/methods/files.remote.update """ kwargs.update( { "external_id": external_id, "external_url": external_url, "file": file, "title": title, "filetype": filetype, } ) files = None # preview_image (file): Preview of the document via multipart/form-data. if preview_image is not None or indexable_file_contents is not None: files = { "preview_image": preview_image, "indexable_file_contents": indexable_file_contents, } return self.api_call( # Intentionally using "POST" method over "GET" here "files.remote.update", http_verb="POST", data=kwargs, files=files, ) def files_remote_remove( self, *, external_id: Optional[str] = None, file: Optional[str] = None, **kwargs, ) -> SlackResponse: """Remove a remote file. https://docs.slack.dev/reference/methods/files.remote.remove """ kwargs.update({"external_id": external_id, "file": file}) return self.api_call("files.remote.remove", http_verb="POST", params=kwargs) def files_remote_share( self, *, channels: Union[str, Sequence[str]], external_id: Optional[str] = None, file: Optional[str] = None, **kwargs, ) -> SlackResponse: """Share a remote file into a channel. https://docs.slack.dev/reference/methods/files.remote.share """ if external_id is None and file is None: raise e.SlackRequestError("Either external_id or file must be provided.") if isinstance(channels, (list, tuple)): kwargs.update({"channels": ",".join(channels)}) else: kwargs.update({"channels": channels}) kwargs.update({"external_id": external_id, "file": file}) return self.api_call("files.remote.share", http_verb="GET", params=kwargs) def files_revokePublicURL( self, *, file: str, **kwargs, ) -> SlackResponse: """Revokes public/external sharing access for a file https://docs.slack.dev/reference/methods/files.revokePublicURL """ kwargs.update({"file": file}) return self.api_call("files.revokePublicURL", params=kwargs) def files_sharedPublicURL( self, *, file: str, **kwargs, ) -> SlackResponse: """Enables a file for public/external sharing. https://docs.slack.dev/reference/methods/files.sharedPublicURL """ kwargs.update({"file": file}) return self.api_call("files.sharedPublicURL", params=kwargs) def files_upload( self, *, file: Optional[Union[str, bytes, IOBase]] = None, content: Optional[Union[str, bytes]] = None, filename: Optional[str] = None, filetype: Optional[str] = None, initial_comment: Optional[str] = None, thread_ts: Optional[str] = None, title: Optional[str] = None, channels: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """Uploads or creates a file. https://docs.slack.dev/reference/methods/files.upload """ _print_files_upload_v2_suggestion() if file is None and content is None: raise e.SlackRequestError("The file or content argument must be specified.") if file is not None and content is not None: raise e.SlackRequestError("You cannot specify both the file and the content argument.") if isinstance(channels, (list, tuple)): kwargs.update({"channels": ",".join(channels)}) else: kwargs.update({"channels": channels}) kwargs.update( { "filename": filename, "filetype": filetype, "initial_comment": initial_comment, "thread_ts": thread_ts, "title": title, } ) if file: if kwargs.get("filename") is None and isinstance(file, str): # use the local filename if filename is missing if kwargs.get("filename") is None: kwargs["filename"] = file.split(os.path.sep)[-1] return self.api_call("files.upload", files={"file": file}, data=kwargs) else: kwargs["content"] = content return self.api_call("files.upload", data=kwargs) def files_upload_v2( self, *, # for sending a single file filename: Optional[str] = None, # you can skip this only when sending along with content parameter file: Optional[Union[str, bytes, IOBase, os.PathLike]] = None, content: Optional[Union[str, bytes]] = None, title: Optional[str] = None, alt_txt: Optional[str] = None, highlight_type: Optional[str] = None, snippet_type: Optional[str] = None, # To upload multiple files at a time file_uploads: Optional[List[Dict[str, Any]]] = None, channel: Optional[str] = None, channels: Optional[List[str]] = None, initial_comment: Optional[str] = None, thread_ts: Optional[str] = None, request_file_info: bool = True, # since v3.23, this flag is no longer necessary **kwargs, ) -> SlackResponse: """This wrapper method provides an easy way to upload files using the following endpoints: - step1: https://docs.slack.dev/reference/methods/files.getUploadURLExternal - step2: "https://files.slack.com/upload/v1/..." URLs returned from files.getUploadURLExternal API - step3: https://docs.slack.dev/reference/methods/files.completeUploadExternal and https://docs.slack.dev/reference/methods/files.info """ if file is None and content is None and file_uploads is None: raise e.SlackRequestError("Any of file, content, and file_uploads must be specified.") if file is not None and content is not None: raise e.SlackRequestError("You cannot specify both the file and the content argument.") # deprecated arguments: filetype = kwargs.get("filetype") if filetype is not None: warnings.warn("The filetype parameter is no longer supported. Please remove it from the arguments.") # step1: files.getUploadURLExternal per file files: List[Dict[str, Any]] = [] if file_uploads is not None: for f in file_uploads: files.append(_to_v2_file_upload_item(f)) else: f = _to_v2_file_upload_item( { "filename": filename, "file": file, "content": content, "title": title, "alt_txt": alt_txt, "highlight_type": highlight_type, "snippet_type": snippet_type, } ) files.append(f) for f in files: url_response = self.files_getUploadURLExternal( filename=f.get("filename"), # type: ignore[arg-type] length=f.get("length"), # type: ignore[arg-type] alt_txt=f.get("alt_txt"), snippet_type=f.get("snippet_type"), token=kwargs.get("token"), ) _validate_for_legacy_client(url_response) f["file_id"] = url_response.get("file_id") # type: ignore[union-attr, unused-ignore] f["upload_url"] = url_response.get("upload_url") # type: ignore[union-attr, unused-ignore] # step2: "https://files.slack.com/upload/v1/..." per file for f in files: upload_result = self._upload_file( url=f["upload_url"], data=f["data"], logger=self._logger, timeout=self.timeout, proxy=self.proxy, ssl=self.ssl, ) if upload_result.status != 200: status = upload_result.status body = upload_result.body message = ( "Failed to upload a file " f"(status: {status}, body: {body}, filename: {f.get('filename')}, title: {f.get('title')})" ) raise e.SlackRequestError(message) # step3: files.completeUploadExternal with all the sets of (file_id + title) completion = self.files_completeUploadExternal( files=[{"id": f["file_id"], "title": f["title"], "highlight_type": f.get("highlight_type")} for f in files], channel_id=channel, channels=channels, initial_comment=initial_comment, thread_ts=thread_ts, **kwargs, ) if len(completion.get("files")) == 1: # type: ignore[arg-type, union-attr, unused-ignore] completion.data["file"] = completion.get("files")[0] # type: ignore[index, union-attr, unused-ignore] return completion def files_getUploadURLExternal( self, *, filename: str, length: int, alt_txt: Optional[str] = None, snippet_type: Optional[str] = None, **kwargs, ) -> SlackResponse: """Gets a URL for an edge external upload. https://docs.slack.dev/reference/methods/files.getUploadURLExternal """ kwargs.update( { "filename": filename, "length": length, "alt_txt": alt_txt, "snippet_type": snippet_type, } ) return self.api_call("files.getUploadURLExternal", params=kwargs) def files_completeUploadExternal( self, *, files: List[Dict[str, Optional[str]]], channel_id: Optional[str] = None, channels: Optional[List[str]] = None, initial_comment: Optional[str] = None, thread_ts: Optional[str] = None, **kwargs, ) -> SlackResponse: """Finishes an upload started with files.getUploadURLExternal. https://docs.slack.dev/reference/methods/files.completeUploadExternal """ _files = [{k: v for k, v in f.items() if v is not None} for f in files] kwargs.update( { "files": json.dumps(_files), "channel_id": channel_id, "initial_comment": initial_comment, "thread_ts": thread_ts, } ) if channels: kwargs["channels"] = ",".join(channels) return self.api_call("files.completeUploadExternal", params=kwargs) def functions_completeSuccess( self, *, function_execution_id: str, outputs: Dict[str, Any], **kwargs, ) -> SlackResponse: """Signal the successful completion of a function https://docs.slack.dev/reference/methods/functions.completeSuccess """ kwargs.update({"function_execution_id": function_execution_id, "outputs": json.dumps(outputs)}) return self.api_call("functions.completeSuccess", params=kwargs) def functions_completeError( self, *, function_execution_id: str, error: str, **kwargs, ) -> SlackResponse: """Signal the failure to execute a function https://docs.slack.dev/reference/methods/functions.completeError """ kwargs.update({"function_execution_id": function_execution_id, "error": error}) return self.api_call("functions.completeError", params=kwargs) # -------------------------- # Deprecated: groups.* # You can use conversations.* APIs instead. # https://docs.slack.dev/changelog/2020-01-deprecating-antecedents-to-the-conversations-api/ # -------------------------- def groups_archive( self, *, channel: str, **kwargs, ) -> SlackResponse: """Archives a private channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.archive", json=kwargs) def groups_create( self, *, name: str, **kwargs, ) -> SlackResponse: """Creates a private channel.""" kwargs.update({"name": name}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.create", json=kwargs) def groups_createChild( self, *, channel: str, **kwargs, ) -> SlackResponse: """Clones and archives a private channel.""" kwargs.update({"channel": channel}) return self.api_call("groups.createChild", http_verb="GET", params=kwargs) def groups_history( self, *, channel: str, **kwargs, ) -> SlackResponse: """Fetches history of messages and events from a private channel.""" kwargs.update({"channel": channel}) return self.api_call("groups.history", http_verb="GET", params=kwargs) def groups_info( self, *, channel: str, **kwargs, ) -> SlackResponse: """Gets information about a private channel.""" kwargs.update({"channel": channel}) return self.api_call("groups.info", http_verb="GET", params=kwargs) def groups_invite( self, *, channel: str, user: str, **kwargs, ) -> SlackResponse: """Invites a user to a private channel.""" kwargs.update({"channel": channel, "user": user}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.invite", json=kwargs) def groups_kick( self, *, channel: str, user: str, **kwargs, ) -> SlackResponse: """Removes a user from a private channel.""" kwargs.update({"channel": channel, "user": user}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.kick", json=kwargs) def groups_leave( self, *, channel: str, **kwargs, ) -> SlackResponse: """Leaves a private channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.leave", json=kwargs) def groups_list( self, **kwargs, ) -> SlackResponse: """Lists private channels that the calling user has access to.""" return self.api_call("groups.list", http_verb="GET", params=kwargs) def groups_mark( self, *, channel: str, ts: str, **kwargs, ) -> SlackResponse: """Sets the read cursor in a private channel.""" kwargs.update({"channel": channel, "ts": ts}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.mark", json=kwargs) def groups_open( self, *, channel: str, **kwargs, ) -> SlackResponse: """Opens a private channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.open", json=kwargs) def groups_rename( self, *, channel: str, name: str, **kwargs, ) -> SlackResponse: """Renames a private channel.""" kwargs.update({"channel": channel, "name": name}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.rename", json=kwargs) def groups_replies( self, *, channel: str, thread_ts: str, **kwargs, ) -> SlackResponse: """Retrieve a thread of messages posted to a private channel""" kwargs.update({"channel": channel, "thread_ts": thread_ts}) return self.api_call("groups.replies", http_verb="GET", params=kwargs) def groups_setPurpose( self, *, channel: str, purpose: str, **kwargs, ) -> SlackResponse: """Sets the purpose for a private channel.""" kwargs.update({"channel": channel, "purpose": purpose}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.setPurpose", json=kwargs) def groups_setTopic( self, *, channel: str, topic: str, **kwargs, ) -> SlackResponse: """Sets the topic for a private channel.""" kwargs.update({"channel": channel, "topic": topic}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.setTopic", json=kwargs) def groups_unarchive( self, *, channel: str, **kwargs, ) -> SlackResponse: """Unarchives a private channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.unarchive", json=kwargs) # -------------------------- # Deprecated: im.* # You can use conversations.* APIs instead. # https://docs.slack.dev/changelog/2020-01-deprecating-antecedents-to-the-conversations-api/ # -------------------------- def im_close( self, *, channel: str, **kwargs, ) -> SlackResponse: """Close a direct message channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("im.close", json=kwargs) def im_history( self, *, channel: str, **kwargs, ) -> SlackResponse: """Fetches history of messages and events from direct message channel.""" kwargs.update({"channel": channel}) return self.api_call("im.history", http_verb="GET", params=kwargs) def im_list( self, **kwargs, ) -> SlackResponse: """Lists direct message channels for the calling user.""" return self.api_call("im.list", http_verb="GET", params=kwargs) def im_mark( self, *, channel: str, ts: str, **kwargs, ) -> SlackResponse: """Sets the read cursor in a direct message channel.""" kwargs.update({"channel": channel, "ts": ts}) kwargs = _remove_none_values(kwargs) return self.api_call("im.mark", json=kwargs) def im_open( self, *, user: str, **kwargs, ) -> SlackResponse: """Opens a direct message channel.""" kwargs.update({"user": user}) kwargs = _remove_none_values(kwargs) return self.api_call("im.open", json=kwargs) def im_replies( self, *, channel: str, thread_ts: str, **kwargs, ) -> SlackResponse: """Retrieve a thread of messages posted to a direct message conversation""" kwargs.update({"channel": channel, "thread_ts": thread_ts}) return self.api_call("im.replies", http_verb="GET", params=kwargs) # -------------------------- def migration_exchange( self, *, users: Union[str, Sequence[str]], team_id: Optional[str] = None, to_old: Optional[bool] = None, **kwargs, ) -> SlackResponse: """For Enterprise Grid workspaces, map local user IDs to global user IDs https://docs.slack.dev/reference/methods/migration.exchange """ if isinstance(users, (list, tuple)): kwargs.update({"users": ",".join(users)}) else: kwargs.update({"users": users}) kwargs.update({"team_id": team_id, "to_old": to_old}) return self.api_call("migration.exchange", http_verb="GET", params=kwargs) # -------------------------- # Deprecated: mpim.* # You can use conversations.* APIs instead. # https://docs.slack.dev/changelog/2020-01-deprecating-antecedents-to-the-conversations-api/ # -------------------------- def mpim_close( self, *, channel: str, **kwargs, ) -> SlackResponse: """Closes a multiparty direct message channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("mpim.close", json=kwargs) def mpim_history( self, *, channel: str, **kwargs, ) -> SlackResponse: """Fetches history of messages and events from a multiparty direct message.""" kwargs.update({"channel": channel}) return self.api_call("mpim.history", http_verb="GET", params=kwargs) def mpim_list( self, **kwargs, ) -> SlackResponse: """Lists multiparty direct message channels for the calling user.""" return self.api_call("mpim.list", http_verb="GET", params=kwargs) def mpim_mark( self, *, channel: str, ts: str, **kwargs, ) -> SlackResponse: """Sets the read cursor in a multiparty direct message channel.""" kwargs.update({"channel": channel, "ts": ts}) kwargs = _remove_none_values(kwargs) return self.api_call("mpim.mark", json=kwargs) def mpim_open( self, *, users: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """This method opens a multiparty direct message.""" if isinstance(users, (list, tuple)): kwargs.update({"users": ",".join(users)}) else: kwargs.update({"users": users}) return self.api_call("mpim.open", params=kwargs) def mpim_replies( self, *, channel: str, thread_ts: str, **kwargs, ) -> SlackResponse: """Retrieve a thread of messages posted to a direct message conversation from a multiparty direct message. """ kwargs.update({"channel": channel, "thread_ts": thread_ts}) return self.api_call("mpim.replies", http_verb="GET", params=kwargs) # -------------------------- def oauth_v2_access( self, *, client_id: str, client_secret: str, # This field is required when processing the OAuth redirect URL requests # while it's absent for token rotation code: Optional[str] = None, redirect_uri: Optional[str] = None, # This field is required for token rotation grant_type: Optional[str] = None, # This field is required for token rotation refresh_token: Optional[str] = None, **kwargs, ) -> SlackResponse: """Exchanges a temporary OAuth verifier code for an access token. https://docs.slack.dev/reference/methods/oauth.v2.access """ if redirect_uri is not None: kwargs.update({"redirect_uri": redirect_uri}) if code is not None: kwargs.update({"code": code}) if grant_type is not None: kwargs.update({"grant_type": grant_type}) if refresh_token is not None: kwargs.update({"refresh_token": refresh_token}) return self.api_call( "oauth.v2.access", data=kwargs, auth={"client_id": client_id, "client_secret": client_secret}, ) def oauth_access( self, *, client_id: str, client_secret: str, code: str, redirect_uri: Optional[str] = None, **kwargs, ) -> SlackResponse: """Exchanges a temporary OAuth verifier code for an access token. https://docs.slack.dev/reference/methods/oauth.access """ if redirect_uri is not None: kwargs.update({"redirect_uri": redirect_uri}) kwargs.update({"code": code}) return self.api_call( "oauth.access", data=kwargs, auth={"client_id": client_id, "client_secret": client_secret}, ) def oauth_v2_exchange( self, *, token: str, client_id: str, client_secret: str, **kwargs, ) -> SlackResponse: """Exchanges a legacy access token for a new expiring access token and refresh token https://docs.slack.dev/reference/methods/oauth.v2.exchange """ kwargs.update({"client_id": client_id, "client_secret": client_secret, "token": token}) return self.api_call("oauth.v2.exchange", params=kwargs) def openid_connect_token( self, client_id: str, client_secret: str, code: Optional[str] = None, redirect_uri: Optional[str] = None, grant_type: Optional[str] = None, refresh_token: Optional[str] = None, **kwargs, ) -> SlackResponse: """Exchanges a temporary OAuth verifier code for an access token for Sign in with Slack. https://docs.slack.dev/reference/methods/openid.connect.token """ if redirect_uri is not None: kwargs.update({"redirect_uri": redirect_uri}) if code is not None: kwargs.update({"code": code}) if grant_type is not None: kwargs.update({"grant_type": grant_type}) if refresh_token is not None: kwargs.update({"refresh_token": refresh_token}) return self.api_call( "openid.connect.token", data=kwargs, auth={"client_id": client_id, "client_secret": client_secret}, ) def openid_connect_userInfo( self, **kwargs, ) -> SlackResponse: """Get the identity of a user who has authorized Sign in with Slack. https://docs.slack.dev/reference/methods/openid.connect.userInfo """ return self.api_call("openid.connect.userInfo", params=kwargs) def pins_add( self, *, channel: str, timestamp: Optional[str] = None, **kwargs, ) -> SlackResponse: """Pins an item to a channel. https://docs.slack.dev/reference/methods/pins.add """ kwargs.update({"channel": channel, "timestamp": timestamp}) return self.api_call("pins.add", params=kwargs) def pins_list( self, *, channel: str, **kwargs, ) -> SlackResponse: """Lists items pinned to a channel. https://docs.slack.dev/reference/methods/pins.list """ kwargs.update({"channel": channel}) return self.api_call("pins.list", http_verb="GET", params=kwargs) def pins_remove( self, *, channel: str, timestamp: Optional[str] = None, **kwargs, ) -> SlackResponse: """Un-pins an item from a channel. https://docs.slack.dev/reference/methods/pins.remove """ kwargs.update({"channel": channel, "timestamp": timestamp}) return self.api_call("pins.remove", params=kwargs) def reactions_add( self, *, channel: str, name: str, timestamp: str, **kwargs, ) -> SlackResponse: """Adds a reaction to an item. https://docs.slack.dev/reference/methods/reactions.add """ kwargs.update({"channel": channel, "name": name, "timestamp": timestamp}) return self.api_call("reactions.add", params=kwargs) def reactions_get( self, *, channel: Optional[str] = None, file: Optional[str] = None, file_comment: Optional[str] = None, full: Optional[bool] = None, timestamp: Optional[str] = None, **kwargs, ) -> SlackResponse: """Gets reactions for an item. https://docs.slack.dev/reference/methods/reactions.get """ kwargs.update( { "channel": channel, "file": file, "file_comment": file_comment, "full": full, "timestamp": timestamp, } ) return self.api_call("reactions.get", http_verb="GET", params=kwargs) def reactions_list( self, *, count: Optional[int] = None, cursor: Optional[str] = None, full: Optional[bool] = None, limit: Optional[int] = None, page: Optional[int] = None, team_id: Optional[str] = None, user: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists reactions made by a user. https://docs.slack.dev/reference/methods/reactions.list """ kwargs.update( { "count": count, "cursor": cursor, "full": full, "limit": limit, "page": page, "team_id": team_id, "user": user, } ) return self.api_call("reactions.list", http_verb="GET", params=kwargs) def reactions_remove( self, *, name: str, channel: Optional[str] = None, file: Optional[str] = None, file_comment: Optional[str] = None, timestamp: Optional[str] = None, **kwargs, ) -> SlackResponse: """Removes a reaction from an item. https://docs.slack.dev/reference/methods/reactions.remove """ kwargs.update( { "name": name, "channel": channel, "file": file, "file_comment": file_comment, "timestamp": timestamp, } ) return self.api_call("reactions.remove", params=kwargs) def reminders_add( self, *, text: str, time: str, team_id: Optional[str] = None, user: Optional[str] = None, recurrence: Optional[str] = None, **kwargs, ) -> SlackResponse: """Creates a reminder. https://docs.slack.dev/reference/methods/reminders.add """ kwargs.update( { "text": text, "time": time, "team_id": team_id, "user": user, "recurrence": recurrence, } ) return self.api_call("reminders.add", params=kwargs) def reminders_complete( self, *, reminder: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Marks a reminder as complete. https://docs.slack.dev/reference/methods/reminders.complete """ kwargs.update({"reminder": reminder, "team_id": team_id}) return self.api_call("reminders.complete", params=kwargs) def reminders_delete( self, *, reminder: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Deletes a reminder. https://docs.slack.dev/reference/methods/reminders.delete """ kwargs.update({"reminder": reminder, "team_id": team_id}) return self.api_call("reminders.delete", params=kwargs) def reminders_info( self, *, reminder: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Gets information about a reminder. https://docs.slack.dev/reference/methods/reminders.info """ kwargs.update({"reminder": reminder, "team_id": team_id}) return self.api_call("reminders.info", http_verb="GET", params=kwargs) def reminders_list( self, *, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists all reminders created by or for a given user. https://docs.slack.dev/reference/methods/reminders.list """ kwargs.update({"team_id": team_id}) return self.api_call("reminders.list", http_verb="GET", params=kwargs) def rtm_connect( self, *, batch_presence_aware: Optional[bool] = None, presence_sub: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Starts a Real Time Messaging session. https://docs.slack.dev/reference/methods/rtm.connect """ kwargs.update({"batch_presence_aware": batch_presence_aware, "presence_sub": presence_sub}) return self.api_call("rtm.connect", http_verb="GET", params=kwargs) def rtm_start( self, *, batch_presence_aware: Optional[bool] = None, include_locale: Optional[bool] = None, mpim_aware: Optional[bool] = None, no_latest: Optional[bool] = None, no_unreads: Optional[bool] = None, presence_sub: Optional[bool] = None, simple_latest: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Starts a Real Time Messaging session. https://docs.slack.dev/reference/methods/rtm.start """ kwargs.update( { "batch_presence_aware": batch_presence_aware, "include_locale": include_locale, "mpim_aware": mpim_aware, "no_latest": no_latest, "no_unreads": no_unreads, "presence_sub": presence_sub, "simple_latest": simple_latest, } ) return self.api_call("rtm.start", http_verb="GET", params=kwargs) def search_all( self, *, query: str, count: Optional[int] = None, highlight: Optional[bool] = None, page: Optional[int] = None, sort: Optional[str] = None, sort_dir: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Searches for messages and files matching a query. https://docs.slack.dev/reference/methods/search.all """ kwargs.update( { "query": query, "count": count, "highlight": highlight, "page": page, "sort": sort, "sort_dir": sort_dir, "team_id": team_id, } ) return self.api_call("search.all", http_verb="GET", params=kwargs) def search_files( self, *, query: str, count: Optional[int] = None, highlight: Optional[bool] = None, page: Optional[int] = None, sort: Optional[str] = None, sort_dir: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Searches for files matching a query. https://docs.slack.dev/reference/methods/search.files """ kwargs.update( { "query": query, "count": count, "highlight": highlight, "page": page, "sort": sort, "sort_dir": sort_dir, "team_id": team_id, } ) return self.api_call("search.files", http_verb="GET", params=kwargs) def search_messages( self, *, query: str, count: Optional[int] = None, cursor: Optional[str] = None, highlight: Optional[bool] = None, page: Optional[int] = None, sort: Optional[str] = None, sort_dir: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Searches for messages matching a query. https://docs.slack.dev/reference/methods/search.messages """ kwargs.update( { "query": query, "count": count, "cursor": cursor, "highlight": highlight, "page": page, "sort": sort, "sort_dir": sort_dir, "team_id": team_id, } ) return self.api_call("search.messages", http_verb="GET", params=kwargs) def slackLists_access_delete( self, *, list_id: str, channel_ids: Optional[List[str]] = None, user_ids: Optional[List[str]] = None, **kwargs, ) -> SlackResponse: """Revoke access to a List for specified entities. https://docs.slack.dev/reference/methods/slackLists.access.delete """ kwargs.update({"list_id": list_id, "channel_ids": channel_ids, "user_ids": user_ids}) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.access.delete", json=kwargs) def slackLists_access_set( self, *, list_id: str, access_level: str, channel_ids: Optional[List[str]] = None, user_ids: Optional[List[str]] = None, **kwargs, ) -> SlackResponse: """Set the access level to a List for specified entities. https://docs.slack.dev/reference/methods/slackLists.access.set """ kwargs.update({"list_id": list_id, "access_level": access_level, "channel_ids": channel_ids, "user_ids": user_ids}) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.access.set", json=kwargs) def slackLists_create( self, *, name: str, description_blocks: Optional[Union[str, Sequence[Union[Dict, RichTextBlock]]]] = None, schema: Optional[List[Dict[str, Any]]] = None, copy_from_list_id: Optional[str] = None, include_copied_list_records: Optional[bool] = None, todo_mode: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Creates a List. https://docs.slack.dev/reference/methods/slackLists.create """ kwargs.update( { "name": name, "description_blocks": description_blocks, "schema": schema, "copy_from_list_id": copy_from_list_id, "include_copied_list_records": include_copied_list_records, "todo_mode": todo_mode, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.create", json=kwargs) def slackLists_download_get( self, *, list_id: str, job_id: str, **kwargs, ) -> SlackResponse: """Retrieve List download URL from an export job to download List contents. https://docs.slack.dev/reference/methods/slackLists.download.get """ kwargs.update( { "list_id": list_id, "job_id": job_id, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.download.get", json=kwargs) def slackLists_download_start( self, *, list_id: str, include_archived: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Initiate a job to export List contents. https://docs.slack.dev/reference/methods/slackLists.download.start """ kwargs.update( { "list_id": list_id, "include_archived": include_archived, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.download.start", json=kwargs) def slackLists_items_create( self, *, list_id: str, duplicated_item_id: Optional[str] = None, parent_item_id: Optional[str] = None, initial_fields: Optional[List[Dict[str, Any]]] = None, **kwargs, ) -> SlackResponse: """Add a new item to an existing List. https://docs.slack.dev/reference/methods/slackLists.items.create """ kwargs.update( { "list_id": list_id, "duplicated_item_id": duplicated_item_id, "parent_item_id": parent_item_id, "initial_fields": initial_fields, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.items.create", json=kwargs) def slackLists_items_delete( self, *, list_id: str, id: str, **kwargs, ) -> SlackResponse: """Deletes an item from an existing List. https://docs.slack.dev/reference/methods/slackLists.items.delete """ kwargs.update( { "list_id": list_id, "id": id, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.items.delete", json=kwargs) def slackLists_items_deleteMultiple( self, *, list_id: str, ids: List[str], **kwargs, ) -> SlackResponse: """Deletes multiple items from an existing List. https://docs.slack.dev/reference/methods/slackLists.items.deleteMultiple """ kwargs.update( { "list_id": list_id, "ids": ids, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.items.deleteMultiple", json=kwargs) def slackLists_items_info( self, *, list_id: str, id: str, include_is_subscribed: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Get a row from a List. https://docs.slack.dev/reference/methods/slackLists.items.info """ kwargs.update( { "list_id": list_id, "id": id, "include_is_subscribed": include_is_subscribed, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.items.info", json=kwargs) def slackLists_items_list( self, *, list_id: str, limit: Optional[int] = None, cursor: Optional[str] = None, archived: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Get records from a List. https://docs.slack.dev/reference/methods/slackLists.items.list """ kwargs.update( { "list_id": list_id, "limit": limit, "cursor": cursor, "archived": archived, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.items.list", json=kwargs) def slackLists_items_update( self, *, list_id: str, cells: List[Dict[str, Any]], **kwargs, ) -> SlackResponse: """Updates cells in a List. https://docs.slack.dev/reference/methods/slackLists.items.update """ kwargs.update( { "list_id": list_id, "cells": cells, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.items.update", json=kwargs) def slackLists_update( self, *, id: str, name: Optional[str] = None, description_blocks: Optional[Union[str, Sequence[Union[Dict, RichTextBlock]]]] = None, todo_mode: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Update a List. https://docs.slack.dev/reference/methods/slackLists.update """ kwargs.update( { "id": id, "name": name, "description_blocks": description_blocks, "todo_mode": todo_mode, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.update", json=kwargs) def stars_add( self, *, channel: Optional[str] = None, file: Optional[str] = None, file_comment: Optional[str] = None, timestamp: Optional[str] = None, **kwargs, ) -> SlackResponse: """Adds a star to an item. https://docs.slack.dev/reference/methods/stars.add """ kwargs.update( { "channel": channel, "file": file, "file_comment": file_comment, "timestamp": timestamp, } ) return self.api_call("stars.add", params=kwargs) def stars_list( self, *, count: Optional[int] = None, cursor: Optional[str] = None, limit: Optional[int] = None, page: Optional[int] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists stars for a user. https://docs.slack.dev/reference/methods/stars.list """ kwargs.update( { "count": count, "cursor": cursor, "limit": limit, "page": page, "team_id": team_id, } ) return self.api_call("stars.list", http_verb="GET", params=kwargs) def stars_remove( self, *, channel: Optional[str] = None, file: Optional[str] = None, file_comment: Optional[str] = None, timestamp: Optional[str] = None, **kwargs, ) -> SlackResponse: """Removes a star from an item. https://docs.slack.dev/reference/methods/stars.remove """ kwargs.update( { "channel": channel, "file": file, "file_comment": file_comment, "timestamp": timestamp, } ) return self.api_call("stars.remove", params=kwargs) def team_accessLogs( self, *, before: Optional[Union[int, str]] = None, count: Optional[Union[int, str]] = None, page: Optional[Union[int, str]] = None, team_id: Optional[str] = None, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Gets the access logs for the current team. https://docs.slack.dev/reference/methods/team.accessLogs """ kwargs.update( { "before": before, "count": count, "page": page, "team_id": team_id, "cursor": cursor, "limit": limit, } ) return self.api_call("team.accessLogs", http_verb="GET", params=kwargs) def team_billableInfo( self, *, team_id: Optional[str] = None, user: Optional[str] = None, **kwargs, ) -> SlackResponse: """Gets billable users information for the current team. https://docs.slack.dev/reference/methods/team.billableInfo """ kwargs.update({"team_id": team_id, "user": user}) return self.api_call("team.billableInfo", http_verb="GET", params=kwargs) def team_billing_info( self, **kwargs, ) -> SlackResponse: """Reads a workspace's billing plan information. https://docs.slack.dev/reference/methods/team.billing.info """ return self.api_call("team.billing.info", params=kwargs) def team_externalTeams_disconnect( self, *, target_team: str, **kwargs, ) -> SlackResponse: """Disconnects an external organization. https://docs.slack.dev/reference/methods/team.externalTeams.disconnect """ kwargs.update( { "target_team": target_team, } ) return self.api_call("team.externalTeams.disconnect", params=kwargs) def team_externalTeams_list( self, *, connection_status_filter: Optional[str] = None, slack_connect_pref_filter: Optional[Sequence[str]] = None, sort_direction: Optional[str] = None, sort_field: Optional[str] = None, workspace_filter: Optional[Sequence[str]] = None, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Returns a list of all the external teams connected and details about the connection. https://docs.slack.dev/reference/methods/team.externalTeams.list """ kwargs.update( { "connection_status_filter": connection_status_filter, "sort_direction": sort_direction, "sort_field": sort_field, "cursor": cursor, "limit": limit, } ) if slack_connect_pref_filter is not None: if isinstance(slack_connect_pref_filter, (list, tuple)): kwargs.update({"slack_connect_pref_filter": ",".join(slack_connect_pref_filter)}) else: kwargs.update({"slack_connect_pref_filter": slack_connect_pref_filter}) if workspace_filter is not None: if isinstance(workspace_filter, (list, tuple)): kwargs.update({"workspace_filter": ",".join(workspace_filter)}) else: kwargs.update({"workspace_filter": workspace_filter}) return self.api_call("team.externalTeams.list", http_verb="GET", params=kwargs) def team_info( self, *, team: Optional[str] = None, domain: Optional[str] = None, **kwargs, ) -> SlackResponse: """Gets information about the current team. https://docs.slack.dev/reference/methods/team.info """ kwargs.update({"team": team, "domain": domain}) return self.api_call("team.info", http_verb="GET", params=kwargs) def team_integrationLogs( self, *, app_id: Optional[str] = None, change_type: Optional[str] = None, count: Optional[Union[int, str]] = None, page: Optional[Union[int, str]] = None, service_id: Optional[str] = None, team_id: Optional[str] = None, user: Optional[str] = None, **kwargs, ) -> SlackResponse: """Gets the integration logs for the current team. https://docs.slack.dev/reference/methods/team.integrationLogs """ kwargs.update( { "app_id": app_id, "change_type": change_type, "count": count, "page": page, "service_id": service_id, "team_id": team_id, "user": user, } ) return self.api_call("team.integrationLogs", http_verb="GET", params=kwargs) def team_profile_get( self, *, visibility: Optional[str] = None, **kwargs, ) -> SlackResponse: """Retrieve a team's profile. https://docs.slack.dev/reference/methods/team.profile.get """ kwargs.update({"visibility": visibility}) return self.api_call("team.profile.get", http_verb="GET", params=kwargs) def team_preferences_list( self, **kwargs, ) -> SlackResponse: """Retrieve a list of a workspace's team preferences. https://docs.slack.dev/reference/methods/team.preferences.list """ return self.api_call("team.preferences.list", params=kwargs) def usergroups_create( self, *, name: str, channels: Optional[Union[str, Sequence[str]]] = None, description: Optional[str] = None, handle: Optional[str] = None, include_count: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Create a User Group https://docs.slack.dev/reference/methods/usergroups.create """ kwargs.update( { "name": name, "description": description, "handle": handle, "include_count": include_count, "team_id": team_id, } ) if isinstance(channels, (list, tuple)): kwargs.update({"channels": ",".join(channels)}) else: kwargs.update({"channels": channels}) return self.api_call("usergroups.create", params=kwargs) def usergroups_disable( self, *, usergroup: str, include_count: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Disable an existing User Group https://docs.slack.dev/reference/methods/usergroups.disable """ kwargs.update({"usergroup": usergroup, "include_count": include_count, "team_id": team_id}) return self.api_call("usergroups.disable", params=kwargs) def usergroups_enable( self, *, usergroup: str, include_count: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Enable a User Group https://docs.slack.dev/reference/methods/usergroups.enable """ kwargs.update({"usergroup": usergroup, "include_count": include_count, "team_id": team_id}) return self.api_call("usergroups.enable", params=kwargs) def usergroups_list( self, *, include_count: Optional[bool] = None, include_disabled: Optional[bool] = None, include_users: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List all User Groups for a team https://docs.slack.dev/reference/methods/usergroups.list """ kwargs.update( { "include_count": include_count, "include_disabled": include_disabled, "include_users": include_users, "team_id": team_id, } ) return self.api_call("usergroups.list", http_verb="GET", params=kwargs) def usergroups_update( self, *, usergroup: str, channels: Optional[Union[str, Sequence[str]]] = None, description: Optional[str] = None, handle: Optional[str] = None, include_count: Optional[bool] = None, name: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Update an existing User Group https://docs.slack.dev/reference/methods/usergroups.update """ kwargs.update( { "usergroup": usergroup, "description": description, "handle": handle, "include_count": include_count, "name": name, "team_id": team_id, } ) if isinstance(channels, (list, tuple)): kwargs.update({"channels": ",".join(channels)}) else: kwargs.update({"channels": channels}) return self.api_call("usergroups.update", params=kwargs) def usergroups_users_list( self, *, usergroup: str, include_disabled: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List all users in a User Group https://docs.slack.dev/reference/methods/usergroups.users.list """ kwargs.update( { "usergroup": usergroup, "include_disabled": include_disabled, "team_id": team_id, } ) return self.api_call("usergroups.users.list", http_verb="GET", params=kwargs) def usergroups_users_update( self, *, usergroup: str, users: Union[str, Sequence[str]], include_count: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Update the list of users for a User Group https://docs.slack.dev/reference/methods/usergroups.users.update """ kwargs.update( { "usergroup": usergroup, "include_count": include_count, "team_id": team_id, } ) if isinstance(users, (list, tuple)): kwargs.update({"users": ",".join(users)}) else: kwargs.update({"users": users}) return self.api_call("usergroups.users.update", params=kwargs) def users_conversations( self, *, cursor: Optional[str] = None, exclude_archived: Optional[bool] = None, limit: Optional[int] = None, team_id: Optional[str] = None, types: Optional[Union[str, Sequence[str]]] = None, user: Optional[str] = None, **kwargs, ) -> SlackResponse: """List conversations the calling user may access. https://docs.slack.dev/reference/methods/users.conversations """ kwargs.update( { "cursor": cursor, "exclude_archived": exclude_archived, "limit": limit, "team_id": team_id, "user": user, } ) if isinstance(types, (list, tuple)): kwargs.update({"types": ",".join(types)}) else: kwargs.update({"types": types}) return self.api_call("users.conversations", http_verb="GET", params=kwargs) def users_deletePhoto( self, **kwargs, ) -> SlackResponse: """Delete the user profile photo https://docs.slack.dev/reference/methods/users.deletePhoto """ return self.api_call("users.deletePhoto", http_verb="GET", params=kwargs) def users_getPresence( self, *, user: str, **kwargs, ) -> SlackResponse: """Gets user presence information. https://docs.slack.dev/reference/methods/users.getPresence """ kwargs.update({"user": user}) return self.api_call("users.getPresence", http_verb="GET", params=kwargs) def users_identity( self, **kwargs, ) -> SlackResponse: """Get a user's identity. https://docs.slack.dev/reference/methods/users.identity """ return self.api_call("users.identity", http_verb="GET", params=kwargs) def users_info( self, *, user: str, include_locale: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Gets information about a user. https://docs.slack.dev/reference/methods/users.info """ kwargs.update({"user": user, "include_locale": include_locale}) return self.api_call("users.info", http_verb="GET", params=kwargs) def users_list( self, *, cursor: Optional[str] = None, include_locale: Optional[bool] = None, limit: Optional[int] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists all users in a Slack team. https://docs.slack.dev/reference/methods/users.list """ kwargs.update( { "cursor": cursor, "include_locale": include_locale, "limit": limit, "team_id": team_id, } ) return self.api_call("users.list", http_verb="GET", params=kwargs) def users_lookupByEmail( self, *, email: str, **kwargs, ) -> SlackResponse: """Find a user with an email address. https://docs.slack.dev/reference/methods/users.lookupByEmail """ kwargs.update({"email": email}) return self.api_call("users.lookupByEmail", http_verb="GET", params=kwargs) def users_setPhoto( self, *, image: Union[str, IOBase], crop_w: Optional[Union[int, str]] = None, crop_x: Optional[Union[int, str]] = None, crop_y: Optional[Union[int, str]] = None, **kwargs, ) -> SlackResponse: """Set the user profile photo https://docs.slack.dev/reference/methods/users.setPhoto """ kwargs.update({"crop_w": crop_w, "crop_x": crop_x, "crop_y": crop_y}) return self.api_call("users.setPhoto", files={"image": image}, data=kwargs) def users_setPresence( self, *, presence: str, **kwargs, ) -> SlackResponse: """Manually sets user presence. https://docs.slack.dev/reference/methods/users.setPresence """ kwargs.update({"presence": presence}) return self.api_call("users.setPresence", params=kwargs) def users_discoverableContacts_lookup( self, email: str, **kwargs, ) -> SlackResponse: """Lookup an email address to see if someone is on Slack https://docs.slack.dev/reference/methods/users.discoverableContacts.lookup """ kwargs.update({"email": email}) return self.api_call("users.discoverableContacts.lookup", params=kwargs) def users_profile_get( self, *, user: Optional[str] = None, include_labels: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Retrieves a user's profile information. https://docs.slack.dev/reference/methods/users.profile.get """ kwargs.update({"user": user, "include_labels": include_labels}) return self.api_call("users.profile.get", http_verb="GET", params=kwargs) def users_profile_set( self, *, name: Optional[str] = None, value: Optional[str] = None, user: Optional[str] = None, profile: Optional[Dict] = None, **kwargs, ) -> SlackResponse: """Set the profile information for a user. https://docs.slack.dev/reference/methods/users.profile.set """ kwargs.update( { "name": name, "profile": profile, "user": user, "value": value, } ) kwargs = _remove_none_values(kwargs) # NOTE: Intentionally using json for the "profile" parameter return self.api_call("users.profile.set", json=kwargs) def views_open( self, *, trigger_id: Optional[str] = None, interactivity_pointer: Optional[str] = None, view: Union[dict, View], **kwargs, ) -> SlackResponse: """Open a view for a user. https://docs.slack.dev/reference/methods/views.open See https://docs.slack.dev/surfaces/modals/ for details. """ kwargs.update({"trigger_id": trigger_id, "interactivity_pointer": interactivity_pointer}) if isinstance(view, View): kwargs.update({"view": view.to_dict()}) else: kwargs.update({"view": view}) kwargs = _remove_none_values(kwargs) # NOTE: Intentionally using json for the "view" parameter return self.api_call("views.open", json=kwargs) def views_push( self, *, trigger_id: Optional[str] = None, interactivity_pointer: Optional[str] = None, view: Union[dict, View], **kwargs, ) -> SlackResponse: """Push a view onto the stack of a root view. Push a new view onto the existing view stack by passing a view payload and a valid trigger_id generated from an interaction within the existing modal. Read the modals documentation (https://docs.slack.dev/surfaces/modals/) to learn more about the lifecycle and intricacies of views. https://docs.slack.dev/reference/methods/views.push """ kwargs.update({"trigger_id": trigger_id, "interactivity_pointer": interactivity_pointer}) if isinstance(view, View): kwargs.update({"view": view.to_dict()}) else: kwargs.update({"view": view}) kwargs = _remove_none_values(kwargs) # NOTE: Intentionally using json for the "view" parameter return self.api_call("views.push", json=kwargs) def views_update( self, *, view: Union[dict, View], external_id: Optional[str] = None, view_id: Optional[str] = None, hash: Optional[str] = None, **kwargs, ) -> SlackResponse: """Update an existing view. Update a view by passing a new view definition along with the view_id returned in views.open or the external_id. See the modals documentation (https://docs.slack.dev/surfaces/modals/#updating_views) to learn more about updating views and avoiding race conditions with the hash argument. https://docs.slack.dev/reference/methods/views.update """ if isinstance(view, View): kwargs.update({"view": view.to_dict()}) else: kwargs.update({"view": view}) if external_id: kwargs.update({"external_id": external_id}) elif view_id: kwargs.update({"view_id": view_id}) else: raise e.SlackRequestError("Either view_id or external_id is required.") kwargs.update({"hash": hash}) kwargs = _remove_none_values(kwargs) # NOTE: Intentionally using json for the "view" parameter return self.api_call("views.update", json=kwargs) def views_publish( self, *, user_id: str, view: Union[dict, View], hash: Optional[str] = None, **kwargs, ) -> SlackResponse: """Publish a static view for a User. Create or update the view that comprises an app's Home tab (https://docs.slack.dev/surfaces/app-home/) https://docs.slack.dev/reference/methods/views.publish """ kwargs.update({"user_id": user_id, "hash": hash}) if isinstance(view, View): kwargs.update({"view": view.to_dict()}) else: kwargs.update({"view": view}) kwargs = _remove_none_values(kwargs) # NOTE: Intentionally using json for the "view" parameter return self.api_call("views.publish", json=kwargs) def workflows_featured_add( self, *, channel_id: str, trigger_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Add featured workflows to a channel. https://docs.slack.dev/reference/methods/workflows.featured.add """ kwargs.update({"channel_id": channel_id}) if isinstance(trigger_ids, (list, tuple)): kwargs.update({"trigger_ids": ",".join(trigger_ids)}) else: kwargs.update({"trigger_ids": trigger_ids}) return self.api_call("workflows.featured.add", params=kwargs) def workflows_featured_list( self, *, channel_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """List the featured workflows for specified channels. https://docs.slack.dev/reference/methods/workflows.featured.list """ if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) return self.api_call("workflows.featured.list", params=kwargs) def workflows_featured_remove( self, *, channel_id: str, trigger_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Remove featured workflows from a channel. https://docs.slack.dev/reference/methods/workflows.featured.remove """ kwargs.update({"channel_id": channel_id}) if isinstance(trigger_ids, (list, tuple)): kwargs.update({"trigger_ids": ",".join(trigger_ids)}) else: kwargs.update({"trigger_ids": trigger_ids}) return self.api_call("workflows.featured.remove", params=kwargs) def workflows_featured_set( self, *, channel_id: str, trigger_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Set featured workflows for a channel. https://docs.slack.dev/reference/methods/workflows.featured.set """ kwargs.update({"channel_id": channel_id}) if isinstance(trigger_ids, (list, tuple)): kwargs.update({"trigger_ids": ",".join(trigger_ids)}) else: kwargs.update({"trigger_ids": trigger_ids}) return self.api_call("workflows.featured.set", params=kwargs) def workflows_stepCompleted( self, *, workflow_step_execute_id: str, outputs: Optional[dict] = None, **kwargs, ) -> SlackResponse: """Indicate a successful outcome of a workflow step's execution. https://docs.slack.dev/reference/methods/workflows.stepCompleted """ kwargs.update({"workflow_step_execute_id": workflow_step_execute_id}) if outputs is not None: kwargs.update({"outputs": outputs}) kwargs = _remove_none_values(kwargs) # NOTE: Intentionally using json for the "outputs" parameter return self.api_call("workflows.stepCompleted", json=kwargs) def workflows_stepFailed( self, *, workflow_step_execute_id: str, error: Dict[str, str], **kwargs, ) -> SlackResponse: """Indicate an unsuccessful outcome of a workflow step's execution. https://docs.slack.dev/reference/methods/workflows.stepFailed """ kwargs.update( { "workflow_step_execute_id": workflow_step_execute_id, "error": error, } ) kwargs = _remove_none_values(kwargs) # NOTE: Intentionally using json for the "error" parameter return self.api_call("workflows.stepFailed", json=kwargs) def workflows_updateStep( self, *, workflow_step_edit_id: str, inputs: Optional[Dict[str, Any]] = None, outputs: Optional[List[Dict[str, str]]] = None, **kwargs, ) -> SlackResponse: """Update the configuration for a workflow extension step. https://docs.slack.dev/reference/methods/workflows.updateStep """ kwargs.update({"workflow_step_edit_id": workflow_step_edit_id}) if inputs is not None: kwargs.update({"inputs": inputs}) if outputs is not None: kwargs.update({"outputs": outputs}) kwargs = _remove_none_values(kwargs) # NOTE: Intentionally using json for the "inputs" / "outputs" parameters return self.api_call("workflows.updateStep", json=kwargs) ``` A WebClient allows apps to communicate with the Slack Platform's Web API. [https://docs.slack.dev/reference/methods](https://docs.slack.dev/reference/methods) The Slack Web API is an interface for querying information from and enacting change in a Slack workspace. This client handles constructing and sending HTTP requests to Slack as well as parsing any responses received into a `[SlackResponse](#slack_sdk.web.SlackResponse "slack_sdk.web.SlackResponse")`. ## Attributes **`token`** : `str` A string specifying an `xoxp-*` or `xoxb-*` token. **`base_url`** : `str` A string representing the Slack API base URL. Default is `'https://slack.com/api/'` **`timeout`** : `int` The maximum number of seconds the client will wait to connect and receive a response from Slack. Default is 30 seconds. **`ssl`** : `SSLContext` An [`ssl.SSLContext`](https://docs.python.org/3/library/ssl.html#ssl.SSLContext) instance, helpful for specifying your own custom certificate chain. **`proxy`** : `str` String representing a fully-qualified URL to a proxy through which to route all requests to the Slack API. Even if this parameter is not specified, if any of the following environment variables are present, they will be loaded into this parameter: `HTTPS_PROXY`, `https_proxy`, `HTTP_PROXY` or `http_proxy`. **`headers`** : `dict` Additional request headers to attach to all requests. ## Methods `api_call`: Constructs a request and executes the API call to Slack. Example of recommended usage: ```python import os from slack_sdk import WebClient client = WebClient(token=os.environ['SLACK_API_TOKEN']) response = client.chat_postMessage( channel='#random', text="Hello world!") assert response["ok"] assert response["message"]["text"] == "Hello world!" ``` Example manually creating an API request: ```python import os from slack_sdk import WebClient client = WebClient(token=os.environ['SLACK_API_TOKEN']) response = client.api_call( api_method='chat.postMessage', json={'channel': '#random','text': "Hello world!"} ) assert response["ok"] assert response["message"]["text"] == "Hello world!" ``` ## Note Any attributes or methods prefixed with \_underscores are intended to be "private" internal use only. They may be changed or removed at anytime. ### Ancestors * [BaseClient](base_client.html#slack_sdk.web.base_client.BaseClient "slack_sdk.web.base_client.BaseClient") ### Methods `def admin_analytics_getFile(self, *, type: str, date: str | None = None, metadata_only: bool | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_analytics_getFile( self, *, type: str, date: Optional[str] = None, metadata_only: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Retrieve analytics data for a given date, presented as a compressed JSON file https://docs.slack.dev/reference/methods/admin.analytics.getFile """ kwargs.update({"type": type}) if date is not None: kwargs.update({"date": date}) if metadata_only is not None: kwargs.update({"metadata_only": metadata_only}) return self.api_call("admin.analytics.getFile", params=kwargs) ``` Retrieve analytics data for a given date, presented as a compressed JSON file [https://docs.slack.dev/reference/methods/admin.analytics.getFile](https://docs.slack.dev/reference/methods/admin.analytics.getFile) `def admin_apps_activities_list(self, *, app_id: str | None = None, component_id: str | None = None, component_type: str | None = None, log_event_type: str | None = None, max_date_created: int | None = None, min_date_created: int | None = None, min_log_level: str | None = None, sort_direction: str | None = None, source: str | None = None, team_id: str | None = None, trace_id: str | None = None, cursor: str | None = None, limit: int | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_apps_activities_list( self, *, app_id: Optional[str] = None, component_id: Optional[str] = None, component_type: Optional[str] = None, log_event_type: Optional[str] = None, max_date_created: Optional[int] = None, min_date_created: Optional[int] = None, min_log_level: Optional[str] = None, sort_direction: Optional[str] = None, source: Optional[str] = None, team_id: Optional[str] = None, trace_id: Optional[str] = None, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Get logs for a specified team/org https://docs.slack.dev/reference/methods/admin.apps.activities.list """ kwargs.update( { "app_id": app_id, "component_id": component_id, "component_type": component_type, "log_event_type": log_event_type, "max_date_created": max_date_created, "min_date_created": min_date_created, "min_log_level": min_log_level, "sort_direction": sort_direction, "source": source, "team_id": team_id, "trace_id": trace_id, "cursor": cursor, "limit": limit, } ) return self.api_call("admin.apps.activities.list", params=kwargs) ``` Get logs for a specified team/org [https://docs.slack.dev/reference/methods/admin.apps.activities.list](https://docs.slack.dev/reference/methods/admin.apps.activities.list) `def admin_apps_approve(self, *, app_id: str | None = None, request_id: str | None = None, enterprise_id: str | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_apps_approve( self, *, app_id: Optional[str] = None, request_id: Optional[str] = None, enterprise_id: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Approve an app for installation on a workspace. Either app_id or request_id is required. These IDs can be obtained either directly via the app_requested event, or by the admin.apps.requests.list method. https://docs.slack.dev/reference/methods/admin.apps.approve """ if app_id: kwargs.update({"app_id": app_id}) elif request_id: kwargs.update({"request_id": request_id}) else: raise e.SlackRequestError("The app_id or request_id argument must be specified.") kwargs.update( { "enterprise_id": enterprise_id, "team_id": team_id, } ) return self.api_call("admin.apps.approve", params=kwargs) ``` Approve an app for installation on a workspace. Either app\_id or request\_id is required. These IDs can be obtained either directly via the app\_requested event, or by the admin.apps.requests.list method. [https://docs.slack.dev/reference/methods/admin.apps.approve](https://docs.slack.dev/reference/methods/admin.apps.approve) `def admin_apps_approved_list(self, *, cursor: str | None = None, limit: int | None = None, enterprise_id: str | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_apps_approved_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, enterprise_id: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List approved apps for an org or workspace. https://docs.slack.dev/reference/methods/admin.apps.approved.list """ kwargs.update( { "cursor": cursor, "limit": limit, "enterprise_id": enterprise_id, "team_id": team_id, } ) return self.api_call("admin.apps.approved.list", http_verb="GET", params=kwargs) ``` List approved apps for an org or workspace. [https://docs.slack.dev/reference/methods/admin.apps.approved.list](https://docs.slack.dev/reference/methods/admin.apps.approved.list) `def admin_apps_clearResolution(self, *, app_id: str, enterprise_id: str | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_apps_clearResolution( self, *, app_id: str, enterprise_id: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Clear an app resolution https://docs.slack.dev/reference/methods/admin.apps.clearResolution """ kwargs.update( { "app_id": app_id, "enterprise_id": enterprise_id, "team_id": team_id, } ) return self.api_call("admin.apps.clearResolution", http_verb="POST", params=kwargs) ``` Clear an app resolution [https://docs.slack.dev/reference/methods/admin.apps.clearResolution](https://docs.slack.dev/reference/methods/admin.apps.clearResolution) `def admin_apps_config_lookup(self, *, app_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_apps_config_lookup( self, *, app_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Look up the app config for connectors by their IDs https://docs.slack.dev/reference/methods/admin.apps.config.lookup """ if isinstance(app_ids, (list, tuple)): kwargs.update({"app_ids": ",".join(app_ids)}) else: kwargs.update({"app_ids": app_ids}) return self.api_call("admin.apps.config.lookup", params=kwargs) ``` Look up the app config for connectors by their IDs [https://docs.slack.dev/reference/methods/admin.apps.config.lookup](https://docs.slack.dev/reference/methods/admin.apps.config.lookup) `def admin_apps_config_set(self, *, app_id: str, domain_restrictions: Dict[str, Any] | None = None, workflow_auth_strategy: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_apps_config_set( self, *, app_id: str, domain_restrictions: Optional[Dict[str, Any]] = None, workflow_auth_strategy: Optional[str] = None, **kwargs, ) -> SlackResponse: """Set the app config for a connector https://docs.slack.dev/reference/methods/admin.apps.config.set """ kwargs.update( { "app_id": app_id, "workflow_auth_strategy": workflow_auth_strategy, } ) if domain_restrictions is not None: kwargs.update({"domain_restrictions": json.dumps(domain_restrictions)}) return self.api_call("admin.apps.config.set", params=kwargs) ``` Set the app config for a connector [https://docs.slack.dev/reference/methods/admin.apps.config.set](https://docs.slack.dev/reference/methods/admin.apps.config.set) `def admin_apps_requests_cancel(self, *, request_id: str, enterprise_id: str | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_apps_requests_cancel( self, *, request_id: str, enterprise_id: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List app requests for a team/workspace. https://docs.slack.dev/reference/methods/admin.apps.requests.cancel """ kwargs.update( { "request_id": request_id, "enterprise_id": enterprise_id, "team_id": team_id, } ) return self.api_call("admin.apps.requests.cancel", http_verb="POST", params=kwargs) ``` List app requests for a team/workspace. [https://docs.slack.dev/reference/methods/admin.apps.requests.cancel](https://docs.slack.dev/reference/methods/admin.apps.requests.cancel) `def admin_apps_requests_list(self, *, cursor: str | None = None, limit: int | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_apps_requests_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List app requests for a team/workspace. https://docs.slack.dev/reference/methods/admin.apps.requests.list """ kwargs.update( { "cursor": cursor, "limit": limit, "team_id": team_id, } ) return self.api_call("admin.apps.requests.list", http_verb="GET", params=kwargs) ``` List app requests for a team/workspace. [https://docs.slack.dev/reference/methods/admin.apps.requests.list](https://docs.slack.dev/reference/methods/admin.apps.requests.list) `def admin_apps_restrict(self, *, app_id: str | None = None, request_id: str | None = None, enterprise_id: str | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_apps_restrict( self, *, app_id: Optional[str] = None, request_id: Optional[str] = None, enterprise_id: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Restrict an app for installation on a workspace. Exactly one of the team_id or enterprise_id arguments is required, not both. Either app_id or request_id is required. These IDs can be obtained either directly via the app_requested event, or by the admin.apps.requests.list method. https://docs.slack.dev/reference/methods/admin.apps.restrict """ if app_id: kwargs.update({"app_id": app_id}) elif request_id: kwargs.update({"request_id": request_id}) else: raise e.SlackRequestError("The app_id or request_id argument must be specified.") kwargs.update( { "enterprise_id": enterprise_id, "team_id": team_id, } ) return self.api_call("admin.apps.restrict", params=kwargs) ``` Restrict an app for installation on a workspace. Exactly one of the team\_id or enterprise\_id arguments is required, not both. Either app\_id or request\_id is required. These IDs can be obtained either directly via the app\_requested event, or by the admin.apps.requests.list method. [https://docs.slack.dev/reference/methods/admin.apps.restrict](https://docs.slack.dev/reference/methods/admin.apps.restrict) `def admin_apps_restricted_list(self, *, cursor: str | None = None, limit: int | None = None, enterprise_id: str | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_apps_restricted_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, enterprise_id: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List restricted apps for an org or workspace. https://docs.slack.dev/reference/methods/admin.apps.restricted.list """ kwargs.update( { "cursor": cursor, "limit": limit, "enterprise_id": enterprise_id, "team_id": team_id, } ) return self.api_call("admin.apps.restricted.list", http_verb="GET", params=kwargs) ``` List restricted apps for an org or workspace. [https://docs.slack.dev/reference/methods/admin.apps.restricted.list](https://docs.slack.dev/reference/methods/admin.apps.restricted.list) `def admin_apps_uninstall(self, *, app_id: str, enterprise_id: str | None = None, team_ids: str | Sequence[str] | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_apps_uninstall( self, *, app_id: str, enterprise_id: Optional[str] = None, team_ids: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """Uninstall an app from one or many workspaces, or an entire enterprise organization. With an org-level token, enterprise_id or team_ids is required. https://docs.slack.dev/reference/methods/admin.apps.uninstall """ kwargs.update({"app_id": app_id}) if enterprise_id is not None: kwargs.update({"enterprise_id": enterprise_id}) if team_ids is not None: if isinstance(team_ids, (list, tuple)): kwargs.update({"team_ids": ",".join(team_ids)}) else: kwargs.update({"team_ids": team_ids}) return self.api_call("admin.apps.uninstall", http_verb="POST", params=kwargs) ``` Uninstall an app from one or many workspaces, or an entire enterprise organization. With an org-level token, enterprise\_id or team\_ids is required. [https://docs.slack.dev/reference/methods/admin.apps.uninstall](https://docs.slack.dev/reference/methods/admin.apps.uninstall) `def admin_auth_policy_assignEntities(self, *, entity_ids: str | Sequence[str], policy_name: str, entity_type: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_auth_policy_assignEntities( self, *, entity_ids: Union[str, Sequence[str]], policy_name: str, entity_type: str, **kwargs, ) -> SlackResponse: """Assign entities to a particular authentication policy. https://docs.slack.dev/reference/methods/admin.auth.policy.assignEntities """ if isinstance(entity_ids, (list, tuple)): kwargs.update({"entity_ids": ",".join(entity_ids)}) else: kwargs.update({"entity_ids": entity_ids}) kwargs.update({"policy_name": policy_name}) kwargs.update({"entity_type": entity_type}) return self.api_call("admin.auth.policy.assignEntities", http_verb="POST", params=kwargs) ``` Assign entities to a particular authentication policy. [https://docs.slack.dev/reference/methods/admin.auth.policy.assignEntities](https://docs.slack.dev/reference/methods/admin.auth.policy.assignEntities) `def admin_auth_policy_getEntities(self, *, policy_name: str, cursor: str | None = None, entity_type: str | None = None, limit: int | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_auth_policy_getEntities( self, *, policy_name: str, cursor: Optional[str] = None, entity_type: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Fetch all the entities assigned to a particular authentication policy by name. https://docs.slack.dev/reference/methods/admin.auth.policy.getEntities """ kwargs.update({"policy_name": policy_name}) if cursor is not None: kwargs.update({"cursor": cursor}) if entity_type is not None: kwargs.update({"entity_type": entity_type}) if limit is not None: kwargs.update({"limit": limit}) return self.api_call("admin.auth.policy.getEntities", http_verb="POST", params=kwargs) ``` Fetch all the entities assigned to a particular authentication policy by name. [https://docs.slack.dev/reference/methods/admin.auth.policy.getEntities](https://docs.slack.dev/reference/methods/admin.auth.policy.getEntities) `def admin_auth_policy_removeEntities(self, *, entity_ids: str | Sequence[str], policy_name: str, entity_type: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_auth_policy_removeEntities( self, *, entity_ids: Union[str, Sequence[str]], policy_name: str, entity_type: str, **kwargs, ) -> SlackResponse: """Remove specified entities from a specified authentication policy. https://docs.slack.dev/reference/methods/admin.auth.policy.removeEntities """ if isinstance(entity_ids, (list, tuple)): kwargs.update({"entity_ids": ",".join(entity_ids)}) else: kwargs.update({"entity_ids": entity_ids}) kwargs.update({"policy_name": policy_name}) kwargs.update({"entity_type": entity_type}) return self.api_call("admin.auth.policy.removeEntities", http_verb="POST", params=kwargs) ``` Remove specified entities from a specified authentication policy. [https://docs.slack.dev/reference/methods/admin.auth.policy.removeEntities](https://docs.slack.dev/reference/methods/admin.auth.policy.removeEntities) `def admin_barriers_create(self, *, barriered_from_usergroup_ids: str | Sequence[str], primary_usergroup_id: str, restricted_subjects: str | Sequence[str], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_barriers_create( self, *, barriered_from_usergroup_ids: Union[str, Sequence[str]], primary_usergroup_id: str, restricted_subjects: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Create an Information Barrier https://docs.slack.dev/reference/methods/admin.barriers.create """ kwargs.update({"primary_usergroup_id": primary_usergroup_id}) if isinstance(barriered_from_usergroup_ids, (list, tuple)): kwargs.update({"barriered_from_usergroup_ids": ",".join(barriered_from_usergroup_ids)}) else: kwargs.update({"barriered_from_usergroup_ids": barriered_from_usergroup_ids}) if isinstance(restricted_subjects, (list, tuple)): kwargs.update({"restricted_subjects": ",".join(restricted_subjects)}) else: kwargs.update({"restricted_subjects": restricted_subjects}) return self.api_call("admin.barriers.create", http_verb="POST", params=kwargs) ``` Create an Information Barrier [https://docs.slack.dev/reference/methods/admin.barriers.create](https://docs.slack.dev/reference/methods/admin.barriers.create) `def admin_barriers_delete(self, *, barrier_id: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_barriers_delete( self, *, barrier_id: str, **kwargs, ) -> SlackResponse: """Delete an existing Information Barrier https://docs.slack.dev/reference/methods/admin.barriers.delete """ kwargs.update({"barrier_id": barrier_id}) return self.api_call("admin.barriers.delete", http_verb="POST", params=kwargs) ``` Delete an existing Information Barrier [https://docs.slack.dev/reference/methods/admin.barriers.delete](https://docs.slack.dev/reference/methods/admin.barriers.delete) `def admin_barriers_list(self, *, cursor: str | None = None, limit: int | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_barriers_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Get all Information Barriers for your organization https://docs.slack.dev/reference/methods/admin.barriers.list""" kwargs.update( { "cursor": cursor, "limit": limit, } ) return self.api_call("admin.barriers.list", http_verb="GET", params=kwargs) ``` Get all Information Barriers for your organization [https://docs.slack.dev/reference/methods/admin.barriers.list](https://docs.slack.dev/reference/methods/admin.barriers.list) `def admin_barriers_update(self, *, barrier_id: str, barriered_from_usergroup_ids: str | Sequence[str], primary_usergroup_id: str, restricted_subjects: str | Sequence[str], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_barriers_update( self, *, barrier_id: str, barriered_from_usergroup_ids: Union[str, Sequence[str]], primary_usergroup_id: str, restricted_subjects: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Update an existing Information Barrier https://docs.slack.dev/reference/methods/admin.barriers.update """ kwargs.update({"barrier_id": barrier_id, "primary_usergroup_id": primary_usergroup_id}) if isinstance(barriered_from_usergroup_ids, (list, tuple)): kwargs.update({"barriered_from_usergroup_ids": ",".join(barriered_from_usergroup_ids)}) else: kwargs.update({"barriered_from_usergroup_ids": barriered_from_usergroup_ids}) if isinstance(restricted_subjects, (list, tuple)): kwargs.update({"restricted_subjects": ",".join(restricted_subjects)}) else: kwargs.update({"restricted_subjects": restricted_subjects}) return self.api_call("admin.barriers.update", http_verb="POST", params=kwargs) ``` Update an existing Information Barrier [https://docs.slack.dev/reference/methods/admin.barriers.update](https://docs.slack.dev/reference/methods/admin.barriers.update) `def admin_conversations_archive(self, *, channel_id: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_archive( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """Archive a public or private channel. https://docs.slack.dev/reference/methods/admin.conversations.archive """ kwargs.update({"channel_id": channel_id}) return self.api_call("admin.conversations.archive", params=kwargs) ``` Archive a public or private channel. [https://docs.slack.dev/reference/methods/admin.conversations.archive](https://docs.slack.dev/reference/methods/admin.conversations.archive) `def admin_conversations_bulkArchive(self, *, channel_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_bulkArchive( self, *, channel_ids: Union[Sequence[str], str], **kwargs, ) -> SlackResponse: """Archive public or private channels in bulk. https://docs.slack.dev/reference/methods/admin.conversations.bulkArchive """ kwargs.update({"channel_ids": ",".join(channel_ids) if isinstance(channel_ids, (list, tuple)) else channel_ids}) return self.api_call("admin.conversations.bulkArchive", params=kwargs) ``` Archive public or private channels in bulk. [https://docs.slack.dev/reference/methods/admin.conversations.bulkArchive](https://docs.slack.dev/reference/methods/admin.conversations.bulkArchive) `def admin_conversations_bulkDelete(self, *, channel_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_bulkDelete( self, *, channel_ids: Union[Sequence[str], str], **kwargs, ) -> SlackResponse: """Delete public or private channels in bulk. https://slack.com/api/admin.conversations.bulkDelete """ kwargs.update({"channel_ids": ",".join(channel_ids) if isinstance(channel_ids, (list, tuple)) else channel_ids}) return self.api_call("admin.conversations.bulkDelete", params=kwargs) ``` Delete public or private channels in bulk. [https://slack.com/api/admin.conversations.bulkDelete](https://slack.com/api/admin.conversations.bulkDelete) `def admin_conversations_bulkMove(self, *, channel_ids: str | Sequence[str], target_team_id: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_bulkMove( self, *, channel_ids: Union[Sequence[str], str], target_team_id: str, **kwargs, ) -> SlackResponse: """Move public or private channels in bulk. https://docs.slack.dev/reference/methods/admin.conversations.bulkMove """ kwargs.update( { "target_team_id": target_team_id, "channel_ids": ",".join(channel_ids) if isinstance(channel_ids, (list, tuple)) else channel_ids, } ) return self.api_call("admin.conversations.bulkMove", params=kwargs) ``` Move public or private channels in bulk. [https://docs.slack.dev/reference/methods/admin.conversations.bulkMove](https://docs.slack.dev/reference/methods/admin.conversations.bulkMove) `def admin_conversations_convertToPrivate(self, *, channel_id: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_convertToPrivate( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """Convert a public channel to a private channel. https://docs.slack.dev/reference/methods/admin.conversations.convertToPrivate """ kwargs.update({"channel_id": channel_id}) return self.api_call("admin.conversations.convertToPrivate", params=kwargs) ``` Convert a public channel to a private channel. [https://docs.slack.dev/reference/methods/admin.conversations.convertToPrivate](https://docs.slack.dev/reference/methods/admin.conversations.convertToPrivate) `def admin_conversations_convertToPublic(self, *, channel_id: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_convertToPublic( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """Convert a privte channel to a public channel. https://docs.slack.dev/reference/methods/admin.conversations.convertToPublic """ kwargs.update({"channel_id": channel_id}) return self.api_call("admin.conversations.convertToPublic", params=kwargs) ``` Convert a privte channel to a public channel. [https://docs.slack.dev/reference/methods/admin.conversations.convertToPublic](https://docs.slack.dev/reference/methods/admin.conversations.convertToPublic) `def admin_conversations_create(self, *, is_private: bool, name: str, description: str | None = None, org_wide: bool | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_create( self, *, is_private: bool, name: str, description: Optional[str] = None, org_wide: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Create a public or private channel-based conversation. https://docs.slack.dev/reference/methods/admin.conversations.create """ kwargs.update( { "is_private": is_private, "name": name, "description": description, "org_wide": org_wide, "team_id": team_id, } ) return self.api_call("admin.conversations.create", params=kwargs) ``` Create a public or private channel-based conversation. [https://docs.slack.dev/reference/methods/admin.conversations.create](https://docs.slack.dev/reference/methods/admin.conversations.create) `def admin_conversations_createForObjects(self, *, object_id: str, salesforce_org_id: str, invite_object_team: bool | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_createForObjects( self, *, object_id: str, salesforce_org_id: str, invite_object_team: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Create a Salesforce channel for the corresponding object provided. https://docs.slack.dev/reference/methods/admin.conversations.createForObjects """ kwargs.update( {"object_id": object_id, "salesforce_org_id": salesforce_org_id, "invite_object_team": invite_object_team} ) return self.api_call("admin.conversations.createForObjects", params=kwargs) ``` Create a Salesforce channel for the corresponding object provided. [https://docs.slack.dev/reference/methods/admin.conversations.createForObjects](https://docs.slack.dev/reference/methods/admin.conversations.createForObjects) `def admin_conversations_delete(self, *, channel_id: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_delete( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """Delete a public or private channel. https://docs.slack.dev/reference/methods/admin.conversations.delete """ kwargs.update({"channel_id": channel_id}) return self.api_call("admin.conversations.delete", params=kwargs) ``` Delete a public or private channel. [https://docs.slack.dev/reference/methods/admin.conversations.delete](https://docs.slack.dev/reference/methods/admin.conversations.delete) `def admin_conversations_disconnectShared(self, *, channel_id: str, leaving_team_ids: str | Sequence[str] | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_disconnectShared( self, *, channel_id: str, leaving_team_ids: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """Disconnect a connected channel from one or more workspaces. https://docs.slack.dev/reference/methods/admin.conversations.disconnectShared """ kwargs.update({"channel_id": channel_id}) if isinstance(leaving_team_ids, (list, tuple)): kwargs.update({"leaving_team_ids": ",".join(leaving_team_ids)}) else: kwargs.update({"leaving_team_ids": leaving_team_ids}) return self.api_call("admin.conversations.disconnectShared", params=kwargs) ``` Disconnect a connected channel from one or more workspaces. [https://docs.slack.dev/reference/methods/admin.conversations.disconnectShared](https://docs.slack.dev/reference/methods/admin.conversations.disconnectShared) `def admin_conversations_ekm_listOriginalConnectedChannelInfo(self, *, channel_ids: str | Sequence[str] | None = None, cursor: str | None = None, limit: int | None = None, team_ids: str | Sequence[str] | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_ekm_listOriginalConnectedChannelInfo( self, *, channel_ids: Optional[Union[str, Sequence[str]]] = None, cursor: Optional[str] = None, limit: Optional[int] = None, team_ids: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """List all disconnected channels—i.e., channels that were once connected to other workspaces and then disconnected—and the corresponding original channel IDs for key revocation with EKM. https://docs.slack.dev/reference/methods/admin.conversations.ekm.listOriginalConnectedChannelInfo """ kwargs.update( { "cursor": cursor, "limit": limit, } ) if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) if isinstance(team_ids, (list, tuple)): kwargs.update({"team_ids": ",".join(team_ids)}) else: kwargs.update({"team_ids": team_ids}) return self.api_call("admin.conversations.ekm.listOriginalConnectedChannelInfo", params=kwargs) ``` List all disconnected channels—i.e., channels that were once connected to other workspaces and then disconnected—and the corresponding original channel IDs for key revocation with EKM. [https://docs.slack.dev/reference/methods/admin.conversations.ekm.listOriginalConnectedChannelInfo](https://docs.slack.dev/reference/methods/admin.conversations.ekm.listOriginalConnectedChannelInfo) `def admin_conversations_getConversationPrefs(self, *, channel_id: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_getConversationPrefs( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """Get conversation preferences for a public or private channel. https://docs.slack.dev/reference/methods/admin.conversations.getConversationPrefs """ kwargs.update({"channel_id": channel_id}) return self.api_call("admin.conversations.getConversationPrefs", params=kwargs) ``` Get conversation preferences for a public or private channel. [https://docs.slack.dev/reference/methods/admin.conversations.getConversationPrefs](https://docs.slack.dev/reference/methods/admin.conversations.getConversationPrefs) `def admin_conversations_getCustomRetention(self, *, channel_id: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_getCustomRetention( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """Get a channel's retention policy https://docs.slack.dev/reference/methods/admin.conversations.getCustomRetention """ kwargs.update({"channel_id": channel_id}) return self.api_call("admin.conversations.getCustomRetention", params=kwargs) ``` Get a channel's retention policy [https://docs.slack.dev/reference/methods/admin.conversations.getCustomRetention](https://docs.slack.dev/reference/methods/admin.conversations.getCustomRetention) `def admin_conversations_getTeams(self, *, channel_id: str, cursor: str | None = None, limit: int | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_getTeams( self, *, channel_id: str, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Set the workspaces in an Enterprise grid org that connect to a channel. https://docs.slack.dev/reference/methods/admin.conversations.getTeams """ kwargs.update( { "channel_id": channel_id, "cursor": cursor, "limit": limit, } ) return self.api_call("admin.conversations.getTeams", params=kwargs) ``` Set the workspaces in an Enterprise grid org that connect to a channel. [https://docs.slack.dev/reference/methods/admin.conversations.getTeams](https://docs.slack.dev/reference/methods/admin.conversations.getTeams) `def admin_conversations_invite(self, *, channel_id: str, user_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_invite( self, *, channel_id: str, user_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Invite a user to a public or private channel. https://docs.slack.dev/reference/methods/admin.conversations.invite """ kwargs.update({"channel_id": channel_id}) if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) # NOTE: the endpoint is unable to handle Content-Type: application/json as of Sep 3, 2020. return self.api_call("admin.conversations.invite", params=kwargs) ``` Invite a user to a public or private channel. [https://docs.slack.dev/reference/methods/admin.conversations.invite](https://docs.slack.dev/reference/methods/admin.conversations.invite) `def admin_conversations_linkObjects(self, *, channel: str, record_id: str, salesforce_org_id: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_linkObjects( self, *, channel: str, record_id: str, salesforce_org_id: str, **kwargs, ) -> SlackResponse: """Link a Salesforce record to a channel. https://docs.slack.dev/reference/methods/admin.conversations.linkObjects """ kwargs.update( { "channel": channel, "record_id": record_id, "salesforce_org_id": salesforce_org_id, } ) return self.api_call("admin.conversations.linkObjects", params=kwargs) ``` Link a Salesforce record to a channel. [https://docs.slack.dev/reference/methods/admin.conversations.linkObjects](https://docs.slack.dev/reference/methods/admin.conversations.linkObjects) `def admin_conversations_lookup(self, *, last_message_activity_before: int, team_ids: str | Sequence[str], cursor: str | None = None, limit: int | None = None, max_member_count: int | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_lookup( self, *, last_message_activity_before: int, team_ids: Union[str, Sequence[str]], cursor: Optional[str] = None, limit: Optional[int] = None, max_member_count: Optional[int] = None, **kwargs, ) -> SlackResponse: """Returns channels on the given team using the filters. https://docs.slack.dev/reference/methods/admin.conversations.lookup """ kwargs.update( { "last_message_activity_before": last_message_activity_before, "cursor": cursor, "limit": limit, "max_member_count": max_member_count, } ) if isinstance(team_ids, (list, tuple)): kwargs.update({"team_ids": ",".join(team_ids)}) else: kwargs.update({"team_ids": team_ids}) return self.api_call("admin.conversations.lookup", params=kwargs) ``` Returns channels on the given team using the filters. [https://docs.slack.dev/reference/methods/admin.conversations.lookup](https://docs.slack.dev/reference/methods/admin.conversations.lookup) `def admin_conversations_removeCustomRetention(self, *, channel_id: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_removeCustomRetention( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """Remove a channel's retention policy https://docs.slack.dev/reference/methods/admin.conversations.removeCustomRetention """ kwargs.update({"channel_id": channel_id}) return self.api_call("admin.conversations.removeCustomRetention", params=kwargs) ``` Remove a channel's retention policy [https://docs.slack.dev/reference/methods/admin.conversations.removeCustomRetention](https://docs.slack.dev/reference/methods/admin.conversations.removeCustomRetention) `def admin_conversations_rename(self, *, channel_id: str, name: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_rename( self, *, channel_id: str, name: str, **kwargs, ) -> SlackResponse: """Rename a public or private channel. https://docs.slack.dev/reference/methods/admin.conversations.rename """ kwargs.update({"channel_id": channel_id, "name": name}) return self.api_call("admin.conversations.rename", params=kwargs) ``` Rename a public or private channel. [https://docs.slack.dev/reference/methods/admin.conversations.rename](https://docs.slack.dev/reference/methods/admin.conversations.rename) `def admin_conversations_restrictAccess_addGroup(self, *, channel_id: str, group_id: str, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_restrictAccess_addGroup( self, *, channel_id: str, group_id: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Add an allowlist of IDP groups for accessing a channel. https://docs.slack.dev/reference/methods/admin.conversations.restrictAccess.addGroup """ kwargs.update( { "channel_id": channel_id, "group_id": group_id, "team_id": team_id, } ) return self.api_call( "admin.conversations.restrictAccess.addGroup", http_verb="GET", params=kwargs, ) ``` Add an allowlist of IDP groups for accessing a channel. [https://docs.slack.dev/reference/methods/admin.conversations.restrictAccess.addGroup](https://docs.slack.dev/reference/methods/admin.conversations.restrictAccess.addGroup) `def admin_conversations_restrictAccess_listGroups(self, *, channel_id: str, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_restrictAccess_listGroups( self, *, channel_id: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List all IDP Groups linked to a channel. https://docs.slack.dev/reference/methods/admin.conversations.restrictAccess.listGroups """ kwargs.update( { "channel_id": channel_id, "team_id": team_id, } ) return self.api_call( "admin.conversations.restrictAccess.listGroups", http_verb="GET", params=kwargs, ) ``` List all IDP Groups linked to a channel. [https://docs.slack.dev/reference/methods/admin.conversations.restrictAccess.listGroups](https://docs.slack.dev/reference/methods/admin.conversations.restrictAccess.listGroups) `def admin_conversations_restrictAccess_removeGroup(self, *, channel_id: str, group_id: str, team_id: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_restrictAccess_removeGroup( self, *, channel_id: str, group_id: str, team_id: str, **kwargs, ) -> SlackResponse: """Remove a linked IDP group linked from a private channel. https://docs.slack.dev/reference/methods/admin.conversations.restrictAccess.removeGroup """ kwargs.update( { "channel_id": channel_id, "group_id": group_id, "team_id": team_id, } ) return self.api_call( "admin.conversations.restrictAccess.removeGroup", http_verb="GET", params=kwargs, ) ``` Remove a linked IDP group linked from a private channel. [https://docs.slack.dev/reference/methods/admin.conversations.restrictAccess.removeGroup](https://docs.slack.dev/reference/methods/admin.conversations.restrictAccess.removeGroup) `def admin_conversations_search(self, *, cursor: str | None = None, limit: int | None = None, query: str | None = None, search_channel_types: str | Sequence[str] | None = None, sort: str | None = None, sort_dir: str | None = None, team_ids: str | Sequence[str] | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_search( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, query: Optional[str] = None, search_channel_types: Optional[Union[str, Sequence[str]]] = None, sort: Optional[str] = None, sort_dir: Optional[str] = None, team_ids: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """Search for public or private channels in an Enterprise organization. https://docs.slack.dev/reference/methods/admin.conversations.search """ kwargs.update( { "cursor": cursor, "limit": limit, "query": query, "sort": sort, "sort_dir": sort_dir, } ) if isinstance(search_channel_types, (list, tuple)): kwargs.update({"search_channel_types": ",".join(search_channel_types)}) else: kwargs.update({"search_channel_types": search_channel_types}) if isinstance(team_ids, (list, tuple)): kwargs.update({"team_ids": ",".join(team_ids)}) else: kwargs.update({"team_ids": team_ids}) return self.api_call("admin.conversations.search", params=kwargs) ``` Search for public or private channels in an Enterprise organization. [https://docs.slack.dev/reference/methods/admin.conversations.search](https://docs.slack.dev/reference/methods/admin.conversations.search) `def admin_conversations_setConversationPrefs(self, *, channel_id: str, prefs: str | Dict[str, str], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_setConversationPrefs( self, *, channel_id: str, prefs: Union[str, Dict[str, str]], **kwargs, ) -> SlackResponse: """Set the posting permissions for a public or private channel. https://docs.slack.dev/reference/methods/admin.conversations.setConversationPrefs """ kwargs.update({"channel_id": channel_id}) if isinstance(prefs, dict): kwargs.update({"prefs": json.dumps(prefs)}) else: kwargs.update({"prefs": prefs}) return self.api_call("admin.conversations.setConversationPrefs", params=kwargs) ``` Set the posting permissions for a public or private channel. [https://docs.slack.dev/reference/methods/admin.conversations.setConversationPrefs](https://docs.slack.dev/reference/methods/admin.conversations.setConversationPrefs) `def admin_conversations_setCustomRetention(self, *, channel_id: str, duration_days: int, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_setCustomRetention( self, *, channel_id: str, duration_days: int, **kwargs, ) -> SlackResponse: """Set a channel's retention policy https://docs.slack.dev/reference/methods/admin.conversations.setCustomRetention """ kwargs.update({"channel_id": channel_id, "duration_days": duration_days}) return self.api_call("admin.conversations.setCustomRetention", params=kwargs) ``` Set a channel's retention policy [https://docs.slack.dev/reference/methods/admin.conversations.setCustomRetention](https://docs.slack.dev/reference/methods/admin.conversations.setCustomRetention) `def admin_conversations_setTeams(self, *, channel_id: str, org_channel: bool | None = None, target_team_ids: str | Sequence[str] | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_setTeams( self, *, channel_id: str, org_channel: Optional[bool] = None, target_team_ids: Optional[Union[str, Sequence[str]]] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Set the workspaces in an Enterprise grid org that connect to a public or private channel. https://docs.slack.dev/reference/methods/admin.conversations.setTeams """ kwargs.update( { "channel_id": channel_id, "org_channel": org_channel, "team_id": team_id, } ) if isinstance(target_team_ids, (list, tuple)): kwargs.update({"target_team_ids": ",".join(target_team_ids)}) else: kwargs.update({"target_team_ids": target_team_ids}) return self.api_call("admin.conversations.setTeams", params=kwargs) ``` Set the workspaces in an Enterprise grid org that connect to a public or private channel. [https://docs.slack.dev/reference/methods/admin.conversations.setTeams](https://docs.slack.dev/reference/methods/admin.conversations.setTeams) `def admin_conversations_unarchive(self, *, channel_id: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_unarchive( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """Unarchive a public or private channel. https://docs.slack.dev/reference/methods/admin.conversations.archive """ kwargs.update({"channel_id": channel_id}) return self.api_call("admin.conversations.unarchive", params=kwargs) ``` Unarchive a public or private channel. [https://docs.slack.dev/reference/methods/admin.conversations.archive](https://docs.slack.dev/reference/methods/admin.conversations.archive) `def admin_conversations_unlinkObjects(self, *, channel: str, new_name: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_conversations_unlinkObjects( self, *, channel: str, new_name: str, **kwargs, ) -> SlackResponse: """Unlink a Salesforce record from a channel. https://docs.slack.dev/reference/methods/admin.conversations.unlinkObjects """ kwargs.update( { "channel": channel, "new_name": new_name, } ) return self.api_call("admin.conversations.unlinkObjects", params=kwargs) ``` Unlink a Salesforce record from a channel. [https://docs.slack.dev/reference/methods/admin.conversations.unlinkObjects](https://docs.slack.dev/reference/methods/admin.conversations.unlinkObjects) `def admin_emoji_add(self, *, name: str, url: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_emoji_add( self, *, name: str, url: str, **kwargs, ) -> SlackResponse: """Add an emoji. https://docs.slack.dev/reference/methods/admin.emoji.add """ kwargs.update({"name": name, "url": url}) return self.api_call("admin.emoji.add", http_verb="GET", params=kwargs) ``` Add an emoji. [https://docs.slack.dev/reference/methods/admin.emoji.add](https://docs.slack.dev/reference/methods/admin.emoji.add) `def admin_emoji_addAlias(self, *, alias_for: str, name: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_emoji_addAlias( self, *, alias_for: str, name: str, **kwargs, ) -> SlackResponse: """Add an emoji alias. https://docs.slack.dev/reference/methods/admin.emoji.addAlias """ kwargs.update({"alias_for": alias_for, "name": name}) return self.api_call("admin.emoji.addAlias", http_verb="GET", params=kwargs) ``` Add an emoji alias. [https://docs.slack.dev/reference/methods/admin.emoji.addAlias](https://docs.slack.dev/reference/methods/admin.emoji.addAlias) `def admin_emoji_list(self, *, cursor: str | None = None, limit: int | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_emoji_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """List emoji for an Enterprise Grid organization. https://docs.slack.dev/reference/methods/admin.emoji.list """ kwargs.update({"cursor": cursor, "limit": limit}) return self.api_call("admin.emoji.list", http_verb="GET", params=kwargs) ``` List emoji for an Enterprise Grid organization. [https://docs.slack.dev/reference/methods/admin.emoji.list](https://docs.slack.dev/reference/methods/admin.emoji.list) `def admin_emoji_remove(self, *, name: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_emoji_remove( self, *, name: str, **kwargs, ) -> SlackResponse: """Remove an emoji across an Enterprise Grid organization. https://docs.slack.dev/reference/methods/admin.emoji.remove """ kwargs.update({"name": name}) return self.api_call("admin.emoji.remove", http_verb="GET", params=kwargs) ``` Remove an emoji across an Enterprise Grid organization. [https://docs.slack.dev/reference/methods/admin.emoji.remove](https://docs.slack.dev/reference/methods/admin.emoji.remove) `def admin_emoji_rename(self, *, name: str, new_name: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_emoji_rename( self, *, name: str, new_name: str, **kwargs, ) -> SlackResponse: """Rename an emoji. https://docs.slack.dev/reference/methods/admin.emoji.rename """ kwargs.update({"name": name, "new_name": new_name}) return self.api_call("admin.emoji.rename", http_verb="GET", params=kwargs) ``` Rename an emoji. [https://docs.slack.dev/reference/methods/admin.emoji.rename](https://docs.slack.dev/reference/methods/admin.emoji.rename) `def admin_functions_list(self, *, app_ids: str | Sequence[str], team_id: str | None = None, cursor: str | None = None, limit: int | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_functions_list( self, *, app_ids: Union[str, Sequence[str]], team_id: Optional[str] = None, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Look up functions by a set of apps https://docs.slack.dev/reference/methods/admin.functions.list """ if isinstance(app_ids, (list, tuple)): kwargs.update({"app_ids": ",".join(app_ids)}) else: kwargs.update({"app_ids": app_ids}) kwargs.update( { "team_id": team_id, "cursor": cursor, "limit": limit, } ) return self.api_call("admin.functions.list", params=kwargs) ``` Look up functions by a set of apps [https://docs.slack.dev/reference/methods/admin.functions.list](https://docs.slack.dev/reference/methods/admin.functions.list) `def admin_functions_permissions_lookup(self, *, function_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_functions_permissions_lookup( self, *, function_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Lookup the visibility of multiple Slack functions and include the users if it is limited to particular named entities. https://docs.slack.dev/reference/methods/admin.functions.permissions.lookup """ if isinstance(function_ids, (list, tuple)): kwargs.update({"function_ids": ",".join(function_ids)}) else: kwargs.update({"function_ids": function_ids}) return self.api_call("admin.functions.permissions.lookup", params=kwargs) ``` Lookup the visibility of multiple Slack functions and include the users if it is limited to particular named entities. [https://docs.slack.dev/reference/methods/admin.functions.permissions.lookup](https://docs.slack.dev/reference/methods/admin.functions.permissions.lookup) `def admin_functions_permissions_set(self, *, function_id: str, visibility: str, user_ids: str | Sequence[str] | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_functions_permissions_set( self, *, function_id: str, visibility: str, user_ids: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """Set the visibility of a Slack function and define the users or workspaces if it is set to named_entities https://docs.slack.dev/reference/methods/admin.functions.permissions.set """ kwargs.update( { "function_id": function_id, "visibility": visibility, } ) if user_ids is not None: if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) return self.api_call("admin.functions.permissions.set", params=kwargs) ``` Set the visibility of a Slack function and define the users or workspaces if it is set to named\_entities [https://docs.slack.dev/reference/methods/admin.functions.permissions.set](https://docs.slack.dev/reference/methods/admin.functions.permissions.set) `def admin_inviteRequests_approve(self, *, invite_request_id: str, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_inviteRequests_approve( self, *, invite_request_id: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Approve a workspace invite request. https://docs.slack.dev/reference/methods/admin.inviteRequests.approve """ kwargs.update({"invite_request_id": invite_request_id, "team_id": team_id}) return self.api_call("admin.inviteRequests.approve", params=kwargs) ``` Approve a workspace invite request. [https://docs.slack.dev/reference/methods/admin.inviteRequests.approve](https://docs.slack.dev/reference/methods/admin.inviteRequests.approve) `def admin_inviteRequests_approved_list(self, *, cursor: str | None = None, limit: int | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_inviteRequests_approved_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List all approved workspace invite requests. https://docs.slack.dev/reference/methods/admin.inviteRequests.approved.list """ kwargs.update( { "cursor": cursor, "limit": limit, "team_id": team_id, } ) return self.api_call("admin.inviteRequests.approved.list", params=kwargs) ``` List all approved workspace invite requests. [https://docs.slack.dev/reference/methods/admin.inviteRequests.approved.list](https://docs.slack.dev/reference/methods/admin.inviteRequests.approved.list) `def admin_inviteRequests_denied_list(self, *, cursor: str | None = None, limit: int | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_inviteRequests_denied_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List all denied workspace invite requests. https://docs.slack.dev/reference/methods/admin.inviteRequests.denied.list """ kwargs.update( { "cursor": cursor, "limit": limit, "team_id": team_id, } ) return self.api_call("admin.inviteRequests.denied.list", params=kwargs) ``` List all denied workspace invite requests. [https://docs.slack.dev/reference/methods/admin.inviteRequests.denied.list](https://docs.slack.dev/reference/methods/admin.inviteRequests.denied.list) `def admin_inviteRequests_deny(self, *, invite_request_id: str, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_inviteRequests_deny( self, *, invite_request_id: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Deny a workspace invite request. https://docs.slack.dev/reference/methods/admin.inviteRequests.deny """ kwargs.update({"invite_request_id": invite_request_id, "team_id": team_id}) return self.api_call("admin.inviteRequests.deny", params=kwargs) ``` Deny a workspace invite request. [https://docs.slack.dev/reference/methods/admin.inviteRequests.deny](https://docs.slack.dev/reference/methods/admin.inviteRequests.deny) `def admin_inviteRequests_list(self, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_inviteRequests_list( self, **kwargs, ) -> SlackResponse: """List all pending workspace invite requests.""" return self.api_call("admin.inviteRequests.list", params=kwargs) ``` List all pending workspace invite requests. `def admin_roles_addAssignments(self, *, role_id: str, entity_ids: str | Sequence[str], user_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_roles_addAssignments( self, *, role_id: str, entity_ids: Union[str, Sequence[str]], user_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Adds members to the specified role with the specified scopes https://docs.slack.dev/reference/methods/admin.roles.addAssignments """ kwargs.update({"role_id": role_id}) if isinstance(entity_ids, (list, tuple)): kwargs.update({"entity_ids": ",".join(entity_ids)}) else: kwargs.update({"entity_ids": entity_ids}) if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) return self.api_call("admin.roles.addAssignments", params=kwargs) ``` Adds members to the specified role with the specified scopes [https://docs.slack.dev/reference/methods/admin.roles.addAssignments](https://docs.slack.dev/reference/methods/admin.roles.addAssignments) `def admin_roles_listAssignments(self, *, role_ids: str | Sequence[str] | None = None, entity_ids: str | Sequence[str] | None = None, cursor: str | None = None, limit: str | int | None = None, sort_dir: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_roles_listAssignments( self, *, role_ids: Optional[Union[str, Sequence[str]]] = None, entity_ids: Optional[Union[str, Sequence[str]]] = None, cursor: Optional[str] = None, limit: Optional[Union[str, int]] = None, sort_dir: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists assignments for all roles across entities. Options to scope results by any combination of roles or entities https://docs.slack.dev/reference/methods/admin.roles.listAssignments """ kwargs.update({"cursor": cursor, "limit": limit, "sort_dir": sort_dir}) if isinstance(entity_ids, (list, tuple)): kwargs.update({"entity_ids": ",".join(entity_ids)}) else: kwargs.update({"entity_ids": entity_ids}) if isinstance(role_ids, (list, tuple)): kwargs.update({"role_ids": ",".join(role_ids)}) else: kwargs.update({"role_ids": role_ids}) return self.api_call("admin.roles.listAssignments", params=kwargs) ``` Lists assignments for all roles across entities. Options to scope results by any combination of roles or entities [https://docs.slack.dev/reference/methods/admin.roles.listAssignments](https://docs.slack.dev/reference/methods/admin.roles.listAssignments) `def admin_roles_removeAssignments(self, *, role_id: str, entity_ids: str | Sequence[str], user_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_roles_removeAssignments( self, *, role_id: str, entity_ids: Union[str, Sequence[str]], user_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Removes a set of users from a role for the given scopes and entities https://docs.slack.dev/reference/methods/admin.roles.removeAssignments """ kwargs.update({"role_id": role_id}) if isinstance(entity_ids, (list, tuple)): kwargs.update({"entity_ids": ",".join(entity_ids)}) else: kwargs.update({"entity_ids": entity_ids}) if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) return self.api_call("admin.roles.removeAssignments", params=kwargs) ``` Removes a set of users from a role for the given scopes and entities [https://docs.slack.dev/reference/methods/admin.roles.removeAssignments](https://docs.slack.dev/reference/methods/admin.roles.removeAssignments) `def admin_teams_admins_list(self, *, team_id: str, cursor: str | None = None, limit: int | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_teams_admins_list( self, *, team_id: str, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """List all of the admins on a given workspace. https://docs.slack.dev/reference/methods/admin.inviteRequests.list """ kwargs.update( { "cursor": cursor, "limit": limit, "team_id": team_id, } ) return self.api_call("admin.teams.admins.list", http_verb="GET", params=kwargs) ``` List all of the admins on a given workspace. [https://docs.slack.dev/reference/methods/admin.inviteRequests.list](https://docs.slack.dev/reference/methods/admin.inviteRequests.list) `def admin_teams_create(self, *, team_domain: str, team_name: str, team_description: str | None = None, team_discoverability: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_teams_create( self, *, team_domain: str, team_name: str, team_description: Optional[str] = None, team_discoverability: Optional[str] = None, **kwargs, ) -> SlackResponse: """Create an Enterprise team. https://docs.slack.dev/reference/methods/admin.teams.create """ kwargs.update( { "team_domain": team_domain, "team_name": team_name, "team_description": team_description, "team_discoverability": team_discoverability, } ) return self.api_call("admin.teams.create", params=kwargs) ``` Create an Enterprise team. [https://docs.slack.dev/reference/methods/admin.teams.create](https://docs.slack.dev/reference/methods/admin.teams.create) `def admin_teams_list(self, *, cursor: str | None = None, limit: int | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_teams_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """List all teams on an Enterprise organization. https://docs.slack.dev/reference/methods/admin.teams.list """ kwargs.update({"cursor": cursor, "limit": limit}) return self.api_call("admin.teams.list", params=kwargs) ``` List all teams on an Enterprise organization. [https://docs.slack.dev/reference/methods/admin.teams.list](https://docs.slack.dev/reference/methods/admin.teams.list) `def admin_teams_owners_list(self, *, team_id: str, cursor: str | None = None, limit: int | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_teams_owners_list( self, *, team_id: str, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """List all of the admins on a given workspace. https://docs.slack.dev/reference/methods/admin.teams.owners.list """ kwargs.update({"team_id": team_id, "cursor": cursor, "limit": limit}) return self.api_call("admin.teams.owners.list", http_verb="GET", params=kwargs) ``` List all of the admins on a given workspace. [https://docs.slack.dev/reference/methods/admin.teams.owners.list](https://docs.slack.dev/reference/methods/admin.teams.owners.list) `def admin_teams_settings_info(self, *, team_id: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_teams_settings_info( self, *, team_id: str, **kwargs, ) -> SlackResponse: """Fetch information about settings in a workspace https://docs.slack.dev/reference/methods/admin.teams.settings.info """ kwargs.update({"team_id": team_id}) return self.api_call("admin.teams.settings.info", params=kwargs) ``` Fetch information about settings in a workspace [https://docs.slack.dev/reference/methods/admin.teams.settings.info](https://docs.slack.dev/reference/methods/admin.teams.settings.info) `def admin_teams_settings_setDefaultChannels(self, *, team_id: str, channel_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_teams_settings_setDefaultChannels( self, *, team_id: str, channel_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Set the default channels of a workspace. https://docs.slack.dev/reference/methods/admin.teams.settings.setDefaultChannels """ kwargs.update({"team_id": team_id}) if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) return self.api_call("admin.teams.settings.setDefaultChannels", http_verb="GET", params=kwargs) ``` Set the default channels of a workspace. [https://docs.slack.dev/reference/methods/admin.teams.settings.setDefaultChannels](https://docs.slack.dev/reference/methods/admin.teams.settings.setDefaultChannels) `def admin_teams_settings_setDescription(self, *, team_id: str, description: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_teams_settings_setDescription( self, *, team_id: str, description: str, **kwargs, ) -> SlackResponse: """Set the description of a given workspace. https://docs.slack.dev/reference/methods/admin.teams.settings.setDescription """ kwargs.update({"team_id": team_id, "description": description}) return self.api_call("admin.teams.settings.setDescription", params=kwargs) ``` Set the description of a given workspace. [https://docs.slack.dev/reference/methods/admin.teams.settings.setDescription](https://docs.slack.dev/reference/methods/admin.teams.settings.setDescription) `def admin_teams_settings_setDiscoverability(self, *, team_id: str, discoverability: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_teams_settings_setDiscoverability( self, *, team_id: str, discoverability: str, **kwargs, ) -> SlackResponse: """Sets the icon of a workspace. https://docs.slack.dev/reference/methods/admin.teams.settings.setDiscoverability """ kwargs.update({"team_id": team_id, "discoverability": discoverability}) return self.api_call("admin.teams.settings.setDiscoverability", params=kwargs) ``` Sets the icon of a workspace. [https://docs.slack.dev/reference/methods/admin.teams.settings.setDiscoverability](https://docs.slack.dev/reference/methods/admin.teams.settings.setDiscoverability) `def admin_teams_settings_setIcon(self, *, team_id: str, image_url: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_teams_settings_setIcon( self, *, team_id: str, image_url: str, **kwargs, ) -> SlackResponse: """Sets the icon of a workspace. https://docs.slack.dev/reference/methods/admin.teams.settings.setIcon """ kwargs.update({"team_id": team_id, "image_url": image_url}) return self.api_call("admin.teams.settings.setIcon", http_verb="GET", params=kwargs) ``` Sets the icon of a workspace. [https://docs.slack.dev/reference/methods/admin.teams.settings.setIcon](https://docs.slack.dev/reference/methods/admin.teams.settings.setIcon) `def admin_teams_settings_setName(self, *, team_id: str, name: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_teams_settings_setName( self, *, team_id: str, name: str, **kwargs, ) -> SlackResponse: """Sets the icon of a workspace. https://docs.slack.dev/reference/methods/admin.teams.settings.setName """ kwargs.update({"team_id": team_id, "name": name}) return self.api_call("admin.teams.settings.setName", params=kwargs) ``` Sets the icon of a workspace. [https://docs.slack.dev/reference/methods/admin.teams.settings.setName](https://docs.slack.dev/reference/methods/admin.teams.settings.setName) `def admin_usergroups_addChannels(self, *, channel_ids: str | Sequence[str], usergroup_id: str, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_usergroups_addChannels( self, *, channel_ids: Union[str, Sequence[str]], usergroup_id: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Add one or more default channels to an IDP group. https://docs.slack.dev/reference/methods/admin.usergroups.addChannels """ kwargs.update({"team_id": team_id, "usergroup_id": usergroup_id}) if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) return self.api_call("admin.usergroups.addChannels", params=kwargs) ``` Add one or more default channels to an IDP group. [https://docs.slack.dev/reference/methods/admin.usergroups.addChannels](https://docs.slack.dev/reference/methods/admin.usergroups.addChannels) `def admin_usergroups_addTeams(self, *, usergroup_id: str, team_ids: str | Sequence[str], auto_provision: bool | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_usergroups_addTeams( self, *, usergroup_id: str, team_ids: Union[str, Sequence[str]], auto_provision: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Associate one or more default workspaces with an organization-wide IDP group. https://docs.slack.dev/reference/methods/admin.usergroups.addTeams """ kwargs.update({"usergroup_id": usergroup_id, "auto_provision": auto_provision}) if isinstance(team_ids, (list, tuple)): kwargs.update({"team_ids": ",".join(team_ids)}) else: kwargs.update({"team_ids": team_ids}) return self.api_call("admin.usergroups.addTeams", params=kwargs) ``` Associate one or more default workspaces with an organization-wide IDP group. [https://docs.slack.dev/reference/methods/admin.usergroups.addTeams](https://docs.slack.dev/reference/methods/admin.usergroups.addTeams) `def admin_usergroups_listChannels(self, *, usergroup_id: str, include_num_members: bool | None = None, team_id: bool | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_usergroups_listChannels( self, *, usergroup_id: str, include_num_members: Optional[bool] = None, team_id: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Add one or more default channels to an IDP group. https://docs.slack.dev/reference/methods/admin.usergroups.listChannels """ kwargs.update( { "usergroup_id": usergroup_id, "include_num_members": include_num_members, "team_id": team_id, } ) return self.api_call("admin.usergroups.listChannels", params=kwargs) ``` Add one or more default channels to an IDP group. [https://docs.slack.dev/reference/methods/admin.usergroups.listChannels](https://docs.slack.dev/reference/methods/admin.usergroups.listChannels) `def admin_usergroups_removeChannels(self, *, usergroup_id: str, channel_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_usergroups_removeChannels( self, *, usergroup_id: str, channel_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Add one or more default channels to an IDP group. https://docs.slack.dev/reference/methods/admin.usergroups.removeChannels """ kwargs.update({"usergroup_id": usergroup_id}) if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) return self.api_call("admin.usergroups.removeChannels", params=kwargs) ``` Add one or more default channels to an IDP group. [https://docs.slack.dev/reference/methods/admin.usergroups.removeChannels](https://docs.slack.dev/reference/methods/admin.usergroups.removeChannels) `def admin_users_assign(self, *, team_id: str, user_id: str, channel_ids: str | Sequence[str] | None = None, is_restricted: bool | None = None, is_ultra_restricted: bool | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_users_assign( self, *, team_id: str, user_id: str, channel_ids: Optional[Union[str, Sequence[str]]] = None, is_restricted: Optional[bool] = None, is_ultra_restricted: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Add an Enterprise user to a workspace. https://docs.slack.dev/reference/methods/admin.users.assign """ kwargs.update( { "team_id": team_id, "user_id": user_id, "is_restricted": is_restricted, "is_ultra_restricted": is_ultra_restricted, } ) if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) return self.api_call("admin.users.assign", params=kwargs) ``` Add an Enterprise user to a workspace. [https://docs.slack.dev/reference/methods/admin.users.assign](https://docs.slack.dev/reference/methods/admin.users.assign) `def admin_users_invite(self, *, team_id: str, email: str, channel_ids: str | Sequence[str], custom_message: str | None = None, email_password_policy_enabled: bool | None = None, guest_expiration_ts: str | float | None = None, is_restricted: bool | None = None, is_ultra_restricted: bool | None = None, real_name: str | None = None, resend: bool | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_users_invite( self, *, team_id: str, email: str, channel_ids: Union[str, Sequence[str]], custom_message: Optional[str] = None, email_password_policy_enabled: Optional[bool] = None, guest_expiration_ts: Optional[Union[str, float]] = None, is_restricted: Optional[bool] = None, is_ultra_restricted: Optional[bool] = None, real_name: Optional[str] = None, resend: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Invite a user to a workspace. https://docs.slack.dev/reference/methods/admin.users.invite """ kwargs.update( { "team_id": team_id, "email": email, "custom_message": custom_message, "email_password_policy_enabled": email_password_policy_enabled, "guest_expiration_ts": str(guest_expiration_ts) if guest_expiration_ts is not None else None, "is_restricted": is_restricted, "is_ultra_restricted": is_ultra_restricted, "real_name": real_name, "resend": resend, } ) if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) return self.api_call("admin.users.invite", params=kwargs) ``` Invite a user to a workspace. [https://docs.slack.dev/reference/methods/admin.users.invite](https://docs.slack.dev/reference/methods/admin.users.invite) `def admin_users_list(self, *, team_id: str | None = None, include_deactivated_user_workspaces: bool | None = None, is_active: bool | None = None, cursor: str | None = None, limit: int | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_users_list( self, *, team_id: Optional[str] = None, include_deactivated_user_workspaces: Optional[bool] = None, is_active: Optional[bool] = None, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """List users on a workspace https://docs.slack.dev/reference/methods/admin.users.list """ kwargs.update( { "team_id": team_id, "include_deactivated_user_workspaces": include_deactivated_user_workspaces, "is_active": is_active, "cursor": cursor, "limit": limit, } ) return self.api_call("admin.users.list", params=kwargs) ``` List users on a workspace [https://docs.slack.dev/reference/methods/admin.users.list](https://docs.slack.dev/reference/methods/admin.users.list) `def admin_users_remove(self, *, team_id: str, user_id: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_users_remove( self, *, team_id: str, user_id: str, **kwargs, ) -> SlackResponse: """Remove a user from a workspace. https://docs.slack.dev/reference/methods/admin.users.remove """ kwargs.update({"team_id": team_id, "user_id": user_id}) return self.api_call("admin.users.remove", params=kwargs) ``` Remove a user from a workspace. [https://docs.slack.dev/reference/methods/admin.users.remove](https://docs.slack.dev/reference/methods/admin.users.remove) `def admin_users_session_clearSettings(self, *, user_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_users_session_clearSettings( self, *, user_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Clear user-specific session settings—the session duration and what happens when the client closes—for a list of users. https://docs.slack.dev/reference/methods/admin.users.session.clearSettings """ if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) return self.api_call("admin.users.session.clearSettings", params=kwargs) ``` Clear user-specific session settings—the session duration and what happens when the client closes—for a list of users. [https://docs.slack.dev/reference/methods/admin.users.session.clearSettings](https://docs.slack.dev/reference/methods/admin.users.session.clearSettings) `def admin_users_session_getSettings(self, *, user_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_users_session_getSettings( self, *, user_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Get user-specific session settings—the session duration and what happens when the client closes—given a list of users. https://docs.slack.dev/reference/methods/admin.users.session.getSettings """ if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) return self.api_call("admin.users.session.getSettings", params=kwargs) ``` Get user-specific session settings—the session duration and what happens when the client closes—given a list of users. [https://docs.slack.dev/reference/methods/admin.users.session.getSettings](https://docs.slack.dev/reference/methods/admin.users.session.getSettings) `def admin_users_session_invalidate(self, *, session_id: str, team_id: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_users_session_invalidate( self, *, session_id: str, team_id: str, **kwargs, ) -> SlackResponse: """Invalidate a single session for a user by session_id. https://docs.slack.dev/reference/methods/admin.users.session.invalidate """ kwargs.update({"session_id": session_id, "team_id": team_id}) return self.api_call("admin.users.session.invalidate", params=kwargs) ``` Invalidate a single session for a user by session\_id. [https://docs.slack.dev/reference/methods/admin.users.session.invalidate](https://docs.slack.dev/reference/methods/admin.users.session.invalidate) `def admin_users_session_list(self, *, cursor: str | None = None, limit: int | None = None, team_id: str | None = None, user_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_users_session_list( self, *, cursor: Optional[str] = None, limit: Optional[int] = None, team_id: Optional[str] = None, user_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists all active user sessions for an organization https://docs.slack.dev/reference/methods/admin.users.session.list """ kwargs.update( { "cursor": cursor, "limit": limit, "team_id": team_id, "user_id": user_id, } ) return self.api_call("admin.users.session.list", params=kwargs) ``` Lists all active user sessions for an organization [https://docs.slack.dev/reference/methods/admin.users.session.list](https://docs.slack.dev/reference/methods/admin.users.session.list) `def admin_users_session_reset(self, *, user_id: str, mobile_only: bool | None = None, web_only: bool | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_users_session_reset( self, *, user_id: str, mobile_only: Optional[bool] = None, web_only: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Wipes all valid sessions on all devices for a given user. https://docs.slack.dev/reference/methods/admin.users.session.reset """ kwargs.update( { "user_id": user_id, "mobile_only": mobile_only, "web_only": web_only, } ) return self.api_call("admin.users.session.reset", params=kwargs) ``` Wipes all valid sessions on all devices for a given user. [https://docs.slack.dev/reference/methods/admin.users.session.reset](https://docs.slack.dev/reference/methods/admin.users.session.reset) `def admin_users_session_resetBulk(self, *, user_ids: str | Sequence[str], mobile_only: bool | None = None, web_only: bool | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_users_session_resetBulk( self, *, user_ids: Union[str, Sequence[str]], mobile_only: Optional[bool] = None, web_only: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Enqueues an asynchronous job to wipe all valid sessions on all devices for a given list of users https://docs.slack.dev/reference/methods/admin.users.session.resetBulk """ if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) kwargs.update( { "mobile_only": mobile_only, "web_only": web_only, } ) return self.api_call("admin.users.session.resetBulk", params=kwargs) ``` Enqueues an asynchronous job to wipe all valid sessions on all devices for a given list of users [https://docs.slack.dev/reference/methods/admin.users.session.resetBulk](https://docs.slack.dev/reference/methods/admin.users.session.resetBulk) `def admin_users_session_setSettings(self, *, user_ids: str | Sequence[str], desktop_app_browser_quit: bool | None = None, duration: int | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_users_session_setSettings( self, *, user_ids: Union[str, Sequence[str]], desktop_app_browser_quit: Optional[bool] = None, duration: Optional[int] = None, **kwargs, ) -> SlackResponse: """Configure the user-level session settings—the session duration and what happens when the client closes—for one or more users. https://docs.slack.dev/reference/methods/admin.users.session.setSettings """ if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) kwargs.update( { "desktop_app_browser_quit": desktop_app_browser_quit, "duration": duration, } ) return self.api_call("admin.users.session.setSettings", params=kwargs) ``` Configure the user-level session settings—the session duration and what happens when the client closes—for one or more users. [https://docs.slack.dev/reference/methods/admin.users.session.setSettings](https://docs.slack.dev/reference/methods/admin.users.session.setSettings) `def admin_users_setAdmin(self, *, team_id: str, user_id: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_users_setAdmin( self, *, team_id: str, user_id: str, **kwargs, ) -> SlackResponse: """Set an existing guest, regular user, or owner to be an admin user. https://docs.slack.dev/reference/methods/admin.users.setAdmin """ kwargs.update({"team_id": team_id, "user_id": user_id}) return self.api_call("admin.users.setAdmin", params=kwargs) ``` Set an existing guest, regular user, or owner to be an admin user. [https://docs.slack.dev/reference/methods/admin.users.setAdmin](https://docs.slack.dev/reference/methods/admin.users.setAdmin) `def admin_users_setExpiration(self, *, expiration_ts: int, user_id: str, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_users_setExpiration( self, *, expiration_ts: int, user_id: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Set an expiration for a guest user. https://docs.slack.dev/reference/methods/admin.users.setExpiration """ kwargs.update({"expiration_ts": expiration_ts, "team_id": team_id, "user_id": user_id}) return self.api_call("admin.users.setExpiration", params=kwargs) ``` Set an expiration for a guest user. [https://docs.slack.dev/reference/methods/admin.users.setExpiration](https://docs.slack.dev/reference/methods/admin.users.setExpiration) `def admin_users_setOwner(self, *, team_id: str, user_id: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_users_setOwner( self, *, team_id: str, user_id: str, **kwargs, ) -> SlackResponse: """Set an existing guest, regular user, or admin user to be a workspace owner. https://docs.slack.dev/reference/methods/admin.users.setOwner """ kwargs.update({"team_id": team_id, "user_id": user_id}) return self.api_call("admin.users.setOwner", params=kwargs) ``` Set an existing guest, regular user, or admin user to be a workspace owner. [https://docs.slack.dev/reference/methods/admin.users.setOwner](https://docs.slack.dev/reference/methods/admin.users.setOwner) `def admin_users_setRegular(self, *, team_id: str, user_id: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_users_setRegular( self, *, team_id: str, user_id: str, **kwargs, ) -> SlackResponse: """Set an existing guest user, admin user, or owner to be a regular user. https://docs.slack.dev/reference/methods/admin.users.setRegular """ kwargs.update({"team_id": team_id, "user_id": user_id}) return self.api_call("admin.users.setRegular", params=kwargs) ``` Set an existing guest user, admin user, or owner to be a regular user. [https://docs.slack.dev/reference/methods/admin.users.setRegular](https://docs.slack.dev/reference/methods/admin.users.setRegular) `def admin_users_unsupportedVersions_export(self, *, date_end_of_support: str | int | None = None, date_sessions_started: str | int | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_users_unsupportedVersions_export( self, *, date_end_of_support: Optional[Union[str, int]] = None, date_sessions_started: Optional[Union[str, int]] = None, **kwargs, ) -> SlackResponse: """Ask Slackbot to send you an export listing all workspace members using unsupported software, presented as a zipped CSV file. https://docs.slack.dev/reference/methods/admin.users.unsupportedVersions.export """ kwargs.update( { "date_end_of_support": date_end_of_support, "date_sessions_started": date_sessions_started, } ) return self.api_call("admin.users.unsupportedVersions.export", params=kwargs) ``` Ask Slackbot to send you an export listing all workspace members using unsupported software, presented as a zipped CSV file. [https://docs.slack.dev/reference/methods/admin.users.unsupportedVersions.export](https://docs.slack.dev/reference/methods/admin.users.unsupportedVersions.export) `def admin_workflows_collaborators_add(self, *, collaborator_ids: str | Sequence[str], workflow_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_workflows_collaborators_add( self, *, collaborator_ids: Union[str, Sequence[str]], workflow_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Add collaborators to workflows within the team or enterprise https://docs.slack.dev/reference/methods/admin.workflows.collaborators.add """ if isinstance(collaborator_ids, (list, tuple)): kwargs.update({"collaborator_ids": ",".join(collaborator_ids)}) else: kwargs.update({"collaborator_ids": collaborator_ids}) if isinstance(workflow_ids, (list, tuple)): kwargs.update({"workflow_ids": ",".join(workflow_ids)}) else: kwargs.update({"workflow_ids": workflow_ids}) return self.api_call("admin.workflows.collaborators.add", params=kwargs) ``` Add collaborators to workflows within the team or enterprise [https://docs.slack.dev/reference/methods/admin.workflows.collaborators.add](https://docs.slack.dev/reference/methods/admin.workflows.collaborators.add) `def admin_workflows_collaborators_remove(self, *, collaborator_ids: str | Sequence[str], workflow_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_workflows_collaborators_remove( self, *, collaborator_ids: Union[str, Sequence[str]], workflow_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Remove collaborators from workflows within the team or enterprise https://docs.slack.dev/reference/methods/admin.workflows.collaborators.remove """ if isinstance(collaborator_ids, (list, tuple)): kwargs.update({"collaborator_ids": ",".join(collaborator_ids)}) else: kwargs.update({"collaborator_ids": collaborator_ids}) if isinstance(workflow_ids, (list, tuple)): kwargs.update({"workflow_ids": ",".join(workflow_ids)}) else: kwargs.update({"workflow_ids": workflow_ids}) return self.api_call("admin.workflows.collaborators.remove", params=kwargs) ``` Remove collaborators from workflows within the team or enterprise [https://docs.slack.dev/reference/methods/admin.workflows.collaborators.remove](https://docs.slack.dev/reference/methods/admin.workflows.collaborators.remove) `def admin_workflows_permissions_lookup(self, *, workflow_ids: str | Sequence[str], max_workflow_triggers: int | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_workflows_permissions_lookup( self, *, workflow_ids: Union[str, Sequence[str]], max_workflow_triggers: Optional[int] = None, **kwargs, ) -> SlackResponse: """Look up the permissions for a set of workflows https://docs.slack.dev/reference/methods/admin.workflows.permissions.lookup """ if isinstance(workflow_ids, (list, tuple)): kwargs.update({"workflow_ids": ",".join(workflow_ids)}) else: kwargs.update({"workflow_ids": workflow_ids}) kwargs.update( { "max_workflow_triggers": max_workflow_triggers, } ) return self.api_call("admin.workflows.permissions.lookup", params=kwargs) ``` Look up the permissions for a set of workflows [https://docs.slack.dev/reference/methods/admin.workflows.permissions.lookup](https://docs.slack.dev/reference/methods/admin.workflows.permissions.lookup) `def admin_workflows_search(self, *, app_id: str | None = None, collaborator_ids: str | Sequence[str] | None = None, cursor: str | None = None, limit: int | None = None, no_collaborators: bool | None = None, num_trigger_ids: int | None = None, query: str | None = None, sort: str | None = None, sort_dir: str | None = None, source: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_workflows_search( self, *, app_id: Optional[str] = None, collaborator_ids: Optional[Union[str, Sequence[str]]] = None, cursor: Optional[str] = None, limit: Optional[int] = None, no_collaborators: Optional[bool] = None, num_trigger_ids: Optional[int] = None, query: Optional[str] = None, sort: Optional[str] = None, sort_dir: Optional[str] = None, source: Optional[str] = None, **kwargs, ) -> SlackResponse: """Search workflows within the team or enterprise https://docs.slack.dev/reference/methods/admin.workflows.search """ if collaborator_ids is not None: if isinstance(collaborator_ids, (list, tuple)): kwargs.update({"collaborator_ids": ",".join(collaborator_ids)}) else: kwargs.update({"collaborator_ids": collaborator_ids}) kwargs.update( { "app_id": app_id, "cursor": cursor, "limit": limit, "no_collaborators": no_collaborators, "num_trigger_ids": num_trigger_ids, "query": query, "sort": sort, "sort_dir": sort_dir, "source": source, } ) return self.api_call("admin.workflows.search", params=kwargs) ``` Search workflows within the team or enterprise [https://docs.slack.dev/reference/methods/admin.workflows.search](https://docs.slack.dev/reference/methods/admin.workflows.search) `def admin_workflows_unpublish(self, *, workflow_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def admin_workflows_unpublish( self, *, workflow_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Unpublish workflows within the team or enterprise https://docs.slack.dev/reference/methods/admin.workflows.unpublish """ if isinstance(workflow_ids, (list, tuple)): kwargs.update({"workflow_ids": ",".join(workflow_ids)}) else: kwargs.update({"workflow_ids": workflow_ids}) return self.api_call("admin.workflows.unpublish", params=kwargs) ``` Unpublish workflows within the team or enterprise [https://docs.slack.dev/reference/methods/admin.workflows.unpublish](https://docs.slack.dev/reference/methods/admin.workflows.unpublish) `def api_test(self, *, error: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def api_test( self, *, error: Optional[str] = None, **kwargs, ) -> SlackResponse: """Checks API calling code. https://docs.slack.dev/reference/methods/api.test """ kwargs.update({"error": error}) return self.api_call("api.test", params=kwargs) ``` Checks API calling code. [https://docs.slack.dev/reference/methods/api.test](https://docs.slack.dev/reference/methods/api.test) `def apps_connections_open(self, *, app_token: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def apps_connections_open( self, *, app_token: str, **kwargs, ) -> SlackResponse: """Generate a temporary Socket Mode WebSocket URL that your app can connect to in order to receive events and interactive payloads https://docs.slack.dev/reference/methods/apps.connections.open """ kwargs.update({"token": app_token}) return self.api_call("apps.connections.open", http_verb="POST", params=kwargs) ``` Generate a temporary Socket Mode WebSocket URL that your app can connect to in order to receive events and interactive payloads [https://docs.slack.dev/reference/methods/apps.connections.open](https://docs.slack.dev/reference/methods/apps.connections.open) `def apps_event_authorizations_list(self, *, event_context: str, cursor: str | None = None, limit: int | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def apps_event_authorizations_list( self, *, event_context: str, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Get a list of authorizations for the given event context. Each authorization represents an app installation that the event is visible to. https://docs.slack.dev/reference/methods/apps.event.authorizations.list """ kwargs.update({"event_context": event_context, "cursor": cursor, "limit": limit}) return self.api_call("apps.event.authorizations.list", params=kwargs) ``` Get a list of authorizations for the given event context. Each authorization represents an app installation that the event is visible to. [https://docs.slack.dev/reference/methods/apps.event.authorizations.list](https://docs.slack.dev/reference/methods/apps.event.authorizations.list) `def apps_manifest_create(self, *, manifest: str | Dict[str, Any], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def apps_manifest_create( self, *, manifest: Union[str, Dict[str, Any]], **kwargs, ) -> SlackResponse: """Create an app from an app manifest https://docs.slack.dev/reference/methods/apps.manifest.create """ if isinstance(manifest, str): kwargs.update({"manifest": manifest}) else: kwargs.update({"manifest": json.dumps(manifest)}) return self.api_call("apps.manifest.create", params=kwargs) ``` Create an app from an app manifest [https://docs.slack.dev/reference/methods/apps.manifest.create](https://docs.slack.dev/reference/methods/apps.manifest.create) `def apps_manifest_delete(self, *, app_id: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def apps_manifest_delete( self, *, app_id: str, **kwargs, ) -> SlackResponse: """Permanently deletes an app created through app manifests https://docs.slack.dev/reference/methods/apps.manifest.delete """ kwargs.update({"app_id": app_id}) return self.api_call("apps.manifest.delete", params=kwargs) ``` Permanently deletes an app created through app manifests [https://docs.slack.dev/reference/methods/apps.manifest.delete](https://docs.slack.dev/reference/methods/apps.manifest.delete) `def apps_manifest_export(self, *, app_id: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def apps_manifest_export( self, *, app_id: str, **kwargs, ) -> SlackResponse: """Export an app manifest from an existing app https://docs.slack.dev/reference/methods/apps.manifest.export """ kwargs.update({"app_id": app_id}) return self.api_call("apps.manifest.export", params=kwargs) ``` Export an app manifest from an existing app [https://docs.slack.dev/reference/methods/apps.manifest.export](https://docs.slack.dev/reference/methods/apps.manifest.export) `def apps_manifest_update(self, *, app_id: str, manifest: str | Dict[str, Any], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def apps_manifest_update( self, *, app_id: str, manifest: Union[str, Dict[str, Any]], **kwargs, ) -> SlackResponse: """Update an app from an app manifest https://docs.slack.dev/reference/methods/apps.manifest.update """ if isinstance(manifest, str): kwargs.update({"manifest": manifest}) else: kwargs.update({"manifest": json.dumps(manifest)}) kwargs.update({"app_id": app_id}) return self.api_call("apps.manifest.update", params=kwargs) ``` Update an app from an app manifest [https://docs.slack.dev/reference/methods/apps.manifest.update](https://docs.slack.dev/reference/methods/apps.manifest.update) `def apps_manifest_validate(self, *, manifest: str | Dict[str, Any], app_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def apps_manifest_validate( self, *, manifest: Union[str, Dict[str, Any]], app_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Validate an app manifest https://docs.slack.dev/reference/methods/apps.manifest.validate """ if isinstance(manifest, str): kwargs.update({"manifest": manifest}) else: kwargs.update({"manifest": json.dumps(manifest)}) kwargs.update({"app_id": app_id}) return self.api_call("apps.manifest.validate", params=kwargs) ``` Validate an app manifest [https://docs.slack.dev/reference/methods/apps.manifest.validate](https://docs.slack.dev/reference/methods/apps.manifest.validate) `def apps_uninstall(self, *, client_id: str, client_secret: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def apps_uninstall( self, *, client_id: str, client_secret: str, **kwargs, ) -> SlackResponse: """Uninstalls your app from a workspace. https://docs.slack.dev/reference/methods/apps.uninstall """ kwargs.update({"client_id": client_id, "client_secret": client_secret}) return self.api_call("apps.uninstall", params=kwargs) ``` Uninstalls your app from a workspace. [https://docs.slack.dev/reference/methods/apps.uninstall](https://docs.slack.dev/reference/methods/apps.uninstall) `def apps_user_connection_update(self, *, user_id: str, status: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def apps_user_connection_update( self, *, user_id: str, status: str, **kwargs, ) -> SlackResponse: """Updates the connection status between a user and an app. https://docs.slack.dev/reference/methods/apps.user.connection.update """ kwargs.update({"user_id": user_id, "status": status}) return self.api_call("apps.user.connection.update", params=kwargs) ``` Updates the connection status between a user and an app. [https://docs.slack.dev/reference/methods/apps.user.connection.update](https://docs.slack.dev/reference/methods/apps.user.connection.update) `def assistant_threads_setStatus(self, *, channel_id: str, thread_ts: str, status: str, loading_messages: List[str] | None = None, icon_emoji: str | None = None, icon_url: str | None = None, username: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def assistant_threads_setStatus( self, *, channel_id: str, thread_ts: str, status: str, loading_messages: Optional[List[str]] = None, icon_emoji: Optional[str] = None, icon_url: Optional[str] = None, username: Optional[str] = None, **kwargs, ) -> SlackResponse: """Set the status for an AI assistant thread. https://docs.slack.dev/reference/methods/assistant.threads.setStatus """ kwargs.update( { "channel_id": channel_id, "thread_ts": thread_ts, "status": status, "loading_messages": loading_messages, "icon_emoji": icon_emoji, "icon_url": icon_url, "username": username, } ) kwargs = _remove_none_values(kwargs) return self.api_call("assistant.threads.setStatus", json=kwargs) ``` Set the status for an AI assistant thread. [https://docs.slack.dev/reference/methods/assistant.threads.setStatus](https://docs.slack.dev/reference/methods/assistant.threads.setStatus) `def assistant_threads_setSuggestedPrompts(self, *, channel_id: str, thread_ts: str | None = None, title: str | None = None, prompts: List[Dict[str, str]], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def assistant_threads_setSuggestedPrompts( self, *, channel_id: str, thread_ts: Optional[str] = None, title: Optional[str] = None, prompts: List[Dict[str, str]], **kwargs, ) -> SlackResponse: """Set suggested prompts for the given assistant thread. https://docs.slack.dev/reference/methods/assistant.threads.setSuggestedPrompts """ kwargs.update({"channel_id": channel_id, "prompts": prompts}) if thread_ts is not None: kwargs.update({"thread_ts": thread_ts}) if title is not None: kwargs.update({"title": title}) return self.api_call("assistant.threads.setSuggestedPrompts", json=kwargs) ``` Set suggested prompts for the given assistant thread. [https://docs.slack.dev/reference/methods/assistant.threads.setSuggestedPrompts](https://docs.slack.dev/reference/methods/assistant.threads.setSuggestedPrompts) `def assistant_threads_setTitle(self, *, channel_id: str, thread_ts: str, title: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def assistant_threads_setTitle( self, *, channel_id: str, thread_ts: str, title: str, **kwargs, ) -> SlackResponse: """Set the title for the given assistant thread. https://docs.slack.dev/reference/methods/assistant.threads.setTitle """ kwargs.update({"channel_id": channel_id, "thread_ts": thread_ts, "title": title}) return self.api_call("assistant.threads.setTitle", params=kwargs) ``` Set the title for the given assistant thread. [https://docs.slack.dev/reference/methods/assistant.threads.setTitle](https://docs.slack.dev/reference/methods/assistant.threads.setTitle) `def auth_revoke(self, *, test: bool | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def auth_revoke( self, *, test: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Revokes a token. https://docs.slack.dev/reference/methods/auth.revoke """ kwargs.update({"test": test}) return self.api_call("auth.revoke", http_verb="GET", params=kwargs) ``` Revokes a token. [https://docs.slack.dev/reference/methods/auth.revoke](https://docs.slack.dev/reference/methods/auth.revoke) `def auth_teams_list(self, cursor: str | None = None, limit: int | None = None, include_icon: bool | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def auth_teams_list( self, cursor: Optional[str] = None, limit: Optional[int] = None, include_icon: Optional[bool] = None, **kwargs, ) -> SlackResponse: """List the workspaces a token can access. https://docs.slack.dev/reference/methods/auth.teams.list """ kwargs.update({"cursor": cursor, "limit": limit, "include_icon": include_icon}) return self.api_call("auth.teams.list", params=kwargs) ``` List the workspaces a token can access. [https://docs.slack.dev/reference/methods/auth.teams.list](https://docs.slack.dev/reference/methods/auth.teams.list) `def auth_test(self, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def auth_test( self, **kwargs, ) -> SlackResponse: """Checks authentication & identity. https://docs.slack.dev/reference/methods/auth.test """ return self.api_call("auth.test", params=kwargs) ``` Checks authentication & identity. [https://docs.slack.dev/reference/methods/auth.test](https://docs.slack.dev/reference/methods/auth.test) `def bookmarks_add(self, *, channel_id: str, title: str, type: str, emoji: str | None = None, entity_id: str | None = None, link: str | None = None, parent_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def bookmarks_add( self, *, channel_id: str, title: str, type: str, emoji: Optional[str] = None, entity_id: Optional[str] = None, link: Optional[str] = None, # include when type is 'link' parent_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Add bookmark to a channel. https://docs.slack.dev/reference/methods/bookmarks.add """ kwargs.update( { "channel_id": channel_id, "title": title, "type": type, "emoji": emoji, "entity_id": entity_id, "link": link, "parent_id": parent_id, } ) return self.api_call("bookmarks.add", http_verb="POST", params=kwargs) ``` Add bookmark to a channel. [https://docs.slack.dev/reference/methods/bookmarks.add](https://docs.slack.dev/reference/methods/bookmarks.add) `def bookmarks_edit(self, *, bookmark_id: str, channel_id: str, emoji: str | None = None, link: str | None = None, title: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def bookmarks_edit( self, *, bookmark_id: str, channel_id: str, emoji: Optional[str] = None, link: Optional[str] = None, title: Optional[str] = None, **kwargs, ) -> SlackResponse: """Edit bookmark. https://docs.slack.dev/reference/methods/bookmarks.edit """ kwargs.update( { "bookmark_id": bookmark_id, "channel_id": channel_id, "emoji": emoji, "link": link, "title": title, } ) return self.api_call("bookmarks.edit", http_verb="POST", params=kwargs) ``` Edit bookmark. [https://docs.slack.dev/reference/methods/bookmarks.edit](https://docs.slack.dev/reference/methods/bookmarks.edit) `def bookmarks_list(self, *, channel_id: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def bookmarks_list( self, *, channel_id: str, **kwargs, ) -> SlackResponse: """List bookmark for the channel. https://docs.slack.dev/reference/methods/bookmarks.list """ kwargs.update({"channel_id": channel_id}) return self.api_call("bookmarks.list", http_verb="POST", params=kwargs) ``` List bookmark for the channel. [https://docs.slack.dev/reference/methods/bookmarks.list](https://docs.slack.dev/reference/methods/bookmarks.list) `def bookmarks_remove(self, *, bookmark_id: str, channel_id: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def bookmarks_remove( self, *, bookmark_id: str, channel_id: str, **kwargs, ) -> SlackResponse: """Remove bookmark from the channel. https://docs.slack.dev/reference/methods/bookmarks.remove """ kwargs.update({"bookmark_id": bookmark_id, "channel_id": channel_id}) return self.api_call("bookmarks.remove", http_verb="POST", params=kwargs) ``` Remove bookmark from the channel. [https://docs.slack.dev/reference/methods/bookmarks.remove](https://docs.slack.dev/reference/methods/bookmarks.remove) `def bots_info(self, *, bot: str | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def bots_info( self, *, bot: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Gets information about a bot user. https://docs.slack.dev/reference/methods/bots.info """ kwargs.update({"bot": bot, "team_id": team_id}) return self.api_call("bots.info", http_verb="GET", params=kwargs) ``` Gets information about a bot user. [https://docs.slack.dev/reference/methods/bots.info](https://docs.slack.dev/reference/methods/bots.info) `def calls_add(self, *, external_unique_id: str, join_url: str, created_by: str | None = None, date_start: int | None = None, desktop_app_join_url: str | None = None, external_display_id: str | None = None, title: str | None = None, users: str | Sequence[Dict[str, str]] | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def calls_add( self, *, external_unique_id: str, join_url: str, created_by: Optional[str] = None, date_start: Optional[int] = None, desktop_app_join_url: Optional[str] = None, external_display_id: Optional[str] = None, title: Optional[str] = None, users: Optional[Union[str, Sequence[Dict[str, str]]]] = None, **kwargs, ) -> SlackResponse: """Registers a new Call. https://docs.slack.dev/reference/methods/calls.add """ kwargs.update( { "external_unique_id": external_unique_id, "join_url": join_url, "created_by": created_by, "date_start": date_start, "desktop_app_join_url": desktop_app_join_url, "external_display_id": external_display_id, "title": title, } ) _update_call_participants( kwargs, users if users is not None else kwargs.get("users"), # type: ignore[arg-type] ) return self.api_call("calls.add", http_verb="POST", params=kwargs) ``` Registers a new Call. [https://docs.slack.dev/reference/methods/calls.add](https://docs.slack.dev/reference/methods/calls.add) `def calls_end(self, *, id: str, duration: int | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def calls_end( self, *, id: str, duration: Optional[int] = None, **kwargs, ) -> SlackResponse: """Ends a Call. https://docs.slack.dev/reference/methods/calls.end """ kwargs.update({"id": id, "duration": duration}) return self.api_call("calls.end", http_verb="POST", params=kwargs) ``` Ends a Call. [https://docs.slack.dev/reference/methods/calls.end](https://docs.slack.dev/reference/methods/calls.end) `def calls_info(self, *, id: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def calls_info( self, *, id: str, **kwargs, ) -> SlackResponse: """Returns information about a Call. https://docs.slack.dev/reference/methods/calls.info """ kwargs.update({"id": id}) return self.api_call("calls.info", http_verb="POST", params=kwargs) ``` Returns information about a Call. [https://docs.slack.dev/reference/methods/calls.info](https://docs.slack.dev/reference/methods/calls.info) `def calls_participants_add(self, *, id: str, users: str | Sequence[Dict[str, str]], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def calls_participants_add( self, *, id: str, users: Union[str, Sequence[Dict[str, str]]], **kwargs, ) -> SlackResponse: """Registers new participants added to a Call. https://docs.slack.dev/reference/methods/calls.participants.add """ kwargs.update({"id": id}) _update_call_participants(kwargs, users) return self.api_call("calls.participants.add", http_verb="POST", params=kwargs) ``` Registers new participants added to a Call. [https://docs.slack.dev/reference/methods/calls.participants.add](https://docs.slack.dev/reference/methods/calls.participants.add) `def calls_participants_remove(self, *, id: str, users: str | Sequence[Dict[str, str]], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def calls_participants_remove( self, *, id: str, users: Union[str, Sequence[Dict[str, str]]], **kwargs, ) -> SlackResponse: """Registers participants removed from a Call. https://docs.slack.dev/reference/methods/calls.participants.remove """ kwargs.update({"id": id}) _update_call_participants(kwargs, users) return self.api_call("calls.participants.remove", http_verb="POST", params=kwargs) ``` Registers participants removed from a Call. [https://docs.slack.dev/reference/methods/calls.participants.remove](https://docs.slack.dev/reference/methods/calls.participants.remove) `def calls_update(self, *, id: str, desktop_app_join_url: str | None = None, join_url: str | None = None, title: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def calls_update( self, *, id: str, desktop_app_join_url: Optional[str] = None, join_url: Optional[str] = None, title: Optional[str] = None, **kwargs, ) -> SlackResponse: """Updates information about a Call. https://docs.slack.dev/reference/methods/calls.update """ kwargs.update( { "id": id, "desktop_app_join_url": desktop_app_join_url, "join_url": join_url, "title": title, } ) return self.api_call("calls.update", http_verb="POST", params=kwargs) ``` Updates information about a Call. [https://docs.slack.dev/reference/methods/calls.update](https://docs.slack.dev/reference/methods/calls.update) `def canvases_access_delete(self, *, canvas_id: str, channel_ids: str | Sequence[str] | None = None, user_ids: str | Sequence[str] | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def canvases_access_delete( self, *, canvas_id: str, channel_ids: Optional[Union[Sequence[str], str]] = None, user_ids: Optional[Union[Sequence[str], str]] = None, **kwargs, ) -> SlackResponse: """Create a Channel Canvas for a channel https://docs.slack.dev/reference/methods/canvases.access.delete """ kwargs.update({"canvas_id": canvas_id}) if channel_ids is not None: if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) if user_ids is not None: if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) return self.api_call("canvases.access.delete", params=kwargs) ``` Create a Channel Canvas for a channel [https://docs.slack.dev/reference/methods/canvases.access.delete](https://docs.slack.dev/reference/methods/canvases.access.delete) `def canvases_access_set(self, *, canvas_id: str, access_level: str, channel_ids: str | Sequence[str] | None = None, user_ids: str | Sequence[str] | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def canvases_access_set( self, *, canvas_id: str, access_level: str, channel_ids: Optional[Union[Sequence[str], str]] = None, user_ids: Optional[Union[Sequence[str], str]] = None, **kwargs, ) -> SlackResponse: """Sets the access level to a canvas for specified entities https://docs.slack.dev/reference/methods/canvases.access.set """ kwargs.update({"canvas_id": canvas_id, "access_level": access_level}) if channel_ids is not None: if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) if user_ids is not None: if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) return self.api_call("canvases.access.set", params=kwargs) ``` Sets the access level to a canvas for specified entities [https://docs.slack.dev/reference/methods/canvases.access.set](https://docs.slack.dev/reference/methods/canvases.access.set) `def canvases_create(self, *, title: str | None = None, document_content: Dict[str, str], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def canvases_create( self, *, title: Optional[str] = None, document_content: Dict[str, str], **kwargs, ) -> SlackResponse: """Create Canvas for a user https://docs.slack.dev/reference/methods/canvases.create """ kwargs.update({"title": title, "document_content": document_content}) return self.api_call("canvases.create", json=kwargs) ``` Create Canvas for a user [https://docs.slack.dev/reference/methods/canvases.create](https://docs.slack.dev/reference/methods/canvases.create) `def canvases_delete(self, *, canvas_id: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def canvases_delete( self, *, canvas_id: str, **kwargs, ) -> SlackResponse: """Deletes a canvas https://docs.slack.dev/reference/methods/canvases.delete """ kwargs.update({"canvas_id": canvas_id}) return self.api_call("canvases.delete", params=kwargs) ``` Deletes a canvas [https://docs.slack.dev/reference/methods/canvases.delete](https://docs.slack.dev/reference/methods/canvases.delete) `def canvases_edit(self, *, canvas_id: str, changes: Sequence[Dict[str, Any]], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def canvases_edit( self, *, canvas_id: str, changes: Sequence[Dict[str, Any]], **kwargs, ) -> SlackResponse: """Update an existing canvas https://docs.slack.dev/reference/methods/canvases.edit """ kwargs.update({"canvas_id": canvas_id, "changes": changes}) return self.api_call("canvases.edit", json=kwargs) ``` Update an existing canvas [https://docs.slack.dev/reference/methods/canvases.edit](https://docs.slack.dev/reference/methods/canvases.edit) `def canvases_sections_lookup(self, *, canvas_id: str, criteria: Dict[str, Any], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def canvases_sections_lookup( self, *, canvas_id: str, criteria: Dict[str, Any], **kwargs, ) -> SlackResponse: """Find sections matching the provided criteria https://docs.slack.dev/reference/methods/canvases.sections.lookup """ kwargs.update({"canvas_id": canvas_id, "criteria": json.dumps(criteria)}) return self.api_call("canvases.sections.lookup", params=kwargs) ``` Find sections matching the provided criteria [https://docs.slack.dev/reference/methods/canvases.sections.lookup](https://docs.slack.dev/reference/methods/canvases.sections.lookup) `def channels_archive(self, *, channel: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def channels_archive( self, *, channel: str, **kwargs, ) -> SlackResponse: """Archives a channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.archive", json=kwargs) ``` Archives a channel. `def channels_create(self, *, name: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def channels_create( self, *, name: str, **kwargs, ) -> SlackResponse: """Creates a channel.""" kwargs.update({"name": name}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.create", json=kwargs) ``` Creates a channel. `def channels_history(self, *, channel: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def channels_history( self, *, channel: str, **kwargs, ) -> SlackResponse: """Fetches history of messages and events from a channel.""" kwargs.update({"channel": channel}) return self.api_call("channels.history", http_verb="GET", params=kwargs) ``` Fetches history of messages and events from a channel. `def channels_info(self, *, channel: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def channels_info( self, *, channel: str, **kwargs, ) -> SlackResponse: """Gets information about a channel.""" kwargs.update({"channel": channel}) return self.api_call("channels.info", http_verb="GET", params=kwargs) ``` Gets information about a channel. `def channels_invite(self, *, channel: str, user: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def channels_invite( self, *, channel: str, user: str, **kwargs, ) -> SlackResponse: """Invites a user to a channel.""" kwargs.update({"channel": channel, "user": user}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.invite", json=kwargs) ``` Invites a user to a channel. `def channels_join(self, *, name: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def channels_join( self, *, name: str, **kwargs, ) -> SlackResponse: """Joins a channel, creating it if needed.""" kwargs.update({"name": name}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.join", json=kwargs) ``` Joins a channel, creating it if needed. `def channels_kick(self, *, channel: str, user: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def channels_kick( self, *, channel: str, user: str, **kwargs, ) -> SlackResponse: """Removes a user from a channel.""" kwargs.update({"channel": channel, "user": user}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.kick", json=kwargs) ``` Removes a user from a channel. `def channels_leave(self, *, channel: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def channels_leave( self, *, channel: str, **kwargs, ) -> SlackResponse: """Leaves a channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.leave", json=kwargs) ``` Leaves a channel. `def channels_list(self, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def channels_list( self, **kwargs, ) -> SlackResponse: """Lists all channels in a Slack team.""" return self.api_call("channels.list", http_verb="GET", params=kwargs) ``` Lists all channels in a Slack team. `def channels_mark(self, *, channel: str, ts: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def channels_mark( self, *, channel: str, ts: str, **kwargs, ) -> SlackResponse: """Sets the read cursor in a channel.""" kwargs.update({"channel": channel, "ts": ts}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.mark", json=kwargs) ``` Sets the read cursor in a channel. `def channels_rename(self, *, channel: str, name: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def channels_rename( self, *, channel: str, name: str, **kwargs, ) -> SlackResponse: """Renames a channel.""" kwargs.update({"channel": channel, "name": name}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.rename", json=kwargs) ``` Renames a channel. `def channels_replies(self, *, channel: str, thread_ts: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def channels_replies( self, *, channel: str, thread_ts: str, **kwargs, ) -> SlackResponse: """Retrieve a thread of messages posted to a channel""" kwargs.update({"channel": channel, "thread_ts": thread_ts}) return self.api_call("channels.replies", http_verb="GET", params=kwargs) ``` Retrieve a thread of messages posted to a channel `def channels_setPurpose(self, *, channel: str, purpose: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def channels_setPurpose( self, *, channel: str, purpose: str, **kwargs, ) -> SlackResponse: """Sets the purpose for a channel.""" kwargs.update({"channel": channel, "purpose": purpose}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.setPurpose", json=kwargs) ``` Sets the purpose for a channel. `def channels_setTopic(self, *, channel: str, topic: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def channels_setTopic( self, *, channel: str, topic: str, **kwargs, ) -> SlackResponse: """Sets the topic for a channel.""" kwargs.update({"channel": channel, "topic": topic}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.setTopic", json=kwargs) ``` Sets the topic for a channel. `def channels_unarchive(self, *, channel: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def channels_unarchive( self, *, channel: str, **kwargs, ) -> SlackResponse: """Unarchives a channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("channels.unarchive", json=kwargs) ``` Unarchives a channel. `def chat_appendStream(self, *, channel: str, ts: str, markdown_text: str | None = None, chunks: Sequence[Dict | [Chunk](../models/messages/chunk.html#slack_sdk.models.messages.chunk.Chunk "slack_sdk.models.messages.chunk.Chunk")] | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def chat_appendStream( self, *, channel: str, ts: str, markdown_text: Optional[str] = None, chunks: Optional[Sequence[Union[Dict, Chunk]]] = None, **kwargs, ) -> SlackResponse: """Appends text to an existing streaming conversation. https://docs.slack.dev/reference/methods/chat.appendStream """ kwargs.update( { "channel": channel, "ts": ts, "markdown_text": markdown_text, "chunks": chunks, } ) _parse_web_class_objects(kwargs) kwargs = _remove_none_values(kwargs) return self.api_call("chat.appendStream", json=kwargs) ``` Appends text to an existing streaming conversation. [https://docs.slack.dev/reference/methods/chat.appendStream](https://docs.slack.dev/reference/methods/chat.appendStream) `def chat_delete(self, *, channel: str, ts: str, as_user: bool | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def chat_delete( self, *, channel: str, ts: str, as_user: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Deletes a message. https://docs.slack.dev/reference/methods/chat.delete """ kwargs.update({"channel": channel, "ts": ts, "as_user": as_user}) return self.api_call("chat.delete", params=kwargs) ``` Deletes a message. [https://docs.slack.dev/reference/methods/chat.delete](https://docs.slack.dev/reference/methods/chat.delete) `def chat_deleteScheduledMessage(self, *, channel: str, scheduled_message_id: str, as_user: bool | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def chat_deleteScheduledMessage( self, *, channel: str, scheduled_message_id: str, as_user: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Deletes a scheduled message. https://docs.slack.dev/reference/methods/chat.deleteScheduledMessage """ kwargs.update( { "channel": channel, "scheduled_message_id": scheduled_message_id, "as_user": as_user, } ) return self.api_call("chat.deleteScheduledMessage", params=kwargs) ``` Deletes a scheduled message. [https://docs.slack.dev/reference/methods/chat.deleteScheduledMessage](https://docs.slack.dev/reference/methods/chat.deleteScheduledMessage) `def chat_getPermalink(self, *, channel: str, message_ts: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def chat_getPermalink( self, *, channel: str, message_ts: str, **kwargs, ) -> SlackResponse: """Retrieve a permalink URL for a specific extant message https://docs.slack.dev/reference/methods/chat.getPermalink """ kwargs.update({"channel": channel, "message_ts": message_ts}) return self.api_call("chat.getPermalink", http_verb="GET", params=kwargs) ``` Retrieve a permalink URL for a specific extant message [https://docs.slack.dev/reference/methods/chat.getPermalink](https://docs.slack.dev/reference/methods/chat.getPermalink) `def chat_meMessage(self, *, channel: str, text: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def chat_meMessage( self, *, channel: str, text: str, **kwargs, ) -> SlackResponse: """Share a me message into a channel. https://docs.slack.dev/reference/methods/chat.meMessage """ kwargs.update({"channel": channel, "text": text}) return self.api_call("chat.meMessage", params=kwargs) ``` Share a me message into a channel. [https://docs.slack.dev/reference/methods/chat.meMessage](https://docs.slack.dev/reference/methods/chat.meMessage) `def chat_postEphemeral(self, *, channel: str, user: str, text: str | None = None, as_user: bool | None = None, attachments: str | Sequence[Dict | [Attachment](../models/attachments/index.html#slack_sdk.models.attachments.Attachment "slack_sdk.models.attachments.Attachment")] | None = None, blocks: str | Sequence[Dict | [Block](../models/blocks/blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")] | None = None, thread_ts: str | None = None, icon_emoji: str | None = None, icon_url: str | None = None, link_names: bool | None = None, username: str | None = None, parse: str | None = None, markdown_text: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def chat_postEphemeral( self, *, channel: str, user: str, text: Optional[str] = None, as_user: Optional[bool] = None, attachments: Optional[Union[str, Sequence[Union[Dict, Attachment]]]] = None, blocks: Optional[Union[str, Sequence[Union[Dict, Block]]]] = None, thread_ts: Optional[str] = None, icon_emoji: Optional[str] = None, icon_url: Optional[str] = None, link_names: Optional[bool] = None, username: Optional[str] = None, parse: Optional[str] = None, markdown_text: Optional[str] = None, **kwargs, ) -> SlackResponse: """Sends an ephemeral message to a user in a channel. https://docs.slack.dev/reference/methods/chat.postEphemeral """ kwargs.update( { "channel": channel, "user": user, "text": text, "as_user": as_user, "attachments": attachments, "blocks": blocks, "thread_ts": thread_ts, "icon_emoji": icon_emoji, "icon_url": icon_url, "link_names": link_names, "username": username, "parse": parse, "markdown_text": markdown_text, } ) _parse_web_class_objects(kwargs) kwargs = _remove_none_values(kwargs) _warn_if_message_text_content_is_missing("chat.postEphemeral", kwargs) # NOTE: intentionally using json over params for the API methods using blocks/attachments return self.api_call("chat.postEphemeral", json=kwargs) ``` Sends an ephemeral message to a user in a channel. [https://docs.slack.dev/reference/methods/chat.postEphemeral](https://docs.slack.dev/reference/methods/chat.postEphemeral) `def chat_postMessage(self, *, channel: str, text: str | None = None, as_user: bool | None = None, attachments: str | Sequence[Dict | [Attachment](../models/attachments/index.html#slack_sdk.models.attachments.Attachment "slack_sdk.models.attachments.Attachment")] | None = None, blocks: str | Sequence[Dict | [Block](../models/blocks/blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")] | None = None, thread_ts: str | None = None, reply_broadcast: bool | None = None, unfurl_links: bool | None = None, unfurl_media: bool | None = None, container_id: str | None = None, icon_emoji: str | None = None, icon_url: str | None = None, mrkdwn: bool | None = None, link_names: bool | None = None, username: str | None = None, parse: str | None = None, metadata: Dict | [Metadata](../models/metadata/index.html#slack_sdk.models.metadata.Metadata "slack_sdk.models.metadata.Metadata") | [EventAndEntityMetadata](../models/metadata/index.html#slack_sdk.models.metadata.EventAndEntityMetadata "slack_sdk.models.metadata.EventAndEntityMetadata") | None = None, markdown_text: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def chat_postMessage( self, *, channel: str, text: Optional[str] = None, as_user: Optional[bool] = None, attachments: Optional[Union[str, Sequence[Union[Dict, Attachment]]]] = None, blocks: Optional[Union[str, Sequence[Union[Dict, Block]]]] = None, thread_ts: Optional[str] = None, reply_broadcast: Optional[bool] = None, unfurl_links: Optional[bool] = None, unfurl_media: Optional[bool] = None, container_id: Optional[str] = None, icon_emoji: Optional[str] = None, icon_url: Optional[str] = None, mrkdwn: Optional[bool] = None, link_names: Optional[bool] = None, username: Optional[str] = None, parse: Optional[str] = None, # none, full metadata: Optional[Union[Dict, Metadata, EventAndEntityMetadata]] = None, markdown_text: Optional[str] = None, **kwargs, ) -> SlackResponse: """Sends a message to a channel. https://docs.slack.dev/reference/methods/chat.postMessage """ kwargs.update( { "channel": channel, "text": text, "as_user": as_user, "attachments": attachments, "blocks": blocks, "thread_ts": thread_ts, "reply_broadcast": reply_broadcast, "unfurl_links": unfurl_links, "unfurl_media": unfurl_media, "container_id": container_id, "icon_emoji": icon_emoji, "icon_url": icon_url, "mrkdwn": mrkdwn, "link_names": link_names, "username": username, "parse": parse, "metadata": metadata, "markdown_text": markdown_text, } ) _parse_web_class_objects(kwargs) kwargs = _remove_none_values(kwargs) _warn_if_message_text_content_is_missing("chat.postMessage", kwargs) # NOTE: intentionally using json over params for the API methods using blocks/attachments return self.api_call("chat.postMessage", json=kwargs) ``` Sends a message to a channel. [https://docs.slack.dev/reference/methods/chat.postMessage](https://docs.slack.dev/reference/methods/chat.postMessage) `def chat_scheduleMessage(self, *, channel: str, post_at: str | int, text: str | None = None, as_user: bool | None = None, attachments: str | Sequence[Dict | [Attachment](../models/attachments/index.html#slack_sdk.models.attachments.Attachment "slack_sdk.models.attachments.Attachment")] | None = None, blocks: str | Sequence[Dict | [Block](../models/blocks/blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")] | None = None, thread_ts: str | None = None, parse: str | None = None, reply_broadcast: bool | None = None, unfurl_links: bool | None = None, unfurl_media: bool | None = None, link_names: bool | None = None, metadata: Dict | [Metadata](../models/metadata/index.html#slack_sdk.models.metadata.Metadata "slack_sdk.models.metadata.Metadata") | None = None, markdown_text: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def chat_scheduleMessage( self, *, channel: str, post_at: Union[str, int], text: Optional[str] = None, as_user: Optional[bool] = None, attachments: Optional[Union[str, Sequence[Union[Dict, Attachment]]]] = None, blocks: Optional[Union[str, Sequence[Union[Dict, Block]]]] = None, thread_ts: Optional[str] = None, parse: Optional[str] = None, reply_broadcast: Optional[bool] = None, unfurl_links: Optional[bool] = None, unfurl_media: Optional[bool] = None, link_names: Optional[bool] = None, metadata: Optional[Union[Dict, Metadata]] = None, markdown_text: Optional[str] = None, **kwargs, ) -> SlackResponse: """Schedules a message. https://docs.slack.dev/reference/methods/chat.scheduleMessage """ kwargs.update( { "channel": channel, "post_at": post_at, "text": text, "as_user": as_user, "attachments": attachments, "blocks": blocks, "thread_ts": thread_ts, "reply_broadcast": reply_broadcast, "parse": parse, "unfurl_links": unfurl_links, "unfurl_media": unfurl_media, "link_names": link_names, "metadata": metadata, "markdown_text": markdown_text, } ) _parse_web_class_objects(kwargs) kwargs = _remove_none_values(kwargs) _warn_if_message_text_content_is_missing("chat.scheduleMessage", kwargs) # NOTE: intentionally using json over params for the API methods using blocks/attachments return self.api_call("chat.scheduleMessage", json=kwargs) ``` Schedules a message. [https://docs.slack.dev/reference/methods/chat.scheduleMessage](https://docs.slack.dev/reference/methods/chat.scheduleMessage) `def chat_scheduledMessages_list(self, *, channel: str | None = None, cursor: str | None = None, latest: str | None = None, limit: int | None = None, oldest: str | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def chat_scheduledMessages_list( self, *, channel: Optional[str] = None, cursor: Optional[str] = None, latest: Optional[str] = None, limit: Optional[int] = None, oldest: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists all scheduled messages. https://docs.slack.dev/reference/methods/chat.scheduledMessages.list """ kwargs.update( { "channel": channel, "cursor": cursor, "latest": latest, "limit": limit, "oldest": oldest, "team_id": team_id, } ) return self.api_call("chat.scheduledMessages.list", params=kwargs) ``` Lists all scheduled messages. [https://docs.slack.dev/reference/methods/chat.scheduledMessages.list](https://docs.slack.dev/reference/methods/chat.scheduledMessages.list) `def chat_startStream(self, *, channel: str, thread_ts: str, markdown_text: str | None = None, recipient_team_id: str | None = None, recipient_user_id: str | None = None, chunks: Sequence[Dict | [Chunk](../models/messages/chunk.html#slack_sdk.models.messages.chunk.Chunk "slack_sdk.models.messages.chunk.Chunk")] | None = None, task_display_mode: str | None = None, icon_emoji: str | None = None, icon_url: str | None = None, username: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def chat_startStream( self, *, channel: str, thread_ts: str, markdown_text: Optional[str] = None, recipient_team_id: Optional[str] = None, recipient_user_id: Optional[str] = None, chunks: Optional[Sequence[Union[Dict, Chunk]]] = None, task_display_mode: Optional[str] = None, # timeline, plan icon_emoji: Optional[str] = None, icon_url: Optional[str] = None, username: Optional[str] = None, **kwargs, ) -> SlackResponse: """Starts a new streaming conversation. https://docs.slack.dev/reference/methods/chat.startStream """ kwargs.update( { "channel": channel, "thread_ts": thread_ts, "markdown_text": markdown_text, "recipient_team_id": recipient_team_id, "recipient_user_id": recipient_user_id, "chunks": chunks, "task_display_mode": task_display_mode, "icon_emoji": icon_emoji, "icon_url": icon_url, "username": username, } ) _parse_web_class_objects(kwargs) kwargs = _remove_none_values(kwargs) return self.api_call("chat.startStream", json=kwargs) ``` Starts a new streaming conversation. [https://docs.slack.dev/reference/methods/chat.startStream](https://docs.slack.dev/reference/methods/chat.startStream) `def chat_stopStream(self, *, channel: str, ts: str, markdown_text: str | None = None, blocks: str | Sequence[Dict | [Block](../models/blocks/blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")] | None = None, metadata: Dict | [Metadata](../models/metadata/index.html#slack_sdk.models.metadata.Metadata "slack_sdk.models.metadata.Metadata") | None = None, chunks: Sequence[Dict | [Chunk](../models/messages/chunk.html#slack_sdk.models.messages.chunk.Chunk "slack_sdk.models.messages.chunk.Chunk")] | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def chat_stopStream( self, *, channel: str, ts: str, markdown_text: Optional[str] = None, blocks: Optional[Union[str, Sequence[Union[Dict, Block]]]] = None, metadata: Optional[Union[Dict, Metadata]] = None, chunks: Optional[Sequence[Union[Dict, Chunk]]] = None, **kwargs, ) -> SlackResponse: """Stops a streaming conversation. https://docs.slack.dev/reference/methods/chat.stopStream """ kwargs.update( { "channel": channel, "ts": ts, "markdown_text": markdown_text, "blocks": blocks, "metadata": metadata, "chunks": chunks, } ) _parse_web_class_objects(kwargs) kwargs = _remove_none_values(kwargs) return self.api_call("chat.stopStream", json=kwargs) ``` Stops a streaming conversation. [https://docs.slack.dev/reference/methods/chat.stopStream](https://docs.slack.dev/reference/methods/chat.stopStream) `def chat_stream(self, *, buffer_size: int = 256, channel: str, thread_ts: str, recipient_team_id: str | None = None, recipient_user_id: str | None = None, task_display_mode: str | None = None, icon_emoji: str | None = None, icon_url: str | None = None, username: str | None = None, **kwargs) ‑> [ChatStream](chat_stream.html#slack_sdk.web.chat_stream.ChatStream "slack_sdk.web.chat_stream.ChatStream")` Expand source code ``` def chat_stream( self, *, buffer_size: int = 256, channel: str, thread_ts: str, recipient_team_id: Optional[str] = None, recipient_user_id: Optional[str] = None, task_display_mode: Optional[str] = None, icon_emoji: Optional[str] = None, icon_url: Optional[str] = None, username: Optional[str] = None, **kwargs, ) -> ChatStream: """Stream markdown text into a conversation. This method starts a new chat stream in a conversation that can be appended to. After appending an entire message, the stream can be stopped with concluding arguments such as "blocks" for gathering feedback. The following methods are used: - chat.startStream: Starts a new streaming conversation. [Reference](https://docs.slack.dev/reference/methods/chat.startStream). - chat.appendStream: Appends text to an existing streaming conversation. [Reference](https://docs.slack.dev/reference/methods/chat.appendStream). - chat.stopStream: Stops a streaming conversation. [Reference](https://docs.slack.dev/reference/methods/chat.stopStream). Args: buffer_size: The length of markdown_text to buffer in-memory before calling a stream method. Increasing this value decreases the number of method calls made for the same amount of text, which is useful to avoid rate limits. Default: 256. channel: An encoded ID that represents a channel, private group, or DM. thread_ts: Provide another message's ts value to reply to. Streamed messages should always be replies to a user request. recipient_team_id: The encoded ID of the team the user receiving the streaming text belongs to. Required when streaming to channels. recipient_user_id: The encoded ID of the user to receive the streaming text. Required when streaming to channels. task_display_mode: Specifies how tasks are displayed in the message. A "timeline" displays individual tasks with text and "plan" displays all tasks together. icon_emoji: Emoji to use as the icon for this message. Overrides icon_url. icon_url: Image URL to use as the icon for this message. username: The bot's username to display. **kwargs: Additional arguments passed to the underlying API calls. Returns: ChatStream instance for managing the stream Example: ```python streamer = client.chat_stream( channel="C0123456789", thread_ts="1700000001.123456", recipient_team_id="T0123456789", recipient_user_id="U0123456789", ) streamer.append(markdown_text="**hello wo") streamer.append(markdown_text="rld!**") streamer.stop() ``` """ return ChatStream( self, logger=self._logger, channel=channel, thread_ts=thread_ts, recipient_team_id=recipient_team_id, recipient_user_id=recipient_user_id, task_display_mode=task_display_mode, icon_emoji=icon_emoji, icon_url=icon_url, username=username, buffer_size=buffer_size, **kwargs, ) ``` Stream markdown text into a conversation. This method starts a new chat stream in a conversation that can be appended to. After appending an entire message, the stream can be stopped with concluding arguments such as "blocks" for gathering feedback. The following methods are used: * chat.startStream: Starts a new streaming conversation. [Reference](https://docs.slack.dev/reference/methods/chat.startStream). * chat.appendStream: Appends text to an existing streaming conversation. [Reference](https://docs.slack.dev/reference/methods/chat.appendStream). * chat.stopStream: Stops a streaming conversation. [Reference](https://docs.slack.dev/reference/methods/chat.stopStream). ## Args **`buffer_size`** The length of markdown\_text to buffer in-memory before calling a stream method. Increasing this value decreases the number of method calls made for the same amount of text, which is useful to avoid rate limits. Default: 256. **`channel`** An encoded ID that represents a channel, private group, or DM. **`thread_ts`** Provide another message's ts value to reply to. Streamed messages should always be replies to a user request. **`recipient_team_id`** The encoded ID of the team the user receiving the streaming text belongs to. Required when streaming to channels. **`recipient_user_id`** The encoded ID of the user to receive the streaming text. Required when streaming to channels. **`task_display_mode`** Specifies how tasks are displayed in the message. A "timeline" displays individual tasks with text and "plan" displays all tasks together. **`icon_emoji`** Emoji to use as the icon for this message. Overrides icon\_url. **`icon_url`** Image URL to use as the icon for this message. **`username`** The bot's username to display. **`**kwargs`** Additional arguments passed to the underlying API calls. ## Returns ChatStream instance for managing the stream ## Example ```python streamer = client.chat_stream( channel="C0123456789", thread_ts="1700000001.123456", recipient_team_id="T0123456789", recipient_user_id="U0123456789", ) streamer.append(markdown_text="**hello wo") streamer.append(markdown_text="rld!**") streamer.stop() ``` `def chat_unfurl(self, *, channel: str | None = None, ts: str | None = None, source: str | None = None, unfurl_id: str | None = None, unfurls: Dict[str, Dict] | None = None, metadata: Dict | [EventAndEntityMetadata](../models/metadata/index.html#slack_sdk.models.metadata.EventAndEntityMetadata "slack_sdk.models.metadata.EventAndEntityMetadata") | None = None, user_auth_blocks: str | Sequence[Dict | [Block](../models/blocks/blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")] | None = None, user_auth_message: str | None = None, user_auth_required: bool | None = None, user_auth_url: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def chat_unfurl( self, *, channel: Optional[str] = None, ts: Optional[str] = None, source: Optional[str] = None, unfurl_id: Optional[str] = None, unfurls: Optional[Dict[str, Dict]] = None, # or user_auth_* metadata: Optional[Union[Dict, EventAndEntityMetadata]] = None, user_auth_blocks: Optional[Union[str, Sequence[Union[Dict, Block]]]] = None, user_auth_message: Optional[str] = None, user_auth_required: Optional[bool] = None, user_auth_url: Optional[str] = None, **kwargs, ) -> SlackResponse: """Provide custom unfurl behavior for user-posted URLs. https://docs.slack.dev/reference/methods/chat.unfurl """ kwargs.update( { "channel": channel, "ts": ts, "source": source, "unfurl_id": unfurl_id, "unfurls": unfurls, "metadata": metadata, "user_auth_blocks": user_auth_blocks, "user_auth_message": user_auth_message, "user_auth_required": user_auth_required, "user_auth_url": user_auth_url, } ) _parse_web_class_objects(kwargs) # for user_auth_blocks kwargs = _remove_none_values(kwargs) # NOTE: intentionally using json over params for API methods using blocks/attachments return self.api_call("chat.unfurl", json=kwargs) ``` Provide custom unfurl behavior for user-posted URLs. [https://docs.slack.dev/reference/methods/chat.unfurl](https://docs.slack.dev/reference/methods/chat.unfurl) `def chat_update(self, *, channel: str, ts: str, text: str | None = None, attachments: str | Sequence[Dict | [Attachment](../models/attachments/index.html#slack_sdk.models.attachments.Attachment "slack_sdk.models.attachments.Attachment")] | None = None, blocks: str | Sequence[Dict | [Block](../models/blocks/blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")] | None = None, as_user: bool | None = None, file_ids: str | Sequence[str] | None = None, link_names: bool | None = None, parse: str | None = None, reply_broadcast: bool | None = None, metadata: Dict | [Metadata](../models/metadata/index.html#slack_sdk.models.metadata.Metadata "slack_sdk.models.metadata.Metadata") | None = None, markdown_text: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def chat_update( self, *, channel: str, ts: str, text: Optional[str] = None, attachments: Optional[Union[str, Sequence[Union[Dict, Attachment]]]] = None, blocks: Optional[Union[str, Sequence[Union[Dict, Block]]]] = None, as_user: Optional[bool] = None, file_ids: Optional[Union[str, Sequence[str]]] = None, link_names: Optional[bool] = None, parse: Optional[str] = None, # none, full reply_broadcast: Optional[bool] = None, metadata: Optional[Union[Dict, Metadata]] = None, markdown_text: Optional[str] = None, **kwargs, ) -> SlackResponse: """Updates a message in a channel. https://docs.slack.dev/reference/methods/chat.update """ kwargs.update( { "channel": channel, "ts": ts, "text": text, "attachments": attachments, "blocks": blocks, "as_user": as_user, "link_names": link_names, "parse": parse, "reply_broadcast": reply_broadcast, "metadata": metadata, "markdown_text": markdown_text, } ) if isinstance(file_ids, (list, tuple)): kwargs.update({"file_ids": ",".join(file_ids)}) else: kwargs.update({"file_ids": file_ids}) _parse_web_class_objects(kwargs) kwargs = _remove_none_values(kwargs) _warn_if_message_text_content_is_missing("chat.update", kwargs) # NOTE: intentionally using json over params for API methods using blocks/attachments return self.api_call("chat.update", json=kwargs) ``` Updates a message in a channel. [https://docs.slack.dev/reference/methods/chat.update](https://docs.slack.dev/reference/methods/chat.update) `def conversations_acceptSharedInvite(self, *, channel_name: str, channel_id: str | None = None, invite_id: str | None = None, free_trial_accepted: bool | None = None, is_private: bool | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_acceptSharedInvite( self, *, channel_name: str, channel_id: Optional[str] = None, invite_id: Optional[str] = None, free_trial_accepted: Optional[bool] = None, is_private: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Accepts an invitation to a Slack Connect channel. https://docs.slack.dev/reference/methods/conversations.acceptSharedInvite """ if channel_id is None and invite_id is None: raise e.SlackRequestError("Either channel_id or invite_id must be provided.") kwargs.update( { "channel_name": channel_name, "channel_id": channel_id, "invite_id": invite_id, "free_trial_accepted": free_trial_accepted, "is_private": is_private, "team_id": team_id, } ) return self.api_call("conversations.acceptSharedInvite", http_verb="POST", params=kwargs) ``` Accepts an invitation to a Slack Connect channel. [https://docs.slack.dev/reference/methods/conversations.acceptSharedInvite](https://docs.slack.dev/reference/methods/conversations.acceptSharedInvite) `def conversations_approveSharedInvite(self, *, invite_id: str, target_team: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_approveSharedInvite( self, *, invite_id: str, target_team: Optional[str] = None, **kwargs, ) -> SlackResponse: """Approves an invitation to a Slack Connect channel. https://docs.slack.dev/reference/methods/conversations.approveSharedInvite """ kwargs.update({"invite_id": invite_id, "target_team": target_team}) return self.api_call("conversations.approveSharedInvite", http_verb="POST", params=kwargs) ``` Approves an invitation to a Slack Connect channel. [https://docs.slack.dev/reference/methods/conversations.approveSharedInvite](https://docs.slack.dev/reference/methods/conversations.approveSharedInvite) `def conversations_archive(self, *, channel: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_archive( self, *, channel: str, **kwargs, ) -> SlackResponse: """Archives a conversation. https://docs.slack.dev/reference/methods/conversations.archive """ kwargs.update({"channel": channel}) return self.api_call("conversations.archive", params=kwargs) ``` Archives a conversation. [https://docs.slack.dev/reference/methods/conversations.archive](https://docs.slack.dev/reference/methods/conversations.archive) `def conversations_canvases_create(self, *, channel_id: str, document_content: Dict[str, str], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_canvases_create( self, *, channel_id: str, document_content: Dict[str, str], **kwargs, ) -> SlackResponse: """Create a Channel Canvas for a channel https://docs.slack.dev/reference/methods/conversations.canvases.create """ kwargs.update({"channel_id": channel_id, "document_content": document_content}) return self.api_call("conversations.canvases.create", json=kwargs) ``` Create a Channel Canvas for a channel [https://docs.slack.dev/reference/methods/conversations.canvases.create](https://docs.slack.dev/reference/methods/conversations.canvases.create) `def conversations_close(self, *, channel: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_close( self, *, channel: str, **kwargs, ) -> SlackResponse: """Closes a direct message or multi-person direct message. https://docs.slack.dev/reference/methods/conversations.close """ kwargs.update({"channel": channel}) return self.api_call("conversations.close", params=kwargs) ``` Closes a direct message or multi-person direct message. [https://docs.slack.dev/reference/methods/conversations.close](https://docs.slack.dev/reference/methods/conversations.close) `def conversations_create(self, *, name: str, is_private: bool | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_create( self, *, name: str, is_private: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Initiates a public or private channel-based conversation https://docs.slack.dev/reference/methods/conversations.create """ kwargs.update({"name": name, "is_private": is_private, "team_id": team_id}) return self.api_call("conversations.create", params=kwargs) ``` Initiates a public or private channel-based conversation [https://docs.slack.dev/reference/methods/conversations.create](https://docs.slack.dev/reference/methods/conversations.create) `def conversations_declineSharedInvite(self, *, invite_id: str, target_team: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_declineSharedInvite( self, *, invite_id: str, target_team: Optional[str] = None, **kwargs, ) -> SlackResponse: """Declines a Slack Connect channel invite. https://docs.slack.dev/reference/methods/conversations.declineSharedInvite """ kwargs.update({"invite_id": invite_id, "target_team": target_team}) return self.api_call("conversations.declineSharedInvite", http_verb="GET", params=kwargs) ``` Declines a Slack Connect channel invite. [https://docs.slack.dev/reference/methods/conversations.declineSharedInvite](https://docs.slack.dev/reference/methods/conversations.declineSharedInvite) `def conversations_externalInvitePermissions_set(self, *, action: str, channel: str, target_team: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_externalInvitePermissions_set( self, *, action: str, channel: str, target_team: str, **kwargs ) -> SlackResponse: """Sets a team in a shared External Limited channel to a shared Slack Connect channel or vice versa. https://docs.slack.dev/reference/methods/conversations.externalInvitePermissions.set """ kwargs.update( { "action": action, "channel": channel, "target_team": target_team, } ) return self.api_call("conversations.externalInvitePermissions.set", params=kwargs) ``` Sets a team in a shared External Limited channel to a shared Slack Connect channel or vice versa. [https://docs.slack.dev/reference/methods/conversations.externalInvitePermissions.set](https://docs.slack.dev/reference/methods/conversations.externalInvitePermissions.set) `def conversations_history(self, *, channel: str, cursor: str | None = None, inclusive: bool | None = None, include_all_metadata: bool | None = None, latest: str | None = None, limit: int | None = None, oldest: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_history( self, *, channel: str, cursor: Optional[str] = None, inclusive: Optional[bool] = None, include_all_metadata: Optional[bool] = None, latest: Optional[str] = None, limit: Optional[int] = None, oldest: Optional[str] = None, **kwargs, ) -> SlackResponse: """Fetches a conversation's history of messages and events. https://docs.slack.dev/reference/methods/conversations.history """ kwargs.update( { "channel": channel, "cursor": cursor, "inclusive": inclusive, "include_all_metadata": include_all_metadata, "limit": limit, "latest": latest, "oldest": oldest, } ) return self.api_call("conversations.history", http_verb="GET", params=kwargs) ``` Fetches a conversation's history of messages and events. [https://docs.slack.dev/reference/methods/conversations.history](https://docs.slack.dev/reference/methods/conversations.history) `def conversations_info(self, *, channel: str, include_locale: bool | None = None, include_num_members: bool | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_info( self, *, channel: str, include_locale: Optional[bool] = None, include_num_members: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Retrieve information about a conversation. https://docs.slack.dev/reference/methods/conversations.info """ kwargs.update( { "channel": channel, "include_locale": include_locale, "include_num_members": include_num_members, } ) return self.api_call("conversations.info", http_verb="GET", params=kwargs) ``` Retrieve information about a conversation. [https://docs.slack.dev/reference/methods/conversations.info](https://docs.slack.dev/reference/methods/conversations.info) `def conversations_invite(self, *, channel: str, users: str | Sequence[str], force: bool | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_invite( self, *, channel: str, users: Union[str, Sequence[str]], force: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Invites users to a channel. https://docs.slack.dev/reference/methods/conversations.invite """ kwargs.update( { "channel": channel, "force": force, } ) if isinstance(users, (list, tuple)): kwargs.update({"users": ",".join(users)}) else: kwargs.update({"users": users}) return self.api_call("conversations.invite", params=kwargs) ``` Invites users to a channel. [https://docs.slack.dev/reference/methods/conversations.invite](https://docs.slack.dev/reference/methods/conversations.invite) `def conversations_inviteShared(self, *, channel: str, emails: str | Sequence[str] | None = None, user_ids: str | Sequence[str] | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_inviteShared( self, *, channel: str, emails: Optional[Union[str, Sequence[str]]] = None, user_ids: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """Sends an invitation to a Slack Connect channel. https://docs.slack.dev/reference/methods/conversations.inviteShared """ if emails is None and user_ids is None: raise e.SlackRequestError("Either emails or user ids must be provided.") kwargs.update({"channel": channel}) if isinstance(emails, (list, tuple)): kwargs.update({"emails": ",".join(emails)}) else: kwargs.update({"emails": emails}) if isinstance(user_ids, (list, tuple)): kwargs.update({"user_ids": ",".join(user_ids)}) else: kwargs.update({"user_ids": user_ids}) return self.api_call("conversations.inviteShared", http_verb="GET", params=kwargs) ``` Sends an invitation to a Slack Connect channel. [https://docs.slack.dev/reference/methods/conversations.inviteShared](https://docs.slack.dev/reference/methods/conversations.inviteShared) `def conversations_join(self, *, channel: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_join( self, *, channel: str, **kwargs, ) -> SlackResponse: """Joins an existing conversation. https://docs.slack.dev/reference/methods/conversations.join """ kwargs.update({"channel": channel}) return self.api_call("conversations.join", params=kwargs) ``` Joins an existing conversation. [https://docs.slack.dev/reference/methods/conversations.join](https://docs.slack.dev/reference/methods/conversations.join) `def conversations_kick(self, *, channel: str, user: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_kick( self, *, channel: str, user: str, **kwargs, ) -> SlackResponse: """Removes a user from a conversation. https://docs.slack.dev/reference/methods/conversations.kick """ kwargs.update({"channel": channel, "user": user}) return self.api_call("conversations.kick", params=kwargs) ``` Removes a user from a conversation. [https://docs.slack.dev/reference/methods/conversations.kick](https://docs.slack.dev/reference/methods/conversations.kick) `def conversations_leave(self, *, channel: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_leave( self, *, channel: str, **kwargs, ) -> SlackResponse: """Leaves a conversation. https://docs.slack.dev/reference/methods/conversations.leave """ kwargs.update({"channel": channel}) return self.api_call("conversations.leave", params=kwargs) ``` Leaves a conversation. [https://docs.slack.dev/reference/methods/conversations.leave](https://docs.slack.dev/reference/methods/conversations.leave) `def conversations_list(self, *, cursor: str | None = None, exclude_archived: bool | None = None, limit: int | None = None, team_id: str | None = None, types: str | Sequence[str] | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_list( self, *, cursor: Optional[str] = None, exclude_archived: Optional[bool] = None, limit: Optional[int] = None, team_id: Optional[str] = None, types: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """Lists all channels in a Slack team. https://docs.slack.dev/reference/methods/conversations.list """ kwargs.update( { "cursor": cursor, "exclude_archived": exclude_archived, "limit": limit, "team_id": team_id, } ) if isinstance(types, (list, tuple)): kwargs.update({"types": ",".join(types)}) else: kwargs.update({"types": types}) return self.api_call("conversations.list", http_verb="GET", params=kwargs) ``` Lists all channels in a Slack team. [https://docs.slack.dev/reference/methods/conversations.list](https://docs.slack.dev/reference/methods/conversations.list) `def conversations_listConnectInvites(self, *, count: int | None = None, cursor: str | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_listConnectInvites( self, *, count: Optional[int] = None, cursor: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List shared channel invites that have been generated or received but have not yet been approved by all parties. https://docs.slack.dev/reference/methods/conversations.listConnectInvites """ kwargs.update({"count": count, "cursor": cursor, "team_id": team_id}) return self.api_call("conversations.listConnectInvites", params=kwargs) ``` List shared channel invites that have been generated or received but have not yet been approved by all parties. [https://docs.slack.dev/reference/methods/conversations.listConnectInvites](https://docs.slack.dev/reference/methods/conversations.listConnectInvites) `def conversations_mark(self, *, channel: str, ts: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_mark( self, *, channel: str, ts: str, **kwargs, ) -> SlackResponse: """Sets the read cursor in a channel. https://docs.slack.dev/reference/methods/conversations.mark """ kwargs.update({"channel": channel, "ts": ts}) return self.api_call("conversations.mark", params=kwargs) ``` Sets the read cursor in a channel. [https://docs.slack.dev/reference/methods/conversations.mark](https://docs.slack.dev/reference/methods/conversations.mark) `def conversations_members(self, *, channel: str, cursor: str | None = None, limit: int | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_members( self, *, channel: str, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Retrieve members of a conversation. https://docs.slack.dev/reference/methods/conversations.members """ kwargs.update({"channel": channel, "cursor": cursor, "limit": limit}) return self.api_call("conversations.members", http_verb="GET", params=kwargs) ``` Retrieve members of a conversation. [https://docs.slack.dev/reference/methods/conversations.members](https://docs.slack.dev/reference/methods/conversations.members) `def conversations_open(self, *, channel: str | None = None, return_im: bool | None = None, users: str | Sequence[str] | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_open( self, *, channel: Optional[str] = None, return_im: Optional[bool] = None, users: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """Opens or resumes a direct message or multi-person direct message. https://docs.slack.dev/reference/methods/conversations.open """ if channel is None and users is None: raise e.SlackRequestError("Either channel or users must be provided.") kwargs.update({"channel": channel, "return_im": return_im}) if isinstance(users, (list, tuple)): kwargs.update({"users": ",".join(users)}) else: kwargs.update({"users": users}) return self.api_call("conversations.open", params=kwargs) ``` Opens or resumes a direct message or multi-person direct message. [https://docs.slack.dev/reference/methods/conversations.open](https://docs.slack.dev/reference/methods/conversations.open) `def conversations_rename(self, *, channel: str, name: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_rename( self, *, channel: str, name: str, **kwargs, ) -> SlackResponse: """Renames a conversation. https://docs.slack.dev/reference/methods/conversations.rename """ kwargs.update({"channel": channel, "name": name}) return self.api_call("conversations.rename", params=kwargs) ``` Renames a conversation. [https://docs.slack.dev/reference/methods/conversations.rename](https://docs.slack.dev/reference/methods/conversations.rename) `def conversations_replies(self, *, channel: str, ts: str, cursor: str | None = None, inclusive: bool | None = None, include_all_metadata: bool | None = None, latest: str | None = None, limit: int | None = None, oldest: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_replies( self, *, channel: str, ts: str, cursor: Optional[str] = None, inclusive: Optional[bool] = None, include_all_metadata: Optional[bool] = None, latest: Optional[str] = None, limit: Optional[int] = None, oldest: Optional[str] = None, **kwargs, ) -> SlackResponse: """Retrieve a thread of messages posted to a conversation https://docs.slack.dev/reference/methods/conversations.replies """ kwargs.update( { "channel": channel, "ts": ts, "cursor": cursor, "inclusive": inclusive, "include_all_metadata": include_all_metadata, "limit": limit, "latest": latest, "oldest": oldest, } ) return self.api_call("conversations.replies", http_verb="GET", params=kwargs) ``` Retrieve a thread of messages posted to a conversation [https://docs.slack.dev/reference/methods/conversations.replies](https://docs.slack.dev/reference/methods/conversations.replies) `def conversations_requestSharedInvite_approve(self, *, invite_id: str, channel_id: str | None = None, is_external_limited: str | None = None, message: Dict[str, Any] | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_requestSharedInvite_approve( self, *, invite_id: str, channel_id: Optional[str] = None, is_external_limited: Optional[str] = None, message: Optional[Dict[str, Any]] = None, **kwargs, ) -> SlackResponse: """Approve a request to add an external user to a channel. This also sends them a Slack Connect invite. https://docs.slack.dev/reference/methods/conversations.requestSharedInvite.approve """ kwargs.update( { "invite_id": invite_id, "channel_id": channel_id, "is_external_limited": is_external_limited, } ) if message is not None: kwargs.update({"message": json.dumps(message)}) return self.api_call("conversations.requestSharedInvite.approve", params=kwargs) ``` Approve a request to add an external user to a channel. This also sends them a Slack Connect invite. [https://docs.slack.dev/reference/methods/conversations.requestSharedInvite.approve](https://docs.slack.dev/reference/methods/conversations.requestSharedInvite.approve) `def conversations_requestSharedInvite_deny(self, *, invite_id: str, message: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_requestSharedInvite_deny( self, *, invite_id: str, message: Optional[str] = None, **kwargs, ) -> SlackResponse: """Deny a request to invite an external user to a channel. https://docs.slack.dev/reference/methods/conversations.requestSharedInvite.deny """ kwargs.update({"invite_id": invite_id, "message": message}) return self.api_call("conversations.requestSharedInvite.deny", params=kwargs) ``` Deny a request to invite an external user to a channel. [https://docs.slack.dev/reference/methods/conversations.requestSharedInvite.deny](https://docs.slack.dev/reference/methods/conversations.requestSharedInvite.deny) `def conversations_requestSharedInvite_list(self, *, cursor: str | None = None, include_approved: bool | None = None, include_denied: bool | None = None, include_expired: bool | None = None, invite_ids: str | Sequence[str] | None = None, limit: int | None = None, user_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_requestSharedInvite_list( self, *, cursor: Optional[str] = None, include_approved: Optional[bool] = None, include_denied: Optional[bool] = None, include_expired: Optional[bool] = None, invite_ids: Optional[Union[str, Sequence[str]]] = None, limit: Optional[int] = None, user_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists requests to add external users to channels with ability to filter. https://docs.slack.dev/reference/methods/conversations.requestSharedInvite.list """ kwargs.update( { "cursor": cursor, "include_approved": include_approved, "include_denied": include_denied, "include_expired": include_expired, "limit": limit, "user_id": user_id, } ) if invite_ids is not None: if isinstance(invite_ids, (list, tuple)): kwargs.update({"invite_ids": ",".join(invite_ids)}) else: kwargs.update({"invite_ids": invite_ids}) return self.api_call("conversations.requestSharedInvite.list", params=kwargs) ``` Lists requests to add external users to channels with ability to filter. [https://docs.slack.dev/reference/methods/conversations.requestSharedInvite.list](https://docs.slack.dev/reference/methods/conversations.requestSharedInvite.list) `def conversations_setPurpose(self, *, channel: str, purpose: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_setPurpose( self, *, channel: str, purpose: str, **kwargs, ) -> SlackResponse: """Sets the purpose for a conversation. https://docs.slack.dev/reference/methods/conversations.setPurpose """ kwargs.update({"channel": channel, "purpose": purpose}) return self.api_call("conversations.setPurpose", params=kwargs) ``` Sets the purpose for a conversation. [https://docs.slack.dev/reference/methods/conversations.setPurpose](https://docs.slack.dev/reference/methods/conversations.setPurpose) `def conversations_setTopic(self, *, channel: str, topic: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_setTopic( self, *, channel: str, topic: str, **kwargs, ) -> SlackResponse: """Sets the topic for a conversation. https://docs.slack.dev/reference/methods/conversations.setTopic """ kwargs.update({"channel": channel, "topic": topic}) return self.api_call("conversations.setTopic", params=kwargs) ``` Sets the topic for a conversation. [https://docs.slack.dev/reference/methods/conversations.setTopic](https://docs.slack.dev/reference/methods/conversations.setTopic) `def conversations_unarchive(self, *, channel: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def conversations_unarchive( self, *, channel: str, **kwargs, ) -> SlackResponse: """Reverses conversation archival. https://docs.slack.dev/reference/methods/conversations.unarchive """ kwargs.update({"channel": channel}) return self.api_call("conversations.unarchive", params=kwargs) ``` Reverses conversation archival. [https://docs.slack.dev/reference/methods/conversations.unarchive](https://docs.slack.dev/reference/methods/conversations.unarchive) `def dialog_open(self, *, dialog: Dict[str, Any], trigger_id: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def dialog_open( self, *, dialog: Dict[str, Any], trigger_id: str, **kwargs, ) -> SlackResponse: """Open a dialog with a user. https://docs.slack.dev/reference/methods/dialog.open """ kwargs.update({"dialog": dialog, "trigger_id": trigger_id}) kwargs = _remove_none_values(kwargs) # NOTE: As the dialog can be a dict, this API call works only with json format. return self.api_call("dialog.open", json=kwargs) ``` Open a dialog with a user. [https://docs.slack.dev/reference/methods/dialog.open](https://docs.slack.dev/reference/methods/dialog.open) `def dnd_endDnd(self, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def dnd_endDnd( self, **kwargs, ) -> SlackResponse: """Ends the current user's Do Not Disturb session immediately. https://docs.slack.dev/reference/methods/dnd.endDnd """ return self.api_call("dnd.endDnd", params=kwargs) ``` Ends the current user's Do Not Disturb session immediately. [https://docs.slack.dev/reference/methods/dnd.endDnd](https://docs.slack.dev/reference/methods/dnd.endDnd) `def dnd_endSnooze(self, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def dnd_endSnooze( self, **kwargs, ) -> SlackResponse: """Ends the current user's snooze mode immediately. https://docs.slack.dev/reference/methods/dnd.endSnooze """ return self.api_call("dnd.endSnooze", params=kwargs) ``` Ends the current user's snooze mode immediately. [https://docs.slack.dev/reference/methods/dnd.endSnooze](https://docs.slack.dev/reference/methods/dnd.endSnooze) `def dnd_info(self, *, team_id: str | None = None, user: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def dnd_info( self, *, team_id: Optional[str] = None, user: Optional[str] = None, **kwargs, ) -> SlackResponse: """Retrieves a user's current Do Not Disturb status. https://docs.slack.dev/reference/methods/dnd.info """ kwargs.update({"team_id": team_id, "user": user}) return self.api_call("dnd.info", http_verb="GET", params=kwargs) ``` Retrieves a user's current Do Not Disturb status. [https://docs.slack.dev/reference/methods/dnd.info](https://docs.slack.dev/reference/methods/dnd.info) `def dnd_setSnooze(self, *, num_minutes: str | int, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def dnd_setSnooze( self, *, num_minutes: Union[int, str], **kwargs, ) -> SlackResponse: """Turns on Do Not Disturb mode for the current user, or changes its duration. https://docs.slack.dev/reference/methods/dnd.setSnooze """ kwargs.update({"num_minutes": num_minutes}) return self.api_call("dnd.setSnooze", http_verb="GET", params=kwargs) ``` Turns on Do Not Disturb mode for the current user, or changes its duration. [https://docs.slack.dev/reference/methods/dnd.setSnooze](https://docs.slack.dev/reference/methods/dnd.setSnooze) `def dnd_teamInfo(self, users: str | Sequence[str], team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def dnd_teamInfo( self, users: Union[str, Sequence[str]], team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Retrieves the Do Not Disturb status for users on a team. https://docs.slack.dev/reference/methods/dnd.teamInfo """ if isinstance(users, (list, tuple)): kwargs.update({"users": ",".join(users)}) else: kwargs.update({"users": users}) kwargs.update({"team_id": team_id}) return self.api_call("dnd.teamInfo", http_verb="GET", params=kwargs) ``` Retrieves the Do Not Disturb status for users on a team. [https://docs.slack.dev/reference/methods/dnd.teamInfo](https://docs.slack.dev/reference/methods/dnd.teamInfo) `def emoji_list(self, include_categories: bool | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def emoji_list( self, include_categories: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Lists custom emoji for a team. https://docs.slack.dev/reference/methods/emoji.list """ kwargs.update({"include_categories": include_categories}) return self.api_call("emoji.list", http_verb="GET", params=kwargs) ``` Lists custom emoji for a team. [https://docs.slack.dev/reference/methods/emoji.list](https://docs.slack.dev/reference/methods/emoji.list) `def entity_presentDetails(self, trigger_id: str, metadata: Dict | [EntityMetadata](../models/metadata/index.html#slack_sdk.models.metadata.EntityMetadata "slack_sdk.models.metadata.EntityMetadata") | None = None, user_auth_required: bool | None = None, user_auth_url: str | None = None, error: Dict[str, Any] | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def entity_presentDetails( self, trigger_id: str, metadata: Optional[Union[Dict, EntityMetadata]] = None, user_auth_required: Optional[bool] = None, user_auth_url: Optional[str] = None, error: Optional[Dict[str, Any]] = None, **kwargs, ) -> SlackResponse: """Provides entity details for the flexpane. https://docs.slack.dev/reference/methods/entity.presentDetails/ """ kwargs.update({"trigger_id": trigger_id}) if metadata is not None: kwargs.update({"metadata": metadata}) if user_auth_required is not None: kwargs.update({"user_auth_required": user_auth_required}) if user_auth_url is not None: kwargs.update({"user_auth_url": user_auth_url}) if error is not None: kwargs.update({"error": error}) _parse_web_class_objects(kwargs) return self.api_call("entity.presentDetails", json=kwargs) ``` Provides entity details for the flexpane. [https://docs.slack.dev/reference/methods/entity.presentDetails/](https://docs.slack.dev/reference/methods/entity.presentDetails/) `def files_comments_delete(self, *, file: str, id: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def files_comments_delete( self, *, file: str, id: str, **kwargs, ) -> SlackResponse: """Deletes an existing comment on a file. https://docs.slack.dev/reference/methods/files.comments.delete """ kwargs.update({"file": file, "id": id}) return self.api_call("files.comments.delete", params=kwargs) ``` Deletes an existing comment on a file. [https://docs.slack.dev/reference/methods/files.comments.delete](https://docs.slack.dev/reference/methods/files.comments.delete) `def files_completeUploadExternal(self, *, files: List[Dict[str, str | None]], channel_id: str | None = None, channels: List[str] | None = None, initial_comment: str | None = None, thread_ts: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def files_completeUploadExternal( self, *, files: List[Dict[str, Optional[str]]], channel_id: Optional[str] = None, channels: Optional[List[str]] = None, initial_comment: Optional[str] = None, thread_ts: Optional[str] = None, **kwargs, ) -> SlackResponse: """Finishes an upload started with files.getUploadURLExternal. https://docs.slack.dev/reference/methods/files.completeUploadExternal """ _files = [{k: v for k, v in f.items() if v is not None} for f in files] kwargs.update( { "files": json.dumps(_files), "channel_id": channel_id, "initial_comment": initial_comment, "thread_ts": thread_ts, } ) if channels: kwargs["channels"] = ",".join(channels) return self.api_call("files.completeUploadExternal", params=kwargs) ``` Finishes an upload started with files.getUploadURLExternal. [https://docs.slack.dev/reference/methods/files.completeUploadExternal](https://docs.slack.dev/reference/methods/files.completeUploadExternal) `def files_delete(self, *, file: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def files_delete( self, *, file: str, **kwargs, ) -> SlackResponse: """Deletes a file. https://docs.slack.dev/reference/methods/files.delete """ kwargs.update({"file": file}) return self.api_call("files.delete", params=kwargs) ``` Deletes a file. [https://docs.slack.dev/reference/methods/files.delete](https://docs.slack.dev/reference/methods/files.delete) `def files_getUploadURLExternal(self, *, filename: str, length: int, alt_txt: str | None = None, snippet_type: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def files_getUploadURLExternal( self, *, filename: str, length: int, alt_txt: Optional[str] = None, snippet_type: Optional[str] = None, **kwargs, ) -> SlackResponse: """Gets a URL for an edge external upload. https://docs.slack.dev/reference/methods/files.getUploadURLExternal """ kwargs.update( { "filename": filename, "length": length, "alt_txt": alt_txt, "snippet_type": snippet_type, } ) return self.api_call("files.getUploadURLExternal", params=kwargs) ``` Gets a URL for an edge external upload. [https://docs.slack.dev/reference/methods/files.getUploadURLExternal](https://docs.slack.dev/reference/methods/files.getUploadURLExternal) `def files_info(self, *, file: str, count: int | None = None, cursor: str | None = None, limit: int | None = None, page: int | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def files_info( self, *, file: str, count: Optional[int] = None, cursor: Optional[str] = None, limit: Optional[int] = None, page: Optional[int] = None, **kwargs, ) -> SlackResponse: """Gets information about a team file. https://docs.slack.dev/reference/methods/files.info """ kwargs.update( { "file": file, "count": count, "cursor": cursor, "limit": limit, "page": page, } ) return self.api_call("files.info", http_verb="GET", params=kwargs) ``` Gets information about a team file. [https://docs.slack.dev/reference/methods/files.info](https://docs.slack.dev/reference/methods/files.info) `def files_list(self, *, channel: str | None = None, count: int | None = None, page: int | None = None, show_files_hidden_by_limit: bool | None = None, team_id: str | None = None, ts_from: str | None = None, ts_to: str | None = None, types: str | Sequence[str] | None = None, user: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def files_list( self, *, channel: Optional[str] = None, count: Optional[int] = None, page: Optional[int] = None, show_files_hidden_by_limit: Optional[bool] = None, team_id: Optional[str] = None, ts_from: Optional[str] = None, ts_to: Optional[str] = None, types: Optional[Union[str, Sequence[str]]] = None, user: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists & filters team files. https://docs.slack.dev/reference/methods/files.list """ kwargs.update( { "channel": channel, "count": count, "page": page, "show_files_hidden_by_limit": show_files_hidden_by_limit, "team_id": team_id, "ts_from": ts_from, "ts_to": ts_to, "user": user, } ) if isinstance(types, (list, tuple)): kwargs.update({"types": ",".join(types)}) else: kwargs.update({"types": types}) return self.api_call("files.list", http_verb="GET", params=kwargs) ``` Lists & filters team files. [https://docs.slack.dev/reference/methods/files.list](https://docs.slack.dev/reference/methods/files.list) `def files_remote_add(self, *, external_id: str, external_url: str, title: str, filetype: str | None = None, indexable_file_contents: str | bytes | io.IOBase | None = None, preview_image: str | bytes | io.IOBase | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def files_remote_add( self, *, external_id: str, external_url: str, title: str, filetype: Optional[str] = None, indexable_file_contents: Optional[Union[str, bytes, IOBase]] = None, preview_image: Optional[Union[str, bytes, IOBase]] = None, **kwargs, ) -> SlackResponse: """Adds a file from a remote service. https://docs.slack.dev/reference/methods/files.remote.add """ kwargs.update( { "external_id": external_id, "external_url": external_url, "title": title, "filetype": filetype, } ) files = None # preview_image (file): Preview of the document via multipart/form-data. if preview_image is not None or indexable_file_contents is not None: files = { "preview_image": preview_image, "indexable_file_contents": indexable_file_contents, } return self.api_call( # Intentionally using "POST" method over "GET" here "files.remote.add", http_verb="POST", data=kwargs, files=files, ) ``` Adds a file from a remote service. [https://docs.slack.dev/reference/methods/files.remote.add](https://docs.slack.dev/reference/methods/files.remote.add) `def files_remote_info(self, *, external_id: str | None = None, file: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def files_remote_info( self, *, external_id: Optional[str] = None, file: Optional[str] = None, **kwargs, ) -> SlackResponse: """Retrieve information about a remote file added to Slack. https://docs.slack.dev/reference/methods/files.remote.info """ kwargs.update({"external_id": external_id, "file": file}) return self.api_call("files.remote.info", http_verb="GET", params=kwargs) ``` Retrieve information about a remote file added to Slack. [https://docs.slack.dev/reference/methods/files.remote.info](https://docs.slack.dev/reference/methods/files.remote.info) `def files_remote_list(self, *, channel: str | None = None, cursor: str | None = None, limit: int | None = None, ts_from: str | None = None, ts_to: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def files_remote_list( self, *, channel: Optional[str] = None, cursor: Optional[str] = None, limit: Optional[int] = None, ts_from: Optional[str] = None, ts_to: Optional[str] = None, **kwargs, ) -> SlackResponse: """Retrieve information about a remote file added to Slack. https://docs.slack.dev/reference/methods/files.remote.list """ kwargs.update( { "channel": channel, "cursor": cursor, "limit": limit, "ts_from": ts_from, "ts_to": ts_to, } ) return self.api_call("files.remote.list", http_verb="GET", params=kwargs) ``` Retrieve information about a remote file added to Slack. [https://docs.slack.dev/reference/methods/files.remote.list](https://docs.slack.dev/reference/methods/files.remote.list) `def files_remote_remove(self, *, external_id: str | None = None, file: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def files_remote_remove( self, *, external_id: Optional[str] = None, file: Optional[str] = None, **kwargs, ) -> SlackResponse: """Remove a remote file. https://docs.slack.dev/reference/methods/files.remote.remove """ kwargs.update({"external_id": external_id, "file": file}) return self.api_call("files.remote.remove", http_verb="POST", params=kwargs) ``` Remove a remote file. [https://docs.slack.dev/reference/methods/files.remote.remove](https://docs.slack.dev/reference/methods/files.remote.remove) `def files_remote_share(self, *, channels: str | Sequence[str], external_id: str | None = None, file: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def files_remote_share( self, *, channels: Union[str, Sequence[str]], external_id: Optional[str] = None, file: Optional[str] = None, **kwargs, ) -> SlackResponse: """Share a remote file into a channel. https://docs.slack.dev/reference/methods/files.remote.share """ if external_id is None and file is None: raise e.SlackRequestError("Either external_id or file must be provided.") if isinstance(channels, (list, tuple)): kwargs.update({"channels": ",".join(channels)}) else: kwargs.update({"channels": channels}) kwargs.update({"external_id": external_id, "file": file}) return self.api_call("files.remote.share", http_verb="GET", params=kwargs) ``` Share a remote file into a channel. [https://docs.slack.dev/reference/methods/files.remote.share](https://docs.slack.dev/reference/methods/files.remote.share) `def files_remote_update(self, *, external_id: str | None = None, external_url: str | None = None, file: str | None = None, title: str | None = None, filetype: str | None = None, indexable_file_contents: str | None = None, preview_image: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def files_remote_update( self, *, external_id: Optional[str] = None, external_url: Optional[str] = None, file: Optional[str] = None, title: Optional[str] = None, filetype: Optional[str] = None, indexable_file_contents: Optional[str] = None, preview_image: Optional[str] = None, **kwargs, ) -> SlackResponse: """Updates an existing remote file. https://docs.slack.dev/reference/methods/files.remote.update """ kwargs.update( { "external_id": external_id, "external_url": external_url, "file": file, "title": title, "filetype": filetype, } ) files = None # preview_image (file): Preview of the document via multipart/form-data. if preview_image is not None or indexable_file_contents is not None: files = { "preview_image": preview_image, "indexable_file_contents": indexable_file_contents, } return self.api_call( # Intentionally using "POST" method over "GET" here "files.remote.update", http_verb="POST", data=kwargs, files=files, ) ``` Updates an existing remote file. [https://docs.slack.dev/reference/methods/files.remote.update](https://docs.slack.dev/reference/methods/files.remote.update) `def files_revokePublicURL(self, *, file: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def files_revokePublicURL( self, *, file: str, **kwargs, ) -> SlackResponse: """Revokes public/external sharing access for a file https://docs.slack.dev/reference/methods/files.revokePublicURL """ kwargs.update({"file": file}) return self.api_call("files.revokePublicURL", params=kwargs) ``` Revokes public/external sharing access for a file [https://docs.slack.dev/reference/methods/files.revokePublicURL](https://docs.slack.dev/reference/methods/files.revokePublicURL) `def files_sharedPublicURL(self, *, file: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def files_sharedPublicURL( self, *, file: str, **kwargs, ) -> SlackResponse: """Enables a file for public/external sharing. https://docs.slack.dev/reference/methods/files.sharedPublicURL """ kwargs.update({"file": file}) return self.api_call("files.sharedPublicURL", params=kwargs) ``` Enables a file for public/external sharing. [https://docs.slack.dev/reference/methods/files.sharedPublicURL](https://docs.slack.dev/reference/methods/files.sharedPublicURL) `def files_upload(self, *, file: str | bytes | io.IOBase | None = None, content: str | bytes | None = None, filename: str | None = None, filetype: str | None = None, initial_comment: str | None = None, thread_ts: str | None = None, title: str | None = None, channels: str | Sequence[str] | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def files_upload( self, *, file: Optional[Union[str, bytes, IOBase]] = None, content: Optional[Union[str, bytes]] = None, filename: Optional[str] = None, filetype: Optional[str] = None, initial_comment: Optional[str] = None, thread_ts: Optional[str] = None, title: Optional[str] = None, channels: Optional[Union[str, Sequence[str]]] = None, **kwargs, ) -> SlackResponse: """Uploads or creates a file. https://docs.slack.dev/reference/methods/files.upload """ _print_files_upload_v2_suggestion() if file is None and content is None: raise e.SlackRequestError("The file or content argument must be specified.") if file is not None and content is not None: raise e.SlackRequestError("You cannot specify both the file and the content argument.") if isinstance(channels, (list, tuple)): kwargs.update({"channels": ",".join(channels)}) else: kwargs.update({"channels": channels}) kwargs.update( { "filename": filename, "filetype": filetype, "initial_comment": initial_comment, "thread_ts": thread_ts, "title": title, } ) if file: if kwargs.get("filename") is None and isinstance(file, str): # use the local filename if filename is missing if kwargs.get("filename") is None: kwargs["filename"] = file.split(os.path.sep)[-1] return self.api_call("files.upload", files={"file": file}, data=kwargs) else: kwargs["content"] = content return self.api_call("files.upload", data=kwargs) ``` Uploads or creates a file. [https://docs.slack.dev/reference/methods/files.upload](https://docs.slack.dev/reference/methods/files.upload) `def files_upload_v2(self, *, filename: str | None = None, file: str | bytes | io.IOBase | os.PathLike | None = None, content: str | bytes | None = None, title: str | None = None, alt_txt: str | None = None, highlight_type: str | None = None, snippet_type: str | None = None, file_uploads: List[Dict[str, Any]] | None = None, channel: str | None = None, channels: List[str] | None = None, initial_comment: str | None = None, thread_ts: str | None = None, request_file_info: bool = True, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def files_upload_v2( self, *, # for sending a single file filename: Optional[str] = None, # you can skip this only when sending along with content parameter file: Optional[Union[str, bytes, IOBase, os.PathLike]] = None, content: Optional[Union[str, bytes]] = None, title: Optional[str] = None, alt_txt: Optional[str] = None, highlight_type: Optional[str] = None, snippet_type: Optional[str] = None, # To upload multiple files at a time file_uploads: Optional[List[Dict[str, Any]]] = None, channel: Optional[str] = None, channels: Optional[List[str]] = None, initial_comment: Optional[str] = None, thread_ts: Optional[str] = None, request_file_info: bool = True, # since v3.23, this flag is no longer necessary **kwargs, ) -> SlackResponse: """This wrapper method provides an easy way to upload files using the following endpoints: - step1: https://docs.slack.dev/reference/methods/files.getUploadURLExternal - step2: "https://files.slack.com/upload/v1/..." URLs returned from files.getUploadURLExternal API - step3: https://docs.slack.dev/reference/methods/files.completeUploadExternal and https://docs.slack.dev/reference/methods/files.info """ if file is None and content is None and file_uploads is None: raise e.SlackRequestError("Any of file, content, and file_uploads must be specified.") if file is not None and content is not None: raise e.SlackRequestError("You cannot specify both the file and the content argument.") # deprecated arguments: filetype = kwargs.get("filetype") if filetype is not None: warnings.warn("The filetype parameter is no longer supported. Please remove it from the arguments.") # step1: files.getUploadURLExternal per file files: List[Dict[str, Any]] = [] if file_uploads is not None: for f in file_uploads: files.append(_to_v2_file_upload_item(f)) else: f = _to_v2_file_upload_item( { "filename": filename, "file": file, "content": content, "title": title, "alt_txt": alt_txt, "highlight_type": highlight_type, "snippet_type": snippet_type, } ) files.append(f) for f in files: url_response = self.files_getUploadURLExternal( filename=f.get("filename"), # type: ignore[arg-type] length=f.get("length"), # type: ignore[arg-type] alt_txt=f.get("alt_txt"), snippet_type=f.get("snippet_type"), token=kwargs.get("token"), ) _validate_for_legacy_client(url_response) f["file_id"] = url_response.get("file_id") # type: ignore[union-attr, unused-ignore] f["upload_url"] = url_response.get("upload_url") # type: ignore[union-attr, unused-ignore] # step2: "https://files.slack.com/upload/v1/..." per file for f in files: upload_result = self._upload_file( url=f["upload_url"], data=f["data"], logger=self._logger, timeout=self.timeout, proxy=self.proxy, ssl=self.ssl, ) if upload_result.status != 200: status = upload_result.status body = upload_result.body message = ( "Failed to upload a file " f"(status: {status}, body: {body}, filename: {f.get('filename')}, title: {f.get('title')})" ) raise e.SlackRequestError(message) # step3: files.completeUploadExternal with all the sets of (file_id + title) completion = self.files_completeUploadExternal( files=[{"id": f["file_id"], "title": f["title"], "highlight_type": f.get("highlight_type")} for f in files], channel_id=channel, channels=channels, initial_comment=initial_comment, thread_ts=thread_ts, **kwargs, ) if len(completion.get("files")) == 1: # type: ignore[arg-type, union-attr, unused-ignore] completion.data["file"] = completion.get("files")[0] # type: ignore[index, union-attr, unused-ignore] return completion ``` This wrapper method provides an easy way to upload files using the following endpoints: * step1: [https://docs.slack.dev/reference/methods/files.getUploadURLExternal](https://docs.slack.dev/reference/methods/files.getUploadURLExternal) * step2: "https://files.slack.com/upload/v1/…" URLs returned from files.getUploadURLExternal API * step3: [https://docs.slack.dev/reference/methods/files.completeUploadExternal](https://docs.slack.dev/reference/methods/files.completeUploadExternal) and [https://docs.slack.dev/reference/methods/files.info](https://docs.slack.dev/reference/methods/files.info) `def functions_completeError(self, *, function_execution_id: str, error: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def functions_completeError( self, *, function_execution_id: str, error: str, **kwargs, ) -> SlackResponse: """Signal the failure to execute a function https://docs.slack.dev/reference/methods/functions.completeError """ kwargs.update({"function_execution_id": function_execution_id, "error": error}) return self.api_call("functions.completeError", params=kwargs) ``` Signal the failure to execute a function [https://docs.slack.dev/reference/methods/functions.completeError](https://docs.slack.dev/reference/methods/functions.completeError) `def functions_completeSuccess(self, *, function_execution_id: str, outputs: Dict[str, Any], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def functions_completeSuccess( self, *, function_execution_id: str, outputs: Dict[str, Any], **kwargs, ) -> SlackResponse: """Signal the successful completion of a function https://docs.slack.dev/reference/methods/functions.completeSuccess """ kwargs.update({"function_execution_id": function_execution_id, "outputs": json.dumps(outputs)}) return self.api_call("functions.completeSuccess", params=kwargs) ``` Signal the successful completion of a function [https://docs.slack.dev/reference/methods/functions.completeSuccess](https://docs.slack.dev/reference/methods/functions.completeSuccess) `def groups_archive(self, *, channel: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def groups_archive( self, *, channel: str, **kwargs, ) -> SlackResponse: """Archives a private channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.archive", json=kwargs) ``` Archives a private channel. `def groups_create(self, *, name: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def groups_create( self, *, name: str, **kwargs, ) -> SlackResponse: """Creates a private channel.""" kwargs.update({"name": name}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.create", json=kwargs) ``` Creates a private channel. `def groups_createChild(self, *, channel: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def groups_createChild( self, *, channel: str, **kwargs, ) -> SlackResponse: """Clones and archives a private channel.""" kwargs.update({"channel": channel}) return self.api_call("groups.createChild", http_verb="GET", params=kwargs) ``` Clones and archives a private channel. `def groups_history(self, *, channel: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def groups_history( self, *, channel: str, **kwargs, ) -> SlackResponse: """Fetches history of messages and events from a private channel.""" kwargs.update({"channel": channel}) return self.api_call("groups.history", http_verb="GET", params=kwargs) ``` Fetches history of messages and events from a private channel. `def groups_info(self, *, channel: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def groups_info( self, *, channel: str, **kwargs, ) -> SlackResponse: """Gets information about a private channel.""" kwargs.update({"channel": channel}) return self.api_call("groups.info", http_verb="GET", params=kwargs) ``` Gets information about a private channel. `def groups_invite(self, *, channel: str, user: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def groups_invite( self, *, channel: str, user: str, **kwargs, ) -> SlackResponse: """Invites a user to a private channel.""" kwargs.update({"channel": channel, "user": user}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.invite", json=kwargs) ``` Invites a user to a private channel. `def groups_kick(self, *, channel: str, user: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def groups_kick( self, *, channel: str, user: str, **kwargs, ) -> SlackResponse: """Removes a user from a private channel.""" kwargs.update({"channel": channel, "user": user}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.kick", json=kwargs) ``` Removes a user from a private channel. `def groups_leave(self, *, channel: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def groups_leave( self, *, channel: str, **kwargs, ) -> SlackResponse: """Leaves a private channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.leave", json=kwargs) ``` Leaves a private channel. `def groups_list(self, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def groups_list( self, **kwargs, ) -> SlackResponse: """Lists private channels that the calling user has access to.""" return self.api_call("groups.list", http_verb="GET", params=kwargs) ``` Lists private channels that the calling user has access to. `def groups_mark(self, *, channel: str, ts: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def groups_mark( self, *, channel: str, ts: str, **kwargs, ) -> SlackResponse: """Sets the read cursor in a private channel.""" kwargs.update({"channel": channel, "ts": ts}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.mark", json=kwargs) ``` Sets the read cursor in a private channel. `def groups_open(self, *, channel: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def groups_open( self, *, channel: str, **kwargs, ) -> SlackResponse: """Opens a private channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.open", json=kwargs) ``` Opens a private channel. `def groups_rename(self, *, channel: str, name: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def groups_rename( self, *, channel: str, name: str, **kwargs, ) -> SlackResponse: """Renames a private channel.""" kwargs.update({"channel": channel, "name": name}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.rename", json=kwargs) ``` Renames a private channel. `def groups_replies(self, *, channel: str, thread_ts: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def groups_replies( self, *, channel: str, thread_ts: str, **kwargs, ) -> SlackResponse: """Retrieve a thread of messages posted to a private channel""" kwargs.update({"channel": channel, "thread_ts": thread_ts}) return self.api_call("groups.replies", http_verb="GET", params=kwargs) ``` Retrieve a thread of messages posted to a private channel `def groups_setPurpose(self, *, channel: str, purpose: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def groups_setPurpose( self, *, channel: str, purpose: str, **kwargs, ) -> SlackResponse: """Sets the purpose for a private channel.""" kwargs.update({"channel": channel, "purpose": purpose}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.setPurpose", json=kwargs) ``` Sets the purpose for a private channel. `def groups_setTopic(self, *, channel: str, topic: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def groups_setTopic( self, *, channel: str, topic: str, **kwargs, ) -> SlackResponse: """Sets the topic for a private channel.""" kwargs.update({"channel": channel, "topic": topic}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.setTopic", json=kwargs) ``` Sets the topic for a private channel. `def groups_unarchive(self, *, channel: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def groups_unarchive( self, *, channel: str, **kwargs, ) -> SlackResponse: """Unarchives a private channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("groups.unarchive", json=kwargs) ``` Unarchives a private channel. `def im_close(self, *, channel: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def im_close( self, *, channel: str, **kwargs, ) -> SlackResponse: """Close a direct message channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("im.close", json=kwargs) ``` Close a direct message channel. `def im_history(self, *, channel: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def im_history( self, *, channel: str, **kwargs, ) -> SlackResponse: """Fetches history of messages and events from direct message channel.""" kwargs.update({"channel": channel}) return self.api_call("im.history", http_verb="GET", params=kwargs) ``` Fetches history of messages and events from direct message channel. `def im_list(self, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def im_list( self, **kwargs, ) -> SlackResponse: """Lists direct message channels for the calling user.""" return self.api_call("im.list", http_verb="GET", params=kwargs) ``` Lists direct message channels for the calling user. `def im_mark(self, *, channel: str, ts: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def im_mark( self, *, channel: str, ts: str, **kwargs, ) -> SlackResponse: """Sets the read cursor in a direct message channel.""" kwargs.update({"channel": channel, "ts": ts}) kwargs = _remove_none_values(kwargs) return self.api_call("im.mark", json=kwargs) ``` Sets the read cursor in a direct message channel. `def im_open(self, *, user: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def im_open( self, *, user: str, **kwargs, ) -> SlackResponse: """Opens a direct message channel.""" kwargs.update({"user": user}) kwargs = _remove_none_values(kwargs) return self.api_call("im.open", json=kwargs) ``` Opens a direct message channel. `def im_replies(self, *, channel: str, thread_ts: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def im_replies( self, *, channel: str, thread_ts: str, **kwargs, ) -> SlackResponse: """Retrieve a thread of messages posted to a direct message conversation""" kwargs.update({"channel": channel, "thread_ts": thread_ts}) return self.api_call("im.replies", http_verb="GET", params=kwargs) ``` Retrieve a thread of messages posted to a direct message conversation `def migration_exchange(self, *, users: str | Sequence[str], team_id: str | None = None, to_old: bool | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def migration_exchange( self, *, users: Union[str, Sequence[str]], team_id: Optional[str] = None, to_old: Optional[bool] = None, **kwargs, ) -> SlackResponse: """For Enterprise Grid workspaces, map local user IDs to global user IDs https://docs.slack.dev/reference/methods/migration.exchange """ if isinstance(users, (list, tuple)): kwargs.update({"users": ",".join(users)}) else: kwargs.update({"users": users}) kwargs.update({"team_id": team_id, "to_old": to_old}) return self.api_call("migration.exchange", http_verb="GET", params=kwargs) ``` For Enterprise Grid workspaces, map local user IDs to global user IDs [https://docs.slack.dev/reference/methods/migration.exchange](https://docs.slack.dev/reference/methods/migration.exchange) `def mpim_close(self, *, channel: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def mpim_close( self, *, channel: str, **kwargs, ) -> SlackResponse: """Closes a multiparty direct message channel.""" kwargs.update({"channel": channel}) kwargs = _remove_none_values(kwargs) return self.api_call("mpim.close", json=kwargs) ``` Closes a multiparty direct message channel. `def mpim_history(self, *, channel: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def mpim_history( self, *, channel: str, **kwargs, ) -> SlackResponse: """Fetches history of messages and events from a multiparty direct message.""" kwargs.update({"channel": channel}) return self.api_call("mpim.history", http_verb="GET", params=kwargs) ``` Fetches history of messages and events from a multiparty direct message. `def mpim_list(self, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def mpim_list( self, **kwargs, ) -> SlackResponse: """Lists multiparty direct message channels for the calling user.""" return self.api_call("mpim.list", http_verb="GET", params=kwargs) ``` Lists multiparty direct message channels for the calling user. `def mpim_mark(self, *, channel: str, ts: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def mpim_mark( self, *, channel: str, ts: str, **kwargs, ) -> SlackResponse: """Sets the read cursor in a multiparty direct message channel.""" kwargs.update({"channel": channel, "ts": ts}) kwargs = _remove_none_values(kwargs) return self.api_call("mpim.mark", json=kwargs) ``` Sets the read cursor in a multiparty direct message channel. `def mpim_open(self, *, users: str | Sequence[str], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def mpim_open( self, *, users: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """This method opens a multiparty direct message.""" if isinstance(users, (list, tuple)): kwargs.update({"users": ",".join(users)}) else: kwargs.update({"users": users}) return self.api_call("mpim.open", params=kwargs) ``` This method opens a multiparty direct message. `def mpim_replies(self, *, channel: str, thread_ts: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def mpim_replies( self, *, channel: str, thread_ts: str, **kwargs, ) -> SlackResponse: """Retrieve a thread of messages posted to a direct message conversation from a multiparty direct message. """ kwargs.update({"channel": channel, "thread_ts": thread_ts}) return self.api_call("mpim.replies", http_verb="GET", params=kwargs) ``` Retrieve a thread of messages posted to a direct message conversation from a multiparty direct message. `def oauth_access(self, *, client_id: str, client_secret: str, code: str, redirect_uri: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def oauth_access( self, *, client_id: str, client_secret: str, code: str, redirect_uri: Optional[str] = None, **kwargs, ) -> SlackResponse: """Exchanges a temporary OAuth verifier code for an access token. https://docs.slack.dev/reference/methods/oauth.access """ if redirect_uri is not None: kwargs.update({"redirect_uri": redirect_uri}) kwargs.update({"code": code}) return self.api_call( "oauth.access", data=kwargs, auth={"client_id": client_id, "client_secret": client_secret}, ) ``` Exchanges a temporary OAuth verifier code for an access token. [https://docs.slack.dev/reference/methods/oauth.access](https://docs.slack.dev/reference/methods/oauth.access) `def oauth_v2_access(self, *, client_id: str, client_secret: str, code: str | None = None, redirect_uri: str | None = None, grant_type: str | None = None, refresh_token: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def oauth_v2_access( self, *, client_id: str, client_secret: str, # This field is required when processing the OAuth redirect URL requests # while it's absent for token rotation code: Optional[str] = None, redirect_uri: Optional[str] = None, # This field is required for token rotation grant_type: Optional[str] = None, # This field is required for token rotation refresh_token: Optional[str] = None, **kwargs, ) -> SlackResponse: """Exchanges a temporary OAuth verifier code for an access token. https://docs.slack.dev/reference/methods/oauth.v2.access """ if redirect_uri is not None: kwargs.update({"redirect_uri": redirect_uri}) if code is not None: kwargs.update({"code": code}) if grant_type is not None: kwargs.update({"grant_type": grant_type}) if refresh_token is not None: kwargs.update({"refresh_token": refresh_token}) return self.api_call( "oauth.v2.access", data=kwargs, auth={"client_id": client_id, "client_secret": client_secret}, ) ``` Exchanges a temporary OAuth verifier code for an access token. [https://docs.slack.dev/reference/methods/oauth.v2.access](https://docs.slack.dev/reference/methods/oauth.v2.access) `def oauth_v2_exchange(self, *, token: str, client_id: str, client_secret: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def oauth_v2_exchange( self, *, token: str, client_id: str, client_secret: str, **kwargs, ) -> SlackResponse: """Exchanges a legacy access token for a new expiring access token and refresh token https://docs.slack.dev/reference/methods/oauth.v2.exchange """ kwargs.update({"client_id": client_id, "client_secret": client_secret, "token": token}) return self.api_call("oauth.v2.exchange", params=kwargs) ``` Exchanges a legacy access token for a new expiring access token and refresh token [https://docs.slack.dev/reference/methods/oauth.v2.exchange](https://docs.slack.dev/reference/methods/oauth.v2.exchange) `def openid_connect_token(self, client_id: str, client_secret: str, code: str | None = None, redirect_uri: str | None = None, grant_type: str | None = None, refresh_token: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def openid_connect_token( self, client_id: str, client_secret: str, code: Optional[str] = None, redirect_uri: Optional[str] = None, grant_type: Optional[str] = None, refresh_token: Optional[str] = None, **kwargs, ) -> SlackResponse: """Exchanges a temporary OAuth verifier code for an access token for Sign in with Slack. https://docs.slack.dev/reference/methods/openid.connect.token """ if redirect_uri is not None: kwargs.update({"redirect_uri": redirect_uri}) if code is not None: kwargs.update({"code": code}) if grant_type is not None: kwargs.update({"grant_type": grant_type}) if refresh_token is not None: kwargs.update({"refresh_token": refresh_token}) return self.api_call( "openid.connect.token", data=kwargs, auth={"client_id": client_id, "client_secret": client_secret}, ) ``` Exchanges a temporary OAuth verifier code for an access token for Sign in with Slack. [https://docs.slack.dev/reference/methods/openid.connect.token](https://docs.slack.dev/reference/methods/openid.connect.token) `def openid_connect_userInfo(self, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def openid_connect_userInfo( self, **kwargs, ) -> SlackResponse: """Get the identity of a user who has authorized Sign in with Slack. https://docs.slack.dev/reference/methods/openid.connect.userInfo """ return self.api_call("openid.connect.userInfo", params=kwargs) ``` Get the identity of a user who has authorized Sign in with Slack. [https://docs.slack.dev/reference/methods/openid.connect.userInfo](https://docs.slack.dev/reference/methods/openid.connect.userInfo) `def pins_add(self, *, channel: str, timestamp: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def pins_add( self, *, channel: str, timestamp: Optional[str] = None, **kwargs, ) -> SlackResponse: """Pins an item to a channel. https://docs.slack.dev/reference/methods/pins.add """ kwargs.update({"channel": channel, "timestamp": timestamp}) return self.api_call("pins.add", params=kwargs) ``` Pins an item to a channel. [https://docs.slack.dev/reference/methods/pins.add](https://docs.slack.dev/reference/methods/pins.add) `def pins_list(self, *, channel: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def pins_list( self, *, channel: str, **kwargs, ) -> SlackResponse: """Lists items pinned to a channel. https://docs.slack.dev/reference/methods/pins.list """ kwargs.update({"channel": channel}) return self.api_call("pins.list", http_verb="GET", params=kwargs) ``` Lists items pinned to a channel. [https://docs.slack.dev/reference/methods/pins.list](https://docs.slack.dev/reference/methods/pins.list) `def pins_remove(self, *, channel: str, timestamp: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def pins_remove( self, *, channel: str, timestamp: Optional[str] = None, **kwargs, ) -> SlackResponse: """Un-pins an item from a channel. https://docs.slack.dev/reference/methods/pins.remove """ kwargs.update({"channel": channel, "timestamp": timestamp}) return self.api_call("pins.remove", params=kwargs) ``` Un-pins an item from a channel. [https://docs.slack.dev/reference/methods/pins.remove](https://docs.slack.dev/reference/methods/pins.remove) `def reactions_add(self, *, channel: str, name: str, timestamp: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def reactions_add( self, *, channel: str, name: str, timestamp: str, **kwargs, ) -> SlackResponse: """Adds a reaction to an item. https://docs.slack.dev/reference/methods/reactions.add """ kwargs.update({"channel": channel, "name": name, "timestamp": timestamp}) return self.api_call("reactions.add", params=kwargs) ``` Adds a reaction to an item. [https://docs.slack.dev/reference/methods/reactions.add](https://docs.slack.dev/reference/methods/reactions.add) `def reactions_get(self, *, channel: str | None = None, file: str | None = None, file_comment: str | None = None, full: bool | None = None, timestamp: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def reactions_get( self, *, channel: Optional[str] = None, file: Optional[str] = None, file_comment: Optional[str] = None, full: Optional[bool] = None, timestamp: Optional[str] = None, **kwargs, ) -> SlackResponse: """Gets reactions for an item. https://docs.slack.dev/reference/methods/reactions.get """ kwargs.update( { "channel": channel, "file": file, "file_comment": file_comment, "full": full, "timestamp": timestamp, } ) return self.api_call("reactions.get", http_verb="GET", params=kwargs) ``` Gets reactions for an item. [https://docs.slack.dev/reference/methods/reactions.get](https://docs.slack.dev/reference/methods/reactions.get) `def reactions_list(self, *, count: int | None = None, cursor: str | None = None, full: bool | None = None, limit: int | None = None, page: int | None = None, team_id: str | None = None, user: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def reactions_list( self, *, count: Optional[int] = None, cursor: Optional[str] = None, full: Optional[bool] = None, limit: Optional[int] = None, page: Optional[int] = None, team_id: Optional[str] = None, user: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists reactions made by a user. https://docs.slack.dev/reference/methods/reactions.list """ kwargs.update( { "count": count, "cursor": cursor, "full": full, "limit": limit, "page": page, "team_id": team_id, "user": user, } ) return self.api_call("reactions.list", http_verb="GET", params=kwargs) ``` Lists reactions made by a user. [https://docs.slack.dev/reference/methods/reactions.list](https://docs.slack.dev/reference/methods/reactions.list) `def reactions_remove(self, *, name: str, channel: str | None = None, file: str | None = None, file_comment: str | None = None, timestamp: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def reactions_remove( self, *, name: str, channel: Optional[str] = None, file: Optional[str] = None, file_comment: Optional[str] = None, timestamp: Optional[str] = None, **kwargs, ) -> SlackResponse: """Removes a reaction from an item. https://docs.slack.dev/reference/methods/reactions.remove """ kwargs.update( { "name": name, "channel": channel, "file": file, "file_comment": file_comment, "timestamp": timestamp, } ) return self.api_call("reactions.remove", params=kwargs) ``` Removes a reaction from an item. [https://docs.slack.dev/reference/methods/reactions.remove](https://docs.slack.dev/reference/methods/reactions.remove) `def reminders_add(self, *, text: str, time: str, team_id: str | None = None, user: str | None = None, recurrence: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def reminders_add( self, *, text: str, time: str, team_id: Optional[str] = None, user: Optional[str] = None, recurrence: Optional[str] = None, **kwargs, ) -> SlackResponse: """Creates a reminder. https://docs.slack.dev/reference/methods/reminders.add """ kwargs.update( { "text": text, "time": time, "team_id": team_id, "user": user, "recurrence": recurrence, } ) return self.api_call("reminders.add", params=kwargs) ``` Creates a reminder. [https://docs.slack.dev/reference/methods/reminders.add](https://docs.slack.dev/reference/methods/reminders.add) `def reminders_complete(self, *, reminder: str, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def reminders_complete( self, *, reminder: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Marks a reminder as complete. https://docs.slack.dev/reference/methods/reminders.complete """ kwargs.update({"reminder": reminder, "team_id": team_id}) return self.api_call("reminders.complete", params=kwargs) ``` Marks a reminder as complete. [https://docs.slack.dev/reference/methods/reminders.complete](https://docs.slack.dev/reference/methods/reminders.complete) `def reminders_delete(self, *, reminder: str, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def reminders_delete( self, *, reminder: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Deletes a reminder. https://docs.slack.dev/reference/methods/reminders.delete """ kwargs.update({"reminder": reminder, "team_id": team_id}) return self.api_call("reminders.delete", params=kwargs) ``` Deletes a reminder. [https://docs.slack.dev/reference/methods/reminders.delete](https://docs.slack.dev/reference/methods/reminders.delete) `def reminders_info(self, *, reminder: str, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def reminders_info( self, *, reminder: str, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Gets information about a reminder. https://docs.slack.dev/reference/methods/reminders.info """ kwargs.update({"reminder": reminder, "team_id": team_id}) return self.api_call("reminders.info", http_verb="GET", params=kwargs) ``` Gets information about a reminder. [https://docs.slack.dev/reference/methods/reminders.info](https://docs.slack.dev/reference/methods/reminders.info) `def reminders_list(self, *, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def reminders_list( self, *, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists all reminders created by or for a given user. https://docs.slack.dev/reference/methods/reminders.list """ kwargs.update({"team_id": team_id}) return self.api_call("reminders.list", http_verb="GET", params=kwargs) ``` Lists all reminders created by or for a given user. [https://docs.slack.dev/reference/methods/reminders.list](https://docs.slack.dev/reference/methods/reminders.list) `def rtm_connect(self, *, batch_presence_aware: bool | None = None, presence_sub: bool | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def rtm_connect( self, *, batch_presence_aware: Optional[bool] = None, presence_sub: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Starts a Real Time Messaging session. https://docs.slack.dev/reference/methods/rtm.connect """ kwargs.update({"batch_presence_aware": batch_presence_aware, "presence_sub": presence_sub}) return self.api_call("rtm.connect", http_verb="GET", params=kwargs) ``` Starts a Real Time Messaging session. [https://docs.slack.dev/reference/methods/rtm.connect](https://docs.slack.dev/reference/methods/rtm.connect) `def rtm_start(self, *, batch_presence_aware: bool | None = None, include_locale: bool | None = None, mpim_aware: bool | None = None, no_latest: bool | None = None, no_unreads: bool | None = None, presence_sub: bool | None = None, simple_latest: bool | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def rtm_start( self, *, batch_presence_aware: Optional[bool] = None, include_locale: Optional[bool] = None, mpim_aware: Optional[bool] = None, no_latest: Optional[bool] = None, no_unreads: Optional[bool] = None, presence_sub: Optional[bool] = None, simple_latest: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Starts a Real Time Messaging session. https://docs.slack.dev/reference/methods/rtm.start """ kwargs.update( { "batch_presence_aware": batch_presence_aware, "include_locale": include_locale, "mpim_aware": mpim_aware, "no_latest": no_latest, "no_unreads": no_unreads, "presence_sub": presence_sub, "simple_latest": simple_latest, } ) return self.api_call("rtm.start", http_verb="GET", params=kwargs) ``` Starts a Real Time Messaging session. [https://docs.slack.dev/reference/methods/rtm.start](https://docs.slack.dev/reference/methods/rtm.start) `def search_all(self, *, query: str, count: int | None = None, highlight: bool | None = None, page: int | None = None, sort: str | None = None, sort_dir: str | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def search_all( self, *, query: str, count: Optional[int] = None, highlight: Optional[bool] = None, page: Optional[int] = None, sort: Optional[str] = None, sort_dir: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Searches for messages and files matching a query. https://docs.slack.dev/reference/methods/search.all """ kwargs.update( { "query": query, "count": count, "highlight": highlight, "page": page, "sort": sort, "sort_dir": sort_dir, "team_id": team_id, } ) return self.api_call("search.all", http_verb="GET", params=kwargs) ``` Searches for messages and files matching a query. [https://docs.slack.dev/reference/methods/search.all](https://docs.slack.dev/reference/methods/search.all) `def search_files(self, *, query: str, count: int | None = None, highlight: bool | None = None, page: int | None = None, sort: str | None = None, sort_dir: str | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def search_files( self, *, query: str, count: Optional[int] = None, highlight: Optional[bool] = None, page: Optional[int] = None, sort: Optional[str] = None, sort_dir: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Searches for files matching a query. https://docs.slack.dev/reference/methods/search.files """ kwargs.update( { "query": query, "count": count, "highlight": highlight, "page": page, "sort": sort, "sort_dir": sort_dir, "team_id": team_id, } ) return self.api_call("search.files", http_verb="GET", params=kwargs) ``` Searches for files matching a query. [https://docs.slack.dev/reference/methods/search.files](https://docs.slack.dev/reference/methods/search.files) `def search_messages(self, *, query: str, count: int | None = None, cursor: str | None = None, highlight: bool | None = None, page: int | None = None, sort: str | None = None, sort_dir: str | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def search_messages( self, *, query: str, count: Optional[int] = None, cursor: Optional[str] = None, highlight: Optional[bool] = None, page: Optional[int] = None, sort: Optional[str] = None, sort_dir: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Searches for messages matching a query. https://docs.slack.dev/reference/methods/search.messages """ kwargs.update( { "query": query, "count": count, "cursor": cursor, "highlight": highlight, "page": page, "sort": sort, "sort_dir": sort_dir, "team_id": team_id, } ) return self.api_call("search.messages", http_verb="GET", params=kwargs) ``` Searches for messages matching a query. [https://docs.slack.dev/reference/methods/search.messages](https://docs.slack.dev/reference/methods/search.messages) `def slackLists_access_delete(self, *, list_id: str, channel_ids: List[str] | None = None, user_ids: List[str] | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def slackLists_access_delete( self, *, list_id: str, channel_ids: Optional[List[str]] = None, user_ids: Optional[List[str]] = None, **kwargs, ) -> SlackResponse: """Revoke access to a List for specified entities. https://docs.slack.dev/reference/methods/slackLists.access.delete """ kwargs.update({"list_id": list_id, "channel_ids": channel_ids, "user_ids": user_ids}) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.access.delete", json=kwargs) ``` Revoke access to a List for specified entities. [https://docs.slack.dev/reference/methods/slackLists.access.delete](https://docs.slack.dev/reference/methods/slackLists.access.delete) `def slackLists_access_set(self, *, list_id: str, access_level: str, channel_ids: List[str] | None = None, user_ids: List[str] | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def slackLists_access_set( self, *, list_id: str, access_level: str, channel_ids: Optional[List[str]] = None, user_ids: Optional[List[str]] = None, **kwargs, ) -> SlackResponse: """Set the access level to a List for specified entities. https://docs.slack.dev/reference/methods/slackLists.access.set """ kwargs.update({"list_id": list_id, "access_level": access_level, "channel_ids": channel_ids, "user_ids": user_ids}) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.access.set", json=kwargs) ``` Set the access level to a List for specified entities. [https://docs.slack.dev/reference/methods/slackLists.access.set](https://docs.slack.dev/reference/methods/slackLists.access.set) `def slackLists_create(self, *, name: str, description_blocks: str | Sequence[Dict | [RichTextBlock](../models/blocks/blocks.html#slack_sdk.models.blocks.blocks.RichTextBlock "slack_sdk.models.blocks.blocks.RichTextBlock")] | None = None, schema: List[Dict[str, Any]] | None = None, copy_from_list_id: str | None = None, include_copied_list_records: bool | None = None, todo_mode: bool | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def slackLists_create( self, *, name: str, description_blocks: Optional[Union[str, Sequence[Union[Dict, RichTextBlock]]]] = None, schema: Optional[List[Dict[str, Any]]] = None, copy_from_list_id: Optional[str] = None, include_copied_list_records: Optional[bool] = None, todo_mode: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Creates a List. https://docs.slack.dev/reference/methods/slackLists.create """ kwargs.update( { "name": name, "description_blocks": description_blocks, "schema": schema, "copy_from_list_id": copy_from_list_id, "include_copied_list_records": include_copied_list_records, "todo_mode": todo_mode, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.create", json=kwargs) ``` Creates a List. [https://docs.slack.dev/reference/methods/slackLists.create](https://docs.slack.dev/reference/methods/slackLists.create) `def slackLists_download_get(self, *, list_id: str, job_id: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def slackLists_download_get( self, *, list_id: str, job_id: str, **kwargs, ) -> SlackResponse: """Retrieve List download URL from an export job to download List contents. https://docs.slack.dev/reference/methods/slackLists.download.get """ kwargs.update( { "list_id": list_id, "job_id": job_id, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.download.get", json=kwargs) ``` Retrieve List download URL from an export job to download List contents. [https://docs.slack.dev/reference/methods/slackLists.download.get](https://docs.slack.dev/reference/methods/slackLists.download.get) `def slackLists_download_start(self, *, list_id: str, include_archived: bool | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def slackLists_download_start( self, *, list_id: str, include_archived: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Initiate a job to export List contents. https://docs.slack.dev/reference/methods/slackLists.download.start """ kwargs.update( { "list_id": list_id, "include_archived": include_archived, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.download.start", json=kwargs) ``` Initiate a job to export List contents. [https://docs.slack.dev/reference/methods/slackLists.download.start](https://docs.slack.dev/reference/methods/slackLists.download.start) `def slackLists_items_create(self, *, list_id: str, duplicated_item_id: str | None = None, parent_item_id: str | None = None, initial_fields: List[Dict[str, Any]] | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def slackLists_items_create( self, *, list_id: str, duplicated_item_id: Optional[str] = None, parent_item_id: Optional[str] = None, initial_fields: Optional[List[Dict[str, Any]]] = None, **kwargs, ) -> SlackResponse: """Add a new item to an existing List. https://docs.slack.dev/reference/methods/slackLists.items.create """ kwargs.update( { "list_id": list_id, "duplicated_item_id": duplicated_item_id, "parent_item_id": parent_item_id, "initial_fields": initial_fields, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.items.create", json=kwargs) ``` Add a new item to an existing List. [https://docs.slack.dev/reference/methods/slackLists.items.create](https://docs.slack.dev/reference/methods/slackLists.items.create) `def slackLists_items_delete(self, *, list_id: str, id: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def slackLists_items_delete( self, *, list_id: str, id: str, **kwargs, ) -> SlackResponse: """Deletes an item from an existing List. https://docs.slack.dev/reference/methods/slackLists.items.delete """ kwargs.update( { "list_id": list_id, "id": id, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.items.delete", json=kwargs) ``` Deletes an item from an existing List. [https://docs.slack.dev/reference/methods/slackLists.items.delete](https://docs.slack.dev/reference/methods/slackLists.items.delete) `def slackLists_items_deleteMultiple(self, *, list_id: str, ids: List[str], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def slackLists_items_deleteMultiple( self, *, list_id: str, ids: List[str], **kwargs, ) -> SlackResponse: """Deletes multiple items from an existing List. https://docs.slack.dev/reference/methods/slackLists.items.deleteMultiple """ kwargs.update( { "list_id": list_id, "ids": ids, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.items.deleteMultiple", json=kwargs) ``` Deletes multiple items from an existing List. [https://docs.slack.dev/reference/methods/slackLists.items.deleteMultiple](https://docs.slack.dev/reference/methods/slackLists.items.deleteMultiple) `def slackLists_items_info(self, *, list_id: str, id: str, include_is_subscribed: bool | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def slackLists_items_info( self, *, list_id: str, id: str, include_is_subscribed: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Get a row from a List. https://docs.slack.dev/reference/methods/slackLists.items.info """ kwargs.update( { "list_id": list_id, "id": id, "include_is_subscribed": include_is_subscribed, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.items.info", json=kwargs) ``` Get a row from a List. [https://docs.slack.dev/reference/methods/slackLists.items.info](https://docs.slack.dev/reference/methods/slackLists.items.info) `def slackLists_items_list(self, *, list_id: str, limit: int | None = None, cursor: str | None = None, archived: bool | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def slackLists_items_list( self, *, list_id: str, limit: Optional[int] = None, cursor: Optional[str] = None, archived: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Get records from a List. https://docs.slack.dev/reference/methods/slackLists.items.list """ kwargs.update( { "list_id": list_id, "limit": limit, "cursor": cursor, "archived": archived, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.items.list", json=kwargs) ``` Get records from a List. [https://docs.slack.dev/reference/methods/slackLists.items.list](https://docs.slack.dev/reference/methods/slackLists.items.list) `def slackLists_items_update(self, *, list_id: str, cells: List[Dict[str, Any]], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def slackLists_items_update( self, *, list_id: str, cells: List[Dict[str, Any]], **kwargs, ) -> SlackResponse: """Updates cells in a List. https://docs.slack.dev/reference/methods/slackLists.items.update """ kwargs.update( { "list_id": list_id, "cells": cells, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.items.update", json=kwargs) ``` Updates cells in a List. [https://docs.slack.dev/reference/methods/slackLists.items.update](https://docs.slack.dev/reference/methods/slackLists.items.update) `def slackLists_update(self, *, id: str, name: str | None = None, description_blocks: str | Sequence[Dict | [RichTextBlock](../models/blocks/blocks.html#slack_sdk.models.blocks.blocks.RichTextBlock "slack_sdk.models.blocks.blocks.RichTextBlock")] | None = None, todo_mode: bool | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def slackLists_update( self, *, id: str, name: Optional[str] = None, description_blocks: Optional[Union[str, Sequence[Union[Dict, RichTextBlock]]]] = None, todo_mode: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Update a List. https://docs.slack.dev/reference/methods/slackLists.update """ kwargs.update( { "id": id, "name": name, "description_blocks": description_blocks, "todo_mode": todo_mode, } ) kwargs = _remove_none_values(kwargs) return self.api_call("slackLists.update", json=kwargs) ``` Update a List. [https://docs.slack.dev/reference/methods/slackLists.update](https://docs.slack.dev/reference/methods/slackLists.update) `def stars_add(self, *, channel: str | None = None, file: str | None = None, file_comment: str | None = None, timestamp: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def stars_add( self, *, channel: Optional[str] = None, file: Optional[str] = None, file_comment: Optional[str] = None, timestamp: Optional[str] = None, **kwargs, ) -> SlackResponse: """Adds a star to an item. https://docs.slack.dev/reference/methods/stars.add """ kwargs.update( { "channel": channel, "file": file, "file_comment": file_comment, "timestamp": timestamp, } ) return self.api_call("stars.add", params=kwargs) ``` Adds a star to an item. [https://docs.slack.dev/reference/methods/stars.add](https://docs.slack.dev/reference/methods/stars.add) `def stars_list(self, *, count: int | None = None, cursor: str | None = None, limit: int | None = None, page: int | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def stars_list( self, *, count: Optional[int] = None, cursor: Optional[str] = None, limit: Optional[int] = None, page: Optional[int] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists stars for a user. https://docs.slack.dev/reference/methods/stars.list """ kwargs.update( { "count": count, "cursor": cursor, "limit": limit, "page": page, "team_id": team_id, } ) return self.api_call("stars.list", http_verb="GET", params=kwargs) ``` Lists stars for a user. [https://docs.slack.dev/reference/methods/stars.list](https://docs.slack.dev/reference/methods/stars.list) `def stars_remove(self, *, channel: str | None = None, file: str | None = None, file_comment: str | None = None, timestamp: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def stars_remove( self, *, channel: Optional[str] = None, file: Optional[str] = None, file_comment: Optional[str] = None, timestamp: Optional[str] = None, **kwargs, ) -> SlackResponse: """Removes a star from an item. https://docs.slack.dev/reference/methods/stars.remove """ kwargs.update( { "channel": channel, "file": file, "file_comment": file_comment, "timestamp": timestamp, } ) return self.api_call("stars.remove", params=kwargs) ``` Removes a star from an item. [https://docs.slack.dev/reference/methods/stars.remove](https://docs.slack.dev/reference/methods/stars.remove) `def team_accessLogs(self, *, before: str | int | None = None, count: str | int | None = None, page: str | int | None = None, team_id: str | None = None, cursor: str | None = None, limit: int | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def team_accessLogs( self, *, before: Optional[Union[int, str]] = None, count: Optional[Union[int, str]] = None, page: Optional[Union[int, str]] = None, team_id: Optional[str] = None, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Gets the access logs for the current team. https://docs.slack.dev/reference/methods/team.accessLogs """ kwargs.update( { "before": before, "count": count, "page": page, "team_id": team_id, "cursor": cursor, "limit": limit, } ) return self.api_call("team.accessLogs", http_verb="GET", params=kwargs) ``` Gets the access logs for the current team. [https://docs.slack.dev/reference/methods/team.accessLogs](https://docs.slack.dev/reference/methods/team.accessLogs) `def team_billableInfo(self, *, team_id: str | None = None, user: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def team_billableInfo( self, *, team_id: Optional[str] = None, user: Optional[str] = None, **kwargs, ) -> SlackResponse: """Gets billable users information for the current team. https://docs.slack.dev/reference/methods/team.billableInfo """ kwargs.update({"team_id": team_id, "user": user}) return self.api_call("team.billableInfo", http_verb="GET", params=kwargs) ``` Gets billable users information for the current team. [https://docs.slack.dev/reference/methods/team.billableInfo](https://docs.slack.dev/reference/methods/team.billableInfo) `def team_billing_info(self, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def team_billing_info( self, **kwargs, ) -> SlackResponse: """Reads a workspace's billing plan information. https://docs.slack.dev/reference/methods/team.billing.info """ return self.api_call("team.billing.info", params=kwargs) ``` Reads a workspace's billing plan information. [https://docs.slack.dev/reference/methods/team.billing.info](https://docs.slack.dev/reference/methods/team.billing.info) `def team_externalTeams_disconnect(self, *, target_team: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def team_externalTeams_disconnect( self, *, target_team: str, **kwargs, ) -> SlackResponse: """Disconnects an external organization. https://docs.slack.dev/reference/methods/team.externalTeams.disconnect """ kwargs.update( { "target_team": target_team, } ) return self.api_call("team.externalTeams.disconnect", params=kwargs) ``` Disconnects an external organization. [https://docs.slack.dev/reference/methods/team.externalTeams.disconnect](https://docs.slack.dev/reference/methods/team.externalTeams.disconnect) `def team_externalTeams_list(self, *, connection_status_filter: str | None = None, slack_connect_pref_filter: Sequence[str] | None = None, sort_direction: str | None = None, sort_field: str | None = None, workspace_filter: Sequence[str] | None = None, cursor: str | None = None, limit: int | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def team_externalTeams_list( self, *, connection_status_filter: Optional[str] = None, slack_connect_pref_filter: Optional[Sequence[str]] = None, sort_direction: Optional[str] = None, sort_field: Optional[str] = None, workspace_filter: Optional[Sequence[str]] = None, cursor: Optional[str] = None, limit: Optional[int] = None, **kwargs, ) -> SlackResponse: """Returns a list of all the external teams connected and details about the connection. https://docs.slack.dev/reference/methods/team.externalTeams.list """ kwargs.update( { "connection_status_filter": connection_status_filter, "sort_direction": sort_direction, "sort_field": sort_field, "cursor": cursor, "limit": limit, } ) if slack_connect_pref_filter is not None: if isinstance(slack_connect_pref_filter, (list, tuple)): kwargs.update({"slack_connect_pref_filter": ",".join(slack_connect_pref_filter)}) else: kwargs.update({"slack_connect_pref_filter": slack_connect_pref_filter}) if workspace_filter is not None: if isinstance(workspace_filter, (list, tuple)): kwargs.update({"workspace_filter": ",".join(workspace_filter)}) else: kwargs.update({"workspace_filter": workspace_filter}) return self.api_call("team.externalTeams.list", http_verb="GET", params=kwargs) ``` Returns a list of all the external teams connected and details about the connection. [https://docs.slack.dev/reference/methods/team.externalTeams.list](https://docs.slack.dev/reference/methods/team.externalTeams.list) `def team_info(self, *, team: str | None = None, domain: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def team_info( self, *, team: Optional[str] = None, domain: Optional[str] = None, **kwargs, ) -> SlackResponse: """Gets information about the current team. https://docs.slack.dev/reference/methods/team.info """ kwargs.update({"team": team, "domain": domain}) return self.api_call("team.info", http_verb="GET", params=kwargs) ``` Gets information about the current team. [https://docs.slack.dev/reference/methods/team.info](https://docs.slack.dev/reference/methods/team.info) `def team_integrationLogs(self, *, app_id: str | None = None, change_type: str | None = None, count: str | int | None = None, page: str | int | None = None, service_id: str | None = None, team_id: str | None = None, user: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def team_integrationLogs( self, *, app_id: Optional[str] = None, change_type: Optional[str] = None, count: Optional[Union[int, str]] = None, page: Optional[Union[int, str]] = None, service_id: Optional[str] = None, team_id: Optional[str] = None, user: Optional[str] = None, **kwargs, ) -> SlackResponse: """Gets the integration logs for the current team. https://docs.slack.dev/reference/methods/team.integrationLogs """ kwargs.update( { "app_id": app_id, "change_type": change_type, "count": count, "page": page, "service_id": service_id, "team_id": team_id, "user": user, } ) return self.api_call("team.integrationLogs", http_verb="GET", params=kwargs) ``` Gets the integration logs for the current team. [https://docs.slack.dev/reference/methods/team.integrationLogs](https://docs.slack.dev/reference/methods/team.integrationLogs) `def team_preferences_list(self, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def team_preferences_list( self, **kwargs, ) -> SlackResponse: """Retrieve a list of a workspace's team preferences. https://docs.slack.dev/reference/methods/team.preferences.list """ return self.api_call("team.preferences.list", params=kwargs) ``` Retrieve a list of a workspace's team preferences. [https://docs.slack.dev/reference/methods/team.preferences.list](https://docs.slack.dev/reference/methods/team.preferences.list) `def team_profile_get(self, *, visibility: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def team_profile_get( self, *, visibility: Optional[str] = None, **kwargs, ) -> SlackResponse: """Retrieve a team's profile. https://docs.slack.dev/reference/methods/team.profile.get """ kwargs.update({"visibility": visibility}) return self.api_call("team.profile.get", http_verb="GET", params=kwargs) ``` Retrieve a team's profile. [https://docs.slack.dev/reference/methods/team.profile.get](https://docs.slack.dev/reference/methods/team.profile.get) `def tooling_tokens_rotate(self, *, refresh_token: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def tooling_tokens_rotate( self, *, refresh_token: str, **kwargs, ) -> SlackResponse: """Exchanges a refresh token for a new app configuration token https://docs.slack.dev/reference/methods/tooling.tokens.rotate """ kwargs.update({"refresh_token": refresh_token}) return self.api_call("tooling.tokens.rotate", params=kwargs) ``` Exchanges a refresh token for a new app configuration token [https://docs.slack.dev/reference/methods/tooling.tokens.rotate](https://docs.slack.dev/reference/methods/tooling.tokens.rotate) `def usergroups_create(self, *, name: str, channels: str | Sequence[str] | None = None, description: str | None = None, handle: str | None = None, include_count: bool | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def usergroups_create( self, *, name: str, channels: Optional[Union[str, Sequence[str]]] = None, description: Optional[str] = None, handle: Optional[str] = None, include_count: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Create a User Group https://docs.slack.dev/reference/methods/usergroups.create """ kwargs.update( { "name": name, "description": description, "handle": handle, "include_count": include_count, "team_id": team_id, } ) if isinstance(channels, (list, tuple)): kwargs.update({"channels": ",".join(channels)}) else: kwargs.update({"channels": channels}) return self.api_call("usergroups.create", params=kwargs) ``` Create a User Group [https://docs.slack.dev/reference/methods/usergroups.create](https://docs.slack.dev/reference/methods/usergroups.create) `def usergroups_disable(self, *, usergroup: str, include_count: bool | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def usergroups_disable( self, *, usergroup: str, include_count: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Disable an existing User Group https://docs.slack.dev/reference/methods/usergroups.disable """ kwargs.update({"usergroup": usergroup, "include_count": include_count, "team_id": team_id}) return self.api_call("usergroups.disable", params=kwargs) ``` Disable an existing User Group [https://docs.slack.dev/reference/methods/usergroups.disable](https://docs.slack.dev/reference/methods/usergroups.disable) `def usergroups_enable(self, *, usergroup: str, include_count: bool | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def usergroups_enable( self, *, usergroup: str, include_count: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Enable a User Group https://docs.slack.dev/reference/methods/usergroups.enable """ kwargs.update({"usergroup": usergroup, "include_count": include_count, "team_id": team_id}) return self.api_call("usergroups.enable", params=kwargs) ``` Enable a User Group [https://docs.slack.dev/reference/methods/usergroups.enable](https://docs.slack.dev/reference/methods/usergroups.enable) `def usergroups_list(self, *, include_count: bool | None = None, include_disabled: bool | None = None, include_users: bool | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def usergroups_list( self, *, include_count: Optional[bool] = None, include_disabled: Optional[bool] = None, include_users: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List all User Groups for a team https://docs.slack.dev/reference/methods/usergroups.list """ kwargs.update( { "include_count": include_count, "include_disabled": include_disabled, "include_users": include_users, "team_id": team_id, } ) return self.api_call("usergroups.list", http_verb="GET", params=kwargs) ``` List all User Groups for a team [https://docs.slack.dev/reference/methods/usergroups.list](https://docs.slack.dev/reference/methods/usergroups.list) `def usergroups_update(self, *, usergroup: str, channels: str | Sequence[str] | None = None, description: str | None = None, handle: str | None = None, include_count: bool | None = None, name: str | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def usergroups_update( self, *, usergroup: str, channels: Optional[Union[str, Sequence[str]]] = None, description: Optional[str] = None, handle: Optional[str] = None, include_count: Optional[bool] = None, name: Optional[str] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Update an existing User Group https://docs.slack.dev/reference/methods/usergroups.update """ kwargs.update( { "usergroup": usergroup, "description": description, "handle": handle, "include_count": include_count, "name": name, "team_id": team_id, } ) if isinstance(channels, (list, tuple)): kwargs.update({"channels": ",".join(channels)}) else: kwargs.update({"channels": channels}) return self.api_call("usergroups.update", params=kwargs) ``` Update an existing User Group [https://docs.slack.dev/reference/methods/usergroups.update](https://docs.slack.dev/reference/methods/usergroups.update) `def usergroups_users_list(self, *, usergroup: str, include_disabled: bool | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def usergroups_users_list( self, *, usergroup: str, include_disabled: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """List all users in a User Group https://docs.slack.dev/reference/methods/usergroups.users.list """ kwargs.update( { "usergroup": usergroup, "include_disabled": include_disabled, "team_id": team_id, } ) return self.api_call("usergroups.users.list", http_verb="GET", params=kwargs) ``` List all users in a User Group [https://docs.slack.dev/reference/methods/usergroups.users.list](https://docs.slack.dev/reference/methods/usergroups.users.list) `def usergroups_users_update(self, *, usergroup: str, users: str | Sequence[str], include_count: bool | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def usergroups_users_update( self, *, usergroup: str, users: Union[str, Sequence[str]], include_count: Optional[bool] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Update the list of users for a User Group https://docs.slack.dev/reference/methods/usergroups.users.update """ kwargs.update( { "usergroup": usergroup, "include_count": include_count, "team_id": team_id, } ) if isinstance(users, (list, tuple)): kwargs.update({"users": ",".join(users)}) else: kwargs.update({"users": users}) return self.api_call("usergroups.users.update", params=kwargs) ``` Update the list of users for a User Group [https://docs.slack.dev/reference/methods/usergroups.users.update](https://docs.slack.dev/reference/methods/usergroups.users.update) `def users_conversations(self, *, cursor: str | None = None, exclude_archived: bool | None = None, limit: int | None = None, team_id: str | None = None, types: str | Sequence[str] | None = None, user: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def users_conversations( self, *, cursor: Optional[str] = None, exclude_archived: Optional[bool] = None, limit: Optional[int] = None, team_id: Optional[str] = None, types: Optional[Union[str, Sequence[str]]] = None, user: Optional[str] = None, **kwargs, ) -> SlackResponse: """List conversations the calling user may access. https://docs.slack.dev/reference/methods/users.conversations """ kwargs.update( { "cursor": cursor, "exclude_archived": exclude_archived, "limit": limit, "team_id": team_id, "user": user, } ) if isinstance(types, (list, tuple)): kwargs.update({"types": ",".join(types)}) else: kwargs.update({"types": types}) return self.api_call("users.conversations", http_verb="GET", params=kwargs) ``` List conversations the calling user may access. [https://docs.slack.dev/reference/methods/users.conversations](https://docs.slack.dev/reference/methods/users.conversations) `def users_deletePhoto(self, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def users_deletePhoto( self, **kwargs, ) -> SlackResponse: """Delete the user profile photo https://docs.slack.dev/reference/methods/users.deletePhoto """ return self.api_call("users.deletePhoto", http_verb="GET", params=kwargs) ``` Delete the user profile photo [https://docs.slack.dev/reference/methods/users.deletePhoto](https://docs.slack.dev/reference/methods/users.deletePhoto) `def users_discoverableContacts_lookup(self, email: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def users_discoverableContacts_lookup( self, email: str, **kwargs, ) -> SlackResponse: """Lookup an email address to see if someone is on Slack https://docs.slack.dev/reference/methods/users.discoverableContacts.lookup """ kwargs.update({"email": email}) return self.api_call("users.discoverableContacts.lookup", params=kwargs) ``` Lookup an email address to see if someone is on Slack [https://docs.slack.dev/reference/methods/users.discoverableContacts.lookup](https://docs.slack.dev/reference/methods/users.discoverableContacts.lookup) `def users_getPresence(self, *, user: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def users_getPresence( self, *, user: str, **kwargs, ) -> SlackResponse: """Gets user presence information. https://docs.slack.dev/reference/methods/users.getPresence """ kwargs.update({"user": user}) return self.api_call("users.getPresence", http_verb="GET", params=kwargs) ``` Gets user presence information. [https://docs.slack.dev/reference/methods/users.getPresence](https://docs.slack.dev/reference/methods/users.getPresence) `def users_identity(self, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def users_identity( self, **kwargs, ) -> SlackResponse: """Get a user's identity. https://docs.slack.dev/reference/methods/users.identity """ return self.api_call("users.identity", http_verb="GET", params=kwargs) ``` Get a user's identity. [https://docs.slack.dev/reference/methods/users.identity](https://docs.slack.dev/reference/methods/users.identity) `def users_info(self, *, user: str, include_locale: bool | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def users_info( self, *, user: str, include_locale: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Gets information about a user. https://docs.slack.dev/reference/methods/users.info """ kwargs.update({"user": user, "include_locale": include_locale}) return self.api_call("users.info", http_verb="GET", params=kwargs) ``` Gets information about a user. [https://docs.slack.dev/reference/methods/users.info](https://docs.slack.dev/reference/methods/users.info) `def users_list(self, *, cursor: str | None = None, include_locale: bool | None = None, limit: int | None = None, team_id: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def users_list( self, *, cursor: Optional[str] = None, include_locale: Optional[bool] = None, limit: Optional[int] = None, team_id: Optional[str] = None, **kwargs, ) -> SlackResponse: """Lists all users in a Slack team. https://docs.slack.dev/reference/methods/users.list """ kwargs.update( { "cursor": cursor, "include_locale": include_locale, "limit": limit, "team_id": team_id, } ) return self.api_call("users.list", http_verb="GET", params=kwargs) ``` Lists all users in a Slack team. [https://docs.slack.dev/reference/methods/users.list](https://docs.slack.dev/reference/methods/users.list) `def users_lookupByEmail(self, *, email: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def users_lookupByEmail( self, *, email: str, **kwargs, ) -> SlackResponse: """Find a user with an email address. https://docs.slack.dev/reference/methods/users.lookupByEmail """ kwargs.update({"email": email}) return self.api_call("users.lookupByEmail", http_verb="GET", params=kwargs) ``` Find a user with an email address. [https://docs.slack.dev/reference/methods/users.lookupByEmail](https://docs.slack.dev/reference/methods/users.lookupByEmail) `def users_profile_get(self, *, user: str | None = None, include_labels: bool | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def users_profile_get( self, *, user: Optional[str] = None, include_labels: Optional[bool] = None, **kwargs, ) -> SlackResponse: """Retrieves a user's profile information. https://docs.slack.dev/reference/methods/users.profile.get """ kwargs.update({"user": user, "include_labels": include_labels}) return self.api_call("users.profile.get", http_verb="GET", params=kwargs) ``` Retrieves a user's profile information. [https://docs.slack.dev/reference/methods/users.profile.get](https://docs.slack.dev/reference/methods/users.profile.get) `def users_profile_set(self, *, name: str | None = None, value: str | None = None, user: str | None = None, profile: Dict | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def users_profile_set( self, *, name: Optional[str] = None, value: Optional[str] = None, user: Optional[str] = None, profile: Optional[Dict] = None, **kwargs, ) -> SlackResponse: """Set the profile information for a user. https://docs.slack.dev/reference/methods/users.profile.set """ kwargs.update( { "name": name, "profile": profile, "user": user, "value": value, } ) kwargs = _remove_none_values(kwargs) # NOTE: Intentionally using json for the "profile" parameter return self.api_call("users.profile.set", json=kwargs) ``` Set the profile information for a user. [https://docs.slack.dev/reference/methods/users.profile.set](https://docs.slack.dev/reference/methods/users.profile.set) `def users_setPhoto(self, *, image: str | io.IOBase, crop_w: str | int | None = None, crop_x: str | int | None = None, crop_y: str | int | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def users_setPhoto( self, *, image: Union[str, IOBase], crop_w: Optional[Union[int, str]] = None, crop_x: Optional[Union[int, str]] = None, crop_y: Optional[Union[int, str]] = None, **kwargs, ) -> SlackResponse: """Set the user profile photo https://docs.slack.dev/reference/methods/users.setPhoto """ kwargs.update({"crop_w": crop_w, "crop_x": crop_x, "crop_y": crop_y}) return self.api_call("users.setPhoto", files={"image": image}, data=kwargs) ``` Set the user profile photo [https://docs.slack.dev/reference/methods/users.setPhoto](https://docs.slack.dev/reference/methods/users.setPhoto) `def users_setPresence(self, *, presence: str, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def users_setPresence( self, *, presence: str, **kwargs, ) -> SlackResponse: """Manually sets user presence. https://docs.slack.dev/reference/methods/users.setPresence """ kwargs.update({"presence": presence}) return self.api_call("users.setPresence", params=kwargs) ``` Manually sets user presence. [https://docs.slack.dev/reference/methods/users.setPresence](https://docs.slack.dev/reference/methods/users.setPresence) `def views_open(self, *, trigger_id: str | None = None, interactivity_pointer: str | None = None, view: dict | [View](../models/views/index.html#slack_sdk.models.views.View "slack_sdk.models.views.View"), **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def views_open( self, *, trigger_id: Optional[str] = None, interactivity_pointer: Optional[str] = None, view: Union[dict, View], **kwargs, ) -> SlackResponse: """Open a view for a user. https://docs.slack.dev/reference/methods/views.open See https://docs.slack.dev/surfaces/modals/ for details. """ kwargs.update({"trigger_id": trigger_id, "interactivity_pointer": interactivity_pointer}) if isinstance(view, View): kwargs.update({"view": view.to_dict()}) else: kwargs.update({"view": view}) kwargs = _remove_none_values(kwargs) # NOTE: Intentionally using json for the "view" parameter return self.api_call("views.open", json=kwargs) ``` Open a view for a user. [https://docs.slack.dev/reference/methods/views.open](https://docs.slack.dev/reference/methods/views.open) See [https://docs.slack.dev/surfaces/modals/](https://docs.slack.dev/surfaces/modals/) for details. `def views_publish(self, *, user_id: str, view: dict | [View](../models/views/index.html#slack_sdk.models.views.View "slack_sdk.models.views.View"), hash: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def views_publish( self, *, user_id: str, view: Union[dict, View], hash: Optional[str] = None, **kwargs, ) -> SlackResponse: """Publish a static view for a User. Create or update the view that comprises an app's Home tab (https://docs.slack.dev/surfaces/app-home/) https://docs.slack.dev/reference/methods/views.publish """ kwargs.update({"user_id": user_id, "hash": hash}) if isinstance(view, View): kwargs.update({"view": view.to_dict()}) else: kwargs.update({"view": view}) kwargs = _remove_none_values(kwargs) # NOTE: Intentionally using json for the "view" parameter return self.api_call("views.publish", json=kwargs) ``` Publish a static view for a User. Create or update the view that comprises an app's Home tab ([https://docs.slack.dev/surfaces/app-home/](https://docs.slack.dev/surfaces/app-home/)) [https://docs.slack.dev/reference/methods/views.publish](https://docs.slack.dev/reference/methods/views.publish) `def views_push(self, *, trigger_id: str | None = None, interactivity_pointer: str | None = None, view: dict | [View](../models/views/index.html#slack_sdk.models.views.View "slack_sdk.models.views.View"), **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def views_push( self, *, trigger_id: Optional[str] = None, interactivity_pointer: Optional[str] = None, view: Union[dict, View], **kwargs, ) -> SlackResponse: """Push a view onto the stack of a root view. Push a new view onto the existing view stack by passing a view payload and a valid trigger_id generated from an interaction within the existing modal. Read the modals documentation (https://docs.slack.dev/surfaces/modals/) to learn more about the lifecycle and intricacies of views. https://docs.slack.dev/reference/methods/views.push """ kwargs.update({"trigger_id": trigger_id, "interactivity_pointer": interactivity_pointer}) if isinstance(view, View): kwargs.update({"view": view.to_dict()}) else: kwargs.update({"view": view}) kwargs = _remove_none_values(kwargs) # NOTE: Intentionally using json for the "view" parameter return self.api_call("views.push", json=kwargs) ``` Push a view onto the stack of a root view. Push a new view onto the existing view stack by passing a view payload and a valid trigger\_id generated from an interaction within the existing modal. Read the modals documentation ([https://docs.slack.dev/surfaces/modals/](https://docs.slack.dev/surfaces/modals/)) to learn more about the lifecycle and intricacies of views. [https://docs.slack.dev/reference/methods/views.push](https://docs.slack.dev/reference/methods/views.push) `def views_update(self, *, view: dict | [View](../models/views/index.html#slack_sdk.models.views.View "slack_sdk.models.views.View"), external_id: str | None = None, view_id: str | None = None, hash: str | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def views_update( self, *, view: Union[dict, View], external_id: Optional[str] = None, view_id: Optional[str] = None, hash: Optional[str] = None, **kwargs, ) -> SlackResponse: """Update an existing view. Update a view by passing a new view definition along with the view_id returned in views.open or the external_id. See the modals documentation (https://docs.slack.dev/surfaces/modals/#updating_views) to learn more about updating views and avoiding race conditions with the hash argument. https://docs.slack.dev/reference/methods/views.update """ if isinstance(view, View): kwargs.update({"view": view.to_dict()}) else: kwargs.update({"view": view}) if external_id: kwargs.update({"external_id": external_id}) elif view_id: kwargs.update({"view_id": view_id}) else: raise e.SlackRequestError("Either view_id or external_id is required.") kwargs.update({"hash": hash}) kwargs = _remove_none_values(kwargs) # NOTE: Intentionally using json for the "view" parameter return self.api_call("views.update", json=kwargs) ``` Update an existing view. Update a view by passing a new view definition along with the view\_id returned in views.open or the external\_id. See the modals documentation ([https://docs.slack.dev/surfaces/modals/#updating\_views](https://docs.slack.dev/surfaces/modals/#updating_views)) to learn more about updating views and avoiding race conditions with the hash argument. [https://docs.slack.dev/reference/methods/views.update](https://docs.slack.dev/reference/methods/views.update) `def workflows_featured_add(self, *, channel_id: str, trigger_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def workflows_featured_add( self, *, channel_id: str, trigger_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Add featured workflows to a channel. https://docs.slack.dev/reference/methods/workflows.featured.add """ kwargs.update({"channel_id": channel_id}) if isinstance(trigger_ids, (list, tuple)): kwargs.update({"trigger_ids": ",".join(trigger_ids)}) else: kwargs.update({"trigger_ids": trigger_ids}) return self.api_call("workflows.featured.add", params=kwargs) ``` Add featured workflows to a channel. [https://docs.slack.dev/reference/methods/workflows.featured.add](https://docs.slack.dev/reference/methods/workflows.featured.add) `def workflows_featured_list(self, *, channel_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def workflows_featured_list( self, *, channel_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """List the featured workflows for specified channels. https://docs.slack.dev/reference/methods/workflows.featured.list """ if isinstance(channel_ids, (list, tuple)): kwargs.update({"channel_ids": ",".join(channel_ids)}) else: kwargs.update({"channel_ids": channel_ids}) return self.api_call("workflows.featured.list", params=kwargs) ``` List the featured workflows for specified channels. [https://docs.slack.dev/reference/methods/workflows.featured.list](https://docs.slack.dev/reference/methods/workflows.featured.list) `def workflows_featured_remove(self, *, channel_id: str, trigger_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def workflows_featured_remove( self, *, channel_id: str, trigger_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Remove featured workflows from a channel. https://docs.slack.dev/reference/methods/workflows.featured.remove """ kwargs.update({"channel_id": channel_id}) if isinstance(trigger_ids, (list, tuple)): kwargs.update({"trigger_ids": ",".join(trigger_ids)}) else: kwargs.update({"trigger_ids": trigger_ids}) return self.api_call("workflows.featured.remove", params=kwargs) ``` Remove featured workflows from a channel. [https://docs.slack.dev/reference/methods/workflows.featured.remove](https://docs.slack.dev/reference/methods/workflows.featured.remove) `def workflows_featured_set(self, *, channel_id: str, trigger_ids: str | Sequence[str], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def workflows_featured_set( self, *, channel_id: str, trigger_ids: Union[str, Sequence[str]], **kwargs, ) -> SlackResponse: """Set featured workflows for a channel. https://docs.slack.dev/reference/methods/workflows.featured.set """ kwargs.update({"channel_id": channel_id}) if isinstance(trigger_ids, (list, tuple)): kwargs.update({"trigger_ids": ",".join(trigger_ids)}) else: kwargs.update({"trigger_ids": trigger_ids}) return self.api_call("workflows.featured.set", params=kwargs) ``` Set featured workflows for a channel. [https://docs.slack.dev/reference/methods/workflows.featured.set](https://docs.slack.dev/reference/methods/workflows.featured.set) `def workflows_stepCompleted(self, *, workflow_step_execute_id: str, outputs: dict | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def workflows_stepCompleted( self, *, workflow_step_execute_id: str, outputs: Optional[dict] = None, **kwargs, ) -> SlackResponse: """Indicate a successful outcome of a workflow step's execution. https://docs.slack.dev/reference/methods/workflows.stepCompleted """ kwargs.update({"workflow_step_execute_id": workflow_step_execute_id}) if outputs is not None: kwargs.update({"outputs": outputs}) kwargs = _remove_none_values(kwargs) # NOTE: Intentionally using json for the "outputs" parameter return self.api_call("workflows.stepCompleted", json=kwargs) ``` Indicate a successful outcome of a workflow step's execution. [https://docs.slack.dev/reference/methods/workflows.stepCompleted](https://docs.slack.dev/reference/methods/workflows.stepCompleted) `def workflows_stepFailed(self, *, workflow_step_execute_id: str, error: Dict[str, str], **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def workflows_stepFailed( self, *, workflow_step_execute_id: str, error: Dict[str, str], **kwargs, ) -> SlackResponse: """Indicate an unsuccessful outcome of a workflow step's execution. https://docs.slack.dev/reference/methods/workflows.stepFailed """ kwargs.update( { "workflow_step_execute_id": workflow_step_execute_id, "error": error, } ) kwargs = _remove_none_values(kwargs) # NOTE: Intentionally using json for the "error" parameter return self.api_call("workflows.stepFailed", json=kwargs) ``` Indicate an unsuccessful outcome of a workflow step's execution. [https://docs.slack.dev/reference/methods/workflows.stepFailed](https://docs.slack.dev/reference/methods/workflows.stepFailed) `def workflows_updateStep(self, *, workflow_step_edit_id: str, inputs: Dict[str, Any] | None = None, outputs: List[Dict[str, str]] | None = None, **kwargs) ‑> [SlackResponse](slack_response.html#slack_sdk.web.slack_response.SlackResponse "slack_sdk.web.slack_response.SlackResponse")` Expand source code ``` def workflows_updateStep( self, *, workflow_step_edit_id: str, inputs: Optional[Dict[str, Any]] = None, outputs: Optional[List[Dict[str, str]]] = None, **kwargs, ) -> SlackResponse: """Update the configuration for a workflow extension step. https://docs.slack.dev/reference/methods/workflows.updateStep """ kwargs.update({"workflow_step_edit_id": workflow_step_edit_id}) if inputs is not None: kwargs.update({"inputs": inputs}) if outputs is not None: kwargs.update({"outputs": outputs}) kwargs = _remove_none_values(kwargs) # NOTE: Intentionally using json for the "inputs" / "outputs" parameters return self.api_call("workflows.updateStep", json=kwargs) ``` Update the configuration for a workflow extension step. [https://docs.slack.dev/reference/methods/workflows.updateStep](https://docs.slack.dev/reference/methods/workflows.updateStep) ### Inherited members * `**[BaseClient](base_client.html#slack_sdk.web.base_client.BaseClient "slack_sdk.web.base_client.BaseClient")**`: * `[BASE_URL](base_client.html#slack_sdk.web.base_client.BaseClient.BASE_URL "slack_sdk.web.base_client.BaseClient.BASE_URL")` * `[api_call](base_client.html#slack_sdk.web.base_client.BaseClient.api_call "slack_sdk.web.base_client.BaseClient.api_call")` * `[base_url](base_client.html#slack_sdk.web.base_client.BaseClient.base_url "slack_sdk.web.base_client.BaseClient.base_url")` * `[headers](base_client.html#slack_sdk.web.base_client.BaseClient.headers "slack_sdk.web.base_client.BaseClient.headers")` * `[logger](base_client.html#slack_sdk.web.base_client.BaseClient.logger "slack_sdk.web.base_client.BaseClient.logger")` * `[proxy](base_client.html#slack_sdk.web.base_client.BaseClient.proxy "slack_sdk.web.base_client.BaseClient.proxy")` * `[ssl](base_client.html#slack_sdk.web.base_client.BaseClient.ssl "slack_sdk.web.base_client.BaseClient.ssl")` * `[timeout](base_client.html#slack_sdk.web.base_client.BaseClient.timeout "slack_sdk.web.base_client.BaseClient.timeout")` * `[token](base_client.html#slack_sdk.web.base_client.BaseClient.token "slack_sdk.web.base_client.BaseClient.token")` * `[validate_slack_signature](base_client.html#slack_sdk.web.base_client.BaseClient.validate_slack_signature "slack_sdk.web.base_client.BaseClient.validate_slack_signature")` --- Source: https://docs.slack.dev/tools/python-slack-sdk/reference/webhook # Module slack_sdk.webhook You can use slack\_sdk.webhook.WebhookClient for Incoming Webhooks and message responses using response\_url in payloads. ## Sub-modules `[slack_sdk.webhook.async_client](async_client.html "slack_sdk.webhook.async_client")` `[slack_sdk.webhook.client](client.html "slack_sdk.webhook.client")` `[slack_sdk.webhook.internal_utils](internal_utils.html "slack_sdk.webhook.internal_utils")` `[slack_sdk.webhook.webhook_response](webhook_response.html "slack_sdk.webhook.webhook_response")` ## Classes `class WebhookClient (url: str, timeout: int = 30, ssl: ssl.SSLContext | None = None, proxy: str | None = None, default_headers: Dict[str, str] | None = None, user_agent_prefix: str | None = None, user_agent_suffix: str | None = None, logger: logging.Logger | None = None, retry_handlers: List[[RetryHandler](../http_retry/handler.html#slack_sdk.http_retry.handler.RetryHandler "slack_sdk.http_retry.handler.RetryHandler")] | None = None)` Expand source code ``` class WebhookClient: url: str timeout: int ssl: Optional[SSLContext] proxy: Optional[str] default_headers: Dict[str, str] logger: logging.Logger retry_handlers: List[RetryHandler] def __init__( self, url: str, timeout: int = 30, ssl: Optional[SSLContext] = None, proxy: Optional[str] = None, default_headers: Optional[Dict[str, str]] = None, user_agent_prefix: Optional[str] = None, user_agent_suffix: Optional[str] = None, logger: Optional[logging.Logger] = None, retry_handlers: Optional[List[RetryHandler]] = None, ): """API client for Incoming Webhooks and `response_url` https://docs.slack.dev/messaging/sending-messages-using-incoming-webhooks/ Args: url: Complete URL to send data (e.g., `https://hooks.slack.com/XXX`) timeout: Request timeout (in seconds) ssl: `ssl.SSLContext` to use for requests proxy: Proxy URL (e.g., `localhost:9000`, `http://localhost:9000`) default_headers: Request headers to add to all requests user_agent_prefix: Prefix for User-Agent header value user_agent_suffix: Suffix for User-Agent header value logger: Custom logger retry_handlers: Retry handlers """ self.url = url self.timeout = timeout self.ssl = ssl self.proxy = proxy self.default_headers = default_headers if default_headers else {} self.default_headers["User-Agent"] = get_user_agent(user_agent_prefix, user_agent_suffix) self.logger = logger if logger is not None else logging.getLogger(__name__) self.retry_handlers = retry_handlers if retry_handlers is not None else default_retry_handlers() if self.proxy is None or len(self.proxy.strip()) == 0: env_variable = load_http_proxy_from_env(self.logger) if env_variable is not None: self.proxy = env_variable def send( self, *, text: Optional[str] = None, attachments: Optional[Sequence[Union[Dict[str, Any], Attachment]]] = None, blocks: Optional[Sequence[Union[Dict[str, Any], Block]]] = None, 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, metadata: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None, ) -> WebhookResponse: """Performs a Slack API request and returns the result. Args: text: The text message (even when having blocks, setting this as well is recommended as it works as fallback) attachments: A collection of attachments blocks: A collection of Block Kit UI components 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 metadata: Metadata attached to the message headers: Request headers to append only for this request Returns: Webhook response """ return self.send_dict( # It's fine to have None value elements here # because _build_body() filters them out when constructing the actual body data body={ "text": text, "attachments": attachments, "blocks": blocks, "response_type": response_type, "replace_original": replace_original, "delete_original": delete_original, "unfurl_links": unfurl_links, "unfurl_media": unfurl_media, "metadata": metadata, }, headers=headers, ) def send_dict(self, body: Dict[str, Any], headers: Optional[Dict[str, str]] = None) -> WebhookResponse: """Performs a Slack API request and returns the result. Args: body: JSON data structure (it's still a dict at this point), if you give this argument, body_params and files will be skipped headers: Request headers to append only for this request Returns: Webhook response """ return self._perform_http_request( body=_build_body(body), # type: ignore[arg-type] headers=_build_request_headers(self.default_headers, headers), ) def _perform_http_request(self, *, body: Dict[str, Any], headers: Dict[str, str]) -> WebhookResponse: raw_body = json.dumps(body) headers["Content-Type"] = "application/json;charset=utf-8" if self.logger.level <= logging.DEBUG: self.logger.debug(f"Sending a request - url: {self.url}, body: {raw_body}, headers: {headers}") url = self.url # NOTE: Intentionally ignore the `http_verb` here # Slack APIs accepts any API method requests with POST methods req = Request(method="POST", url=url, data=raw_body.encode("utf-8"), headers=headers) resp = None last_error = Exception("undefined internal error") retry_state = RetryState() counter_for_safety = 0 while counter_for_safety < 100: counter_for_safety += 1 # If this is a retry, the next try started here. We can reset the flag. retry_state.next_attempt_requested = False try: resp = self._perform_http_request_internal(url, req) # The resp is a 200 OK response return resp except HTTPError as e: # read the response body here charset = e.headers.get_content_charset() or "utf-8" response_body: str = e.read().decode(charset) # As adding new values to HTTPError#headers can be ignored, building a new dict object here response_headers = dict(e.headers.items()) resp = WebhookResponse( url=url, status_code=e.code, body=response_body, headers=response_headers, ) if e.code == 429: # for backward-compatibility with WebClient (v.2.5.0 or older) if "retry-after" not in resp.headers and "Retry-After" in resp.headers: resp.headers["retry-after"] = resp.headers["Retry-After"] if "Retry-After" not in resp.headers and "retry-after" in resp.headers: resp.headers["Retry-After"] = resp.headers["retry-after"] _debug_log_response(self.logger, resp) # Try to find a retry handler for this error retry_request = RetryHttpRequest.from_urllib_http_request(req) retry_response = RetryHttpResponse( status_code=e.code, headers={k: [v] for k, v in e.headers.items()}, data=response_body.encode("utf-8") if response_body is not None else None, ) for handler in self.retry_handlers: if handler.can_retry( state=retry_state, request=retry_request, response=retry_response, error=e, ): if self.logger.level <= logging.DEBUG: self.logger.info( f"A retry handler found: {type(handler).__name__} for {req.method} {req.full_url} - {e}" ) handler.prepare_for_next_attempt( state=retry_state, request=retry_request, response=retry_response, error=e, ) break if retry_state.next_attempt_requested is False: return resp except Exception as err: last_error = err self.logger.error(f"Failed to send a request to Slack API server: {err}") # Try to find a retry handler for this error retry_request = RetryHttpRequest.from_urllib_http_request(req) for handler in self.retry_handlers: if handler.can_retry( state=retry_state, request=retry_request, response=None, error=err, ): if self.logger.level <= logging.DEBUG: self.logger.info( f"A retry handler found: {type(handler).__name__} for {req.method} {req.full_url} - {err}" ) handler.prepare_for_next_attempt( state=retry_state, request=retry_request, response=None, error=err, ) self.logger.info(f"Going to retry the same request: {req.method} {req.full_url}") break if retry_state.next_attempt_requested is False: raise err if resp is not None: return resp raise last_error def _perform_http_request_internal(self, url: str, req: Request): opener: Optional[OpenerDirector] = None # for security (BAN-B310) if url.lower().startswith("http"): if self.proxy is not None: if isinstance(self.proxy, str): opener = urllib.request.build_opener( ProxyHandler({"http": self.proxy, "https": self.proxy}), HTTPSHandler(context=self.ssl), ) else: raise SlackRequestError(f"Invalid proxy detected: {self.proxy} must be a str value") else: raise SlackRequestError(f"Invalid URL detected: {url}") http_resp: Optional[HTTPResponse] = None if opener: http_resp = opener.open(req, timeout=self.timeout) else: http_resp = urlopen(req, context=self.ssl, timeout=self.timeout) charset: str = http_resp.headers.get_content_charset() or "utf-8" response_body: str = http_resp.read().decode(charset) resp = WebhookResponse( url=url, status_code=http_resp.status, body=response_body, headers=http_resp.headers, # type: ignore[arg-type] ) _debug_log_response(self.logger, resp) return resp ``` API client for Incoming Webhooks and `response_url` [https://docs.slack.dev/messaging/sending-messages-using-incoming-webhooks/](https://docs.slack.dev/messaging/sending-messages-using-incoming-webhooks/) ## Args **`url`** Complete URL to send data (e.g., `https://hooks.slack.com/XXX`) **`timeout`** Request timeout (in seconds) **`ssl`** `ssl.SSLContext` to use for requests **`proxy`** Proxy URL (e.g., `localhost:9000`, `http://localhost:9000`) **`default_headers`** Request headers to add to all requests **`user_agent_prefix`** Prefix for User-Agent header value **`user_agent_suffix`** Suffix for User-Agent header value **`logger`** Custom logger **`retry_handlers`** Retry handlers ### Class variables `var default_headers : Dict[str, str]` The type of the None singleton. `var logger : logging.Logger` The type of the None singleton. `var proxy : str | None` The type of the None singleton. `var retry_handlers : List[[RetryHandler](../http_retry/handler.html#slack_sdk.http_retry.handler.RetryHandler "slack_sdk.http_retry.handler.RetryHandler")]` The type of the None singleton. `var ssl : ssl.SSLContext | None` The type of the None singleton. `var timeout : int` The type of the None singleton. `var url : str` The type of the None singleton. ### Methods `def send(self, *, text: str | None = None, attachments: Sequence[Dict[str, Any] | [Attachment](../models/attachments/index.html#slack_sdk.models.attachments.Attachment "slack_sdk.models.attachments.Attachment")] | None = None, blocks: Sequence[Dict[str, Any] | [Block](../models/blocks/blocks.html#slack_sdk.models.blocks.blocks.Block "slack_sdk.models.blocks.blocks.Block")] | None = None, response_type: str | None = None, replace_original: bool | None = None, delete_original: bool | None = None, unfurl_links: bool | None = None, unfurl_media: bool | None = None, metadata: Dict[str, Any] | None = None, headers: Dict[str, str] | None = None) ‑> [WebhookResponse](webhook_response.html#slack_sdk.webhook.webhook_response.WebhookResponse "slack_sdk.webhook.webhook_response.WebhookResponse")` Expand source code ``` def send( self, *, text: Optional[str] = None, attachments: Optional[Sequence[Union[Dict[str, Any], Attachment]]] = None, blocks: Optional[Sequence[Union[Dict[str, Any], Block]]] = None, 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, metadata: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None, ) -> WebhookResponse: """Performs a Slack API request and returns the result. Args: text: The text message (even when having blocks, setting this as well is recommended as it works as fallback) attachments: A collection of attachments blocks: A collection of Block Kit UI components 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 metadata: Metadata attached to the message headers: Request headers to append only for this request Returns: Webhook response """ return self.send_dict( # It's fine to have None value elements here # because _build_body() filters them out when constructing the actual body data body={ "text": text, "attachments": attachments, "blocks": blocks, "response_type": response_type, "replace_original": replace_original, "delete_original": delete_original, "unfurl_links": unfurl_links, "unfurl_media": unfurl_media, "metadata": metadata, }, headers=headers, ) ``` Performs a Slack API request and returns the result. ## Args **`text`** The text message (even when having blocks, setting this as well is recommended as it works as fallback) **`attachments`** A collection of attachments **`blocks`** A collection of Block Kit UI components **`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 **`metadata`** Metadata attached to the message **`headers`** Request headers to append only for this request ## Returns Webhook response `def send_dict(self, body: Dict[str, Any], headers: Dict[str, str] | None = None) ‑> [WebhookResponse](webhook_response.html#slack_sdk.webhook.webhook_response.WebhookResponse "slack_sdk.webhook.webhook_response.WebhookResponse")` Expand source code ``` def send_dict(self, body: Dict[str, Any], headers: Optional[Dict[str, str]] = None) -> WebhookResponse: """Performs a Slack API request and returns the result. Args: body: JSON data structure (it's still a dict at this point), if you give this argument, body_params and files will be skipped headers: Request headers to append only for this request Returns: Webhook response """ return self._perform_http_request( body=_build_body(body), # type: ignore[arg-type] headers=_build_request_headers(self.default_headers, headers), ) ``` Performs a Slack API request and returns the result. ## Args **`body`** JSON data structure (it's still a dict at this point), if you give this argument, body\_params and files will be skipped **`headers`** Request headers to append only for this request ## Returns Webhook response `class WebhookResponse (*, url: str, status_code: int, body: str, headers: Dict[str, Any])` Expand source code ``` class WebhookResponse: def __init__( self, *, url: str, status_code: int, body: str, headers: Dict[str, Any], ): self.api_url = url self.status_code = status_code self.body = body self.headers = headers ``` --- Source: https://docs.slack.dev/tools/python-slack-sdk/rtm # RTM API client The [Legacy Real Time Messaging (RTM) API](/legacy/legacy-rtm-api) is a WebSocket-based API that allows you to receive events from Slack in real time and to send messages as users. If you prefer events to be pushed to your app, we recommend using the HTTP-based [Events API](/apis/events-api) instead. The Events API contains some events that aren't supported in the Legacy RTM API (such as the [app\_home\_opened event](/reference/events/app_home_opened)), and it supports most of the event types in the Legacy RTM API. If you'd like to use the Events API, you can use the [Python Slack Events Adapter](https://github.com/slackapi/python-slack-events-api). The `RTMClient` allows apps to communicate with the Legacy RTM API. The event-driven architecture of this client allows you to simply link callbacks to their corresponding events. When an event occurs, this client executes your callback while passing along any information it receives. We also give you the ability to call our web client from inside your callbacks. In our example below, we watch for a [message event](/reference/events/message) that contains "Hello" and if it's received, we call the `say_hello()` function. We then issue a call to the web client to post back to the channel saying "Hi" to the user. ## Configuring the RTM API {#configuration} Events using the Legacy RTM API **must** use a Slack app with a plain `bot` scope. If you already have a Slack app with a plain `bot` scope, you can use those credentials. If you don't and need to use the Legacy RTM API, you can create a Slack app [here](https://api.slack.com/apps?new_classic_app=1). Even if the Slack app configuration pages encourage you to upgrade to a newer permission model, don't upgrade it and continue using the "classic" bot permission. ## Connecting to the RTM API {#connecting} Note that the import here is not `from slack_sdk.rtm import RTMClient` but `from slack_sdk.rtm_v2 import RTMClient` (`_v2` is added in the latter one). ``` import osfrom slack_sdk.rtm_v2 import RTMClientrtm = RTMClient(token=os.environ["SLACK_BOT_TOKEN"])@rtm.on("message")def handle(client: RTMClient, event: dict): if 'Hello' in event['text']: channel_id = event['channel'] thread_ts = event['ts'] user = event['user'] # This is not username but user ID (the format is either U*** or W***) client.web_client.chat_postMessage( channel=channel_id, text=f"Hi <@{user}>!", thread_ts=thread_ts )rtm.start() ``` ## Connecting to the RTM API (v1 client) {#connecting-v1} Below is a code snippet that uses the legacy version of `RTMClient`. For new app development, we **do not recommend** using it as it contains issues that have been resolved in v2. Please refer to the [list of these issues](https://github.com/slackapi/python-slack-sdk/issues?q=is%3Aissue+is%3Aclosed+milestone%3A3.3.0+label%3Artm-client) for more details. ``` import osfrom slack_sdk.rtm import RTMClient@RTMClient.run_on(event="message")def say_hello(**payload): data = payload['data'] web_client = payload['web_client'] if 'Hello' in data['text']: channel_id = data['channel'] thread_ts = data['ts'] user = data['user'] # This is not username but user ID (the format is either U*** or W***) web_client.chat_postMessage( channel=channel_id, text=f"Hi <@{user}>!", thread_ts=thread_ts )slack_token = os.environ["SLACK_BOT_TOKEN"]rtm_client = RTMClient(token=slack_token)rtm_client.start() ``` ## The rtm.start vs. rtm.connect API methods (v1 client) {#rtm-methods} By default, the RTM client uses the [`rtm.connect`](/reference/methods/rtm.connect) API method to establish a WebSocket connection with Slack. The response contains basic information about the team and WebSocket URL. See the [`rtm.connect`](/reference/methods/rtm.connect) and [`rtm.start`](/reference/methods/rtm.start) API methods for more details. Note that `slack_sdk.rtm_v2.RTMClient` does not support `rtm.start`. ## RTM events {#rtm-events} ``` { 'type': 'message', 'ts': '1358878749.000002', 'user': 'U023BECGF', 'text': 'Hello'} ``` Refer to the [Legacy RTM events](/legacy/legacy-rtm-api#events) section for a complete list of events. --- Source: https://docs.slack.dev/tools/python-slack-sdk/scim # SCIM API client [SCIM](http://www.simplecloud.info/) is supported by a myriad of services. The SCIM API is a set of APIs for provisioning and managing user accounts and groups. SCIM is used by Single Sign-On (SSO) services and identity providers to manage people across a variety of tools, including Slack. Refer to [using the Slack SCIM API](/admins/scim-api) for more details. View the [Python document for this module](https://docs.slack.dev/tools/python-slack-sdk/reference). ## SCIMClient {#scimclient} An OAuth token with [the admin scope](/reference/scopes/admin) is required to access the SCIM API. To fetch provisioned user data, you can use the `search_users` method in the client. ``` import osfrom slack_sdk.scim import SCIMClientclient = SCIMClient(token=os.environ["SLACK_ORG_ADMIN_USER_TOKEN"])response = client.search_users( start_index=1, count=100, filter='userName Eq "Carly"',)response.users # List[User] ``` Check out [the class source code](https://github.com/slackapi/python-slack-sdk/blob/main/slack_sdk/scim/v1/user.py) to learn more about the structure of the `user` in `response.users`. Similarly, the `search_groups` method is available and the shape of the `Group` object can be [found here](https://github.com/slackapi/python-slack-sdk/blob/main/slack_sdk/scim/v1/group.py). ``` response = client.search_groups( start_index=1, count=10,)response.groups # List[Group] ``` For creating, updating, and deleting users or groups: ``` from slack_sdk.scim.v1.user import User, UserName, UserEmail# POST /Users# Creates a user. Must include the user_name argument and at least one email address.# You may provide an email address as the user_name value,# but it will be automatically converted to a Slack-appropriate username.user = User( user_name="cal", name=UserName(given_name="C", family_name="Henderson"), emails=[UserEmail(value="your-unique-name@example.com")],)creation_result = client.create_user(user)# PATCH /Users/{user_id}# Updates an existing user resource, overwriting values for specified attributes.patch_result = client.patch_user( id=creation_result.user.id, partial_user=User(user_name="chenderson"),)# PUT /Users/{user_id}# Updates an existing user resource, overwriting all values for a user# even if an attribute is empty or not provided.user_to_update = patch_result.useruser_to_update.name = UserName(given_name="Cal", family_name="Henderson")update_result = client.update_user(user=user_to_update)# DELETE /Users/{user_id}# Sets a Slack user to deactivated. The value of the {id}# should be the user's corresponding Slack ID, beginning with either U or W.delete_result = client.delete_user(user_to_update.id) ``` ## AsyncSCIMClient {#asyncscimclient} If you are keen to use asyncio for SCIM API calls, we offer `AsyncSCIMClient`. This client relies on the aiohttp library. ``` import asyncioimport osfrom slack_sdk.scim.async_client import AsyncSCIMClientclient = AsyncSCIMClient(token=os.environ["SLACK_ORG_ADMIN_USER_TOKEN"])async def main(): response = await client.search_groups(start_index=1, count=2) print(response.groups)asyncio.run(main()) ``` * * * ## RetryHandler {#retryhandler} With the default settings, only `ConnectionErrorRetryHandler` with its default configuration (=only one retry in the manner of [exponential backoff and jitter](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/)) is enabled. The retry handler retries if an API client encounters a connectivity-related failure (e.g., connection reset by peer). To use other retry handlers, you can pass a list of `RetryHandler` to the client constructor. For instance, you can add the built-in `RateLimitErrorRetryHandler` this way: ``` import osfrom slack_sdk.scim import SCIMClientclient = SCIMClient(token=os.environ["SLACK_ORG_ADMIN_USER_TOKEN"])# This handler does retries when HTTP status 429 is returnedfrom slack_sdk.http_retry.builtin_handlers import RateLimitErrorRetryHandlerrate_limit_handler = RateLimitErrorRetryHandler(max_retry_count=1)# Enable rate limited error retries as wellclient.retry_handlers.append(rate_limit_handler) ``` You can also create one on your own by defining a new class that inherits `slack_sdk.http_retry.RetryHandler` (`AsyncRetryHandler` for asyncio apps) and implements required methods (internals of `can_retry` / `prepare_for_next_attempt`). Check out the source code for the ones that are built in to learn how to properly implement them. ``` import socketfrom typing import Optionalfrom slack_sdk.http_retry import (RetryHandler, RetryState, HttpRequest, HttpResponse)from slack_sdk.http_retry.builtin_interval_calculators import BackoffRetryIntervalCalculatorfrom slack_sdk.http_retry.jitter import RandomJitterclass MyRetryHandler(RetryHandler): def _can_retry( self, *, state: RetryState, request: HttpRequest, response: Optional[HttpResponse] = None, error: Optional[Exception] = None ) -> bool: # [Errno 104] Connection reset by peer return error is not None and isinstance(error, socket.error) and error.errno == 104client = SCIMClient( token=os.environ["SLACK_ORG_ADMIN_USER_TOKEN"], retry_handlers=[MyRetryHandler( max_retry_count=1, interval_calculator=BackoffRetryIntervalCalculator( backoff_factor=0.5, jitter=RandomJitter(), ), )],) ``` For asyncio apps, `Async` prefixed corresponding modules are available. All the methods in those modules are async/await compatible. Check [the source code](https://github.com/slackapi/python-slack-sdk/blob/main/slack_sdk/http_retry/async_handler.py) for more details. --- Source: https://docs.slack.dev/tools/python-slack-sdk/socket-mode # Socket Mode client Socket Mode is a method of connecting your app to the Slack APIs using WebSockets instead of HTTP. You can use `slack_sdk.socket_mode.SocketModeClient` for managing [Socket Mode](/apis/events-api/using-socket-mode) connections and performing interactions with Slack. ## Using Socket Mode {#socket-mode} Let's start with enabling Socket Mode. Visit [app page](http://api.slack.com/apps), choose the app you're working on, and go to **Settings** on the left pane. There are a few things to do on this page. * Go to **Settings** > **Basic Information**, then add a new **App-Level Token** with the `connections:write` scope. * Go to **Settings** > **Socket Mode**, then toggle on **Enable Socket Mode**. * Go to **Features** > **App Home**, look under **Show Tabs** > **Messages Tab**, then toggle on **Allow users to send Slash commands and messages from the messages tab**. * Go to **Features** > **Event Subscriptions**, then toggle on **Enable Events**. * On the same page, expand **Subscribe to bot events**, click **Add Bot User Event**, and select **message.im**. This will allow the bot to get events for messages that are sent in 1:1 direct messages with itself. * Go to **Features** > **Interactivity and Shortcuts**, look under _Shortcuts_\*, click **Create a New Shortcut**, then create a new Global shortcut with the following details: > **Name**: Hello > **Short Description**: Receive a Greeting > **Callback ID**: hello-shortcut * Go to **Features** > **OAuth & Permissions** under **Scopes** > **Bot Token Scopes**, click **Add an OAuth Scope**, and select **reactions:write**. This will allow the bot to add emoji reactions (Reacjis) to messages. * Go to **Features** > **Oauth & Permissions** under **OAuth Tokens for Your Workspace** and click **Install to Workspace**. You will be using the app-level token that starts with `xapp-`. Note that the token here is not one that starts with either `xoxb-` or `xoxp-`. ``` import osfrom slack_sdk.web import WebClientfrom slack_sdk.socket_mode import SocketModeClient# Initialize SocketModeClient with an app-level token + WebClientclient = SocketModeClient( # This app-level token will be used only for establishing a connection app_token=os.environ.get("SLACK_APP_TOKEN"), # xapp-A111-222-xyz # You will be using this WebClient for performing Web API calls in listeners web_client=WebClient(token=os.environ.get("SLACK_BOT_TOKEN")) # xoxb-111-222-xyz)from slack_sdk.socket_mode.response import SocketModeResponsefrom slack_sdk.socket_mode.request import SocketModeRequestdef process(client: SocketModeClient, req: SocketModeRequest): if req.type == "events_api": # Acknowledge the request anyway response = SocketModeResponse(envelope_id=req.envelope_id) client.send_socket_mode_response(response) # Add a reaction to the message if it's a new message if req.payload["event"]["type"] == "message" \ and req.payload["event"].get("subtype") is None: client.web_client.reactions_add( name="eyes", channel=req.payload["event"]["channel"], timestamp=req.payload["event"]["ts"], ) if req.type == "interactive" \ and req.payload.get("type") == "shortcut": if req.payload["callback_id"] == "hello-shortcut": # Acknowledge the request response = SocketModeResponse(envelope_id=req.envelope_id) client.send_socket_mode_response(response) # Open a welcome modal client.web_client.views_open( trigger_id=req.payload["trigger_id"], view={ "type": "modal", "callback_id": "hello-modal", "title": { "type": "plain_text", "text": "Greetings!" }, "submit": { "type": "plain_text", "text": "Good Bye" }, "blocks": [ { "type": "section", "text": { "type": "mrkdwn", "text": "Hello!" } } ] } ) if req.type == "interactive" \ and req.payload.get("type") == "view_submission": if req.payload["view"]["callback_id"] == "hello-modal": # Acknowledge the request and close the modal response = SocketModeResponse(envelope_id=req.envelope_id) client.send_socket_mode_response(response)# Add a new listener to receive messages from Slack# You can add more listeners like thisclient.socket_mode_request_listeners.append(process)# Establish a WebSocket connection to the Socket Mode serversclient.connect()# Just not to stop this processfrom threading import EventEvent().wait() ``` * * * ## Supported libraries {#supported-libraries} This SDK offers its own WebSocket client covering only required features for Socket Mode. In addition, `SocketModeClient` is implemented with a few 3rd party open source libraries. If you prefer any of the following, you can use it over the built-in one. PyPI Project SocketModeClient [`slack_sdk`](https://pypi.org/project/slack-sdk/) [`slack_sdk.socket_mode.SocketModeClient`](https://github.com/slackapi/python-slack-sdk/blob/main/slack_sdk/socket_mode/builtin) [`websocket_client`](https://pypi.org/project/websocket_client/) [`slack_sdk.socket_mode.websocket_client.SocketModeClient`](https://github.com/slackapi/python-slack-sdk/blob/main/slack_sdk/socket_mode/websocket_client) [`aiohttp`](https://pypi.org/project/aiohttp/) (asyncio-based) [`slack_sdk.socket_mode.aiohttp.SocketModeClient`](https://github.com/slackapi/python-slack-sdk/blob/main/slack_sdk/socket_mode/aiohttp) [`websockets`](https://pypi.org/project/websockets/) (asyncio-based) [`slack_sdk.socket_mode.websockets.SocketModeClient`](https://github.com/slackapi/python-slack-sdk/blob/main/slack_sdk/socket_mode/websockets) To use the [`websocket_client`](https://pypi.org/project/websocket_client/) based-one, add the[`websocket_client`](https://pypi.org/project/websocket_client/) dependency and change the import as below. ``` # Note that the package is differentfrom slack_sdk.socket_mode.websocket_client import SocketModeClientclient = SocketModeClient( app_token=os.environ.get("SLACK_APP_TOKEN"), # xapp-A111-222-xyz web_client=WebClient(token=os.environ.get("SLACK_BOT_TOKEN")) # xoxb-111-222-xyz) ``` You can pass a few additional arguments that are specific to the library. Apart from that, all the functionalities work in the same way as the built-in version. * * * ## Asyncio-based libraries {#asyncio-libraries} To use the asyncio-based ones such as aiohttp, your app needs to be compatible with asyncio's async/await programming model. The `SocketModeClient` only works with `AsyncWebClient` and async listeners. ``` import asyncioimport osfrom slack_sdk.web.async_client import AsyncWebClientfrom slack_sdk.socket_mode.aiohttp import SocketModeClient# Use async methodasync def main(): from slack_sdk.socket_mode.response import SocketModeResponse from slack_sdk.socket_mode.request import SocketModeRequest # Initialize SocketModeClient with an app-level token + AsyncWebClient client = SocketModeClient( # This app-level token will be used only for establishing a connection app_token=os.environ.get("SLACK_APP_TOKEN"), # xapp-A111-222-xyz # You will be using this AsyncWebClient for performing Web API calls in listeners web_client=AsyncWebClient(token=os.environ.get("SLACK_BOT_TOKEN")) # xoxb-111-222-xyz ) # Use async method async def process(client: SocketModeClient, req: SocketModeRequest): if req.type == "events_api": # Acknowledge the request anyway response = SocketModeResponse(envelope_id=req.envelope_id) # Don't forget having await for method calls await client.send_socket_mode_response(response) # Add a reaction to the message if it's a new message if req.payload["event"]["type"] == "message" \ and req.payload["event"].get("subtype") is None: await client.web_client.reactions_add( name="eyes", channel=req.payload["event"]["channel"], timestamp=req.payload["event"]["ts"], ) if req.type == "interactive" \ and req.payload.get("type") == "shortcut": if req.payload["callback_id"] == "hello-shortcut": # Acknowledge the request response = SocketModeResponse(envelope_id=req.envelope_id) await client.send_socket_mode_response(response) # Open a welcome modal await client.web_client.views_open( trigger_id=req.payload["trigger_id"], view={ "type": "modal", "callback_id": "hello-modal", "title": { "type": "plain_text", "text": "Greetings!" }, "submit": { "type": "plain_text", "text": "Good Bye" }, "blocks": [ { "type": "section", "text": { "type": "mrkdwn", "text": "Hello!" } } ] } ) if req.type == "interactive" \ and req.payload.get("type") == "view_submission": if req.payload["view"]["callback_id"] == "hello-modal": # Acknowledge the request and close the modal response = SocketModeResponse(envelope_id=req.envelope_id) await client.send_socket_mode_response(response) # Add a new listener to receive messages from Slack # You can add more listeners like this client.socket_mode_request_listeners.append(process) # Establish a WebSocket connection to the Socket Mode servers await client.connect() # Just not to stop this process await asyncio.sleep(float("inf"))# You can go with other way to run it. This is just for easiness to try it out.asyncio.run(main()) ``` --- Source: https://docs.slack.dev/tools/python-slack-sdk/tutorial/understanding-oauth-scopes # Understanding OAuth scopes for bots In this tutorial, we'll: * explore Slack app permissions and distribution using OAuth, and along the way, learn how to identify which scopes apps need and how to use OAuth to request them. * build an app that sends a direct message to users joining a specific channel. Once installed in a workspace, it will create a new channel named **#the-welcome-channel** if it doesn’t already exist. The channel will be used to thank users for joining the channel. We'll also share code snippets from the app, but the full source code is available on [GitHub](https://github.com/stevengill/slack-python-oauth-example). The code and implementation of OAuth is general enough that you should be able to follow along, even if Python isn't your preferred language. ## Prerequisites {#prerequisites} Before we get started, ensure you have a development workspace with permissions to install apps. If you don’t have one set up, go ahead and [create one](https://slack.com/create). You also need to [create a new app](https://api.slack.com/apps/new) if you haven’t already. Let’s get started! ## Determining scopes {#determine-scopes} Scopes are used to grant your app permission to perform functions in Slack, such as calling Web API methods and receiving Events API events. As a user goes through your app's installation flow, they'll need to permit access to the scopes your app is requesting. To determine which scopes we need, we should take a closer look at what our app does. Instead of scouring the entire list of scopes that might make sense, we can look at what events or methods we need for the app, and build out our scope list as we go. 1. After installation, our app checks to see if a channel exists (private or public since we can’t create a new channel with the same name). A quick search through the list of methods leads us to the `conversations.list` API method, which we can use to get the names of public & private channels. It also shows us what scopes are needed to use this method. In our case, we need `channels:read` and `groups:read`. (We don’t need `im:read` or `mpim:read`, as we aren’t concerned about the names of direct messages.) ``` import osfrom slack_sdk import WebClient# verifies if "the-welcome-channel" already existsdef channel_exists(): token = os.environ["SLACK_BOT_TOKEN"] client = WebClient(token=token) # grab a list of all the channels in a workspace clist = client.conversations_list() exists = False for k in clist["channels"]: # look for the channel in the list of existing channels if k['name'] == 'the-welcome-channel': exists = True break if exists == False: # create the channel since it doesn't exist create_channel() ``` 2. If the channel doesn’t already exist, we need to create it. Looking through the list of API methods leads us to the `conversations.create` API method, which needs the scope `channels:manage`. ``` # creates a channel named "the-welcome-channel"def create_channel(): token = os.environ["SLACK_BOT_TOKEN"] client = WebClient(token=token) resp = client.conversations_create(name="the-welcome-channel") ``` 3. When a user joins our newly created channel, our app sends them a direct message. To see when a user joins our channel, we need to listen for an event. Looking at our list of events, we see that `member_joined_channel` is the event that we need (_Note: events need to be added to your app’s configuration_). The scopes required for this event are `channels:read` and `groups:read` (same ones from step one). Now to send a direct message, we need to use the `chat.postMessage` API method, which requires the `chat:write` scope. ``` # Create an event listener for "member_joined_channel" events# Sends a DM to the user who joined the channel@slack_events_adapter.on("member_joined_channel")def member_joined_channel(event_data): user = event_data['event']['user'] token = os.environ["SLACK_BOT_TOKEN"] client = WebClient(token=token) msg = 'Welcome! Thanks for joining the-welcome-channel' client.chat_postMessage(channel=user, text=msg) ``` Our final list of scopes required are: * `channels:read` * `groups:read` * `channels:manage` * `chat:write` ## Setting up OAuth and requesting scopes {#setup} If you want users to be able to install your app on additional workspaces or from the [Slack Marketplace](/slack-marketplace/slack-marketplace-review-guide), you'll need to implement an OAuth flow. We'll be following the general flow of OAuth with Slack, which is covered in the [installing with OAuth](/authentication/installing-with-oauth) guide and nicely illustrated in the image below: ![OAuth flow](/assets/images/understanding-oauth-flow-cdb98a43894f64d3e01a05f8dc43796e.png) 1. **Requesting Scopes** This first step is sometimes also referred to as "redirect to Slack" or "Add to Slack button". In this step, we redirect to Slack and pass along our list of required scopes, client ID, and state as query parameters in the URL. You can get the client ID from the **Basic Information** section of your app. State is an optional value, but is recommended to prevent [CSRF attacks](https://en.wikipedia.org/wiki/Cross-site_request_forgery). ``` https://slack.com/oauth/v2/authorize?scope=channels:read,groups:read,channels:manage,chat:write&client_id=YOUR_CLIENT_ID&state=STATE_STRING ``` _It is also possible to pass in a `redirect\_uri` into your URL. A `redirect\_uri` is used for Slack to know where to send the request after the user has granted permission to your app. In our example, instead of passing one in the URL, we request that you add a Redirect URL in your app’s configuration on [api.slack.com/apps](https://api.slack.com/apps) under the **OAuth and Permissions** section._ Next, we'll create a route in our app that contains an **Add to Slack** button using that URL above. ``` # Grab client ID from your environment variablesclient_id = os.environ["SLACK_CLIENT_ID"]# Generate random string to use as state to prevent CSRF attacksfrom uuid import uuid4state = str(uuid4())# Route to kick off Oauth flow@app.route("/begin_auth", methods=["GET"])def pre_install(): return f'' ``` Now when a user navigates to the route, they should see the **Add to Slack** button. Clicking the button will trigger the next step. 2. **Waiting for user approval** The user will see the app installation UI (shown below) and will have the option to accept the permissions and allow the app to install to the workspace: ![Approve installation](/assets/images/understanding-oauth-approve-4872b886af3c38a5aedd5609456fb0ef.png) 3. **Exchanging a temporary authorization code for an access token** After the user approves the app, Slack will redirect the user to your specified Redirect URL. As we mentioned earlier, we did not include a `redirect_uri` in our **Add to Slack** button, so our app will use our Redirect URL specified on the app’s **OAuth and Permissions** page. Our Redirect URL function will have to parse the HTTP request for the `code` and `state` query parameters. We need to check that the `state` parameter was created by our app. If it is, we can now exchange the `code` for an access token. To do this, we need to call the `oauth.v2.access` API method with the `code`, `client_id`, and `client_secret`. This method will return the access token, which we can now save (preferably in a persistent database) and use for any of the Slack API method calls we make. (_Note: use this access token for all of the Slack API method calls we covered in the scopes section above_) ``` # Grab client Secret from your environment variablesclient_secret = os.environ["SLACK_CLIENT_SECRET"]# Route for Oauth flow to redirect to after user accepts scopes@app.route("/finish_auth", methods=["GET", "POST"])def post_install(): # Retrieve the auth code and state from the request params auth_code = request.args['code'] received_state = request.args['state']# This request doesn't need a token so an empty string will sufficeclient = WebClient(token="")# verify state received in params matches state we originally sent in auth requestif received_state == state: # Request the auth tokens from Slack response = client.oauth_v2_access( client_id=client_id, client_secret=client_secret, code=auth_code )else: return "Invalid State"# Save the bot token to an environmental variable or to your data storeos.environ["SLACK_BOT_TOKEN"] = response['access_token']# See if "the-welcome-channel" exists. Create it if it doesn't.channel_exists()# Don't forget to let the user know that auth has succeeded!return "Auth complete!" ``` ## Next steps {#next} At this point, you should feel more comfortable learning what scopes your app needs and using OAuth to request those scopes. A few resources you can check out next include: * [Slack-Python-OAuth-Example](https://github.com/stevengill/slack-python-oauth-example): we used code snippets from this app in this tutorial. The README contains more detailed information about running the app locally using ngrok, setting up a Redirect URL for OAuth, and setting up a request URL for events. * Learn more about [installing with OAuth](/authentication/installing-with-oauth). --- Source: https://docs.slack.dev/tools/python-slack-sdk/tutorial/uploading-files # Uploading files with Python This tutorial details how to use the [`slack-sdk` package for Python](https://pypi.org/project/slack-sdk/) to upload files to a channel in Slack with some code samples. In addition to looking at how to upload files, we'll also cover listing and deleting files via the Web API using the Python SDK. ## Creating an app {#create-app} First, create a [Slack app](https://api.slack.com/apps/new). ## Configuring your app's settings with an app manifest {#configuration} Creating your app using this method will include all the required settings for this tutorial, and you won't be bogged down with too many details - all you'll need to do is decide where this app will live. If you're curious about the inner workings of how this button works, refer to [App Manifests](/app-manifests) for more information. ``` _metadata: major_version: 1 minor_version: 1display_information: name: File Writer Appfeatures: bot_user: display_name: File Writer Botoauth_config: scopes: bot: # Used to send messages to a channel - chat:write # This scope is not required if your app will just upload files. We've included it in order to use the `files.info` `files.list` methods. - files:read # Used to upload files to Slack - files:write ``` ## Installing your app in a workspace {#installing} Once you've created your app, you'll see an **Install to Workspace** button. Click it to install your app in your workspace. ![Install to workspace](/assets/images/upload-files-install-5a1c4abb273b564d6f9977809d68ff33.png) Next, click **Allow** to authorize the app in your workspace. ![Authorize app](/assets/images/upload-files-allow-737b1ea19099e4207dfc741eec2220eb.png) Navigate to the **Install App** section under **Settings**. Here, you'll find your `Bot User OAuth Token`. ![Get token](/assets/images/upload-files-bot-token-95db87b20977d7d0522c922deed70493.png) Set this token value as an environment variable called `SLACK_BOT_TOKEN` by using the following command: ``` export SLACK_BOT_TOKEN= ``` With this, all your Slack app configuration is done. Let's start coding. ## Using Python to upload a file {#upload-file-with-python} ### Creating a new project {#create-new-project} First, ensure you're using Python version 3.7 or above. While the current standard is for the `python3` and `pip3` commands to use Python 3.7 or above, it's best to ensure your runtime is always using the latest version of Python. [pyenv](https://github.com/pyenv/pyenv) is a handy tool that can do this for you. We'll create a brand new virtual environment and install the required library dependencies using the following commands. ``` echo 'slack-sdk>=3.12,<4' > requirements.txtpython3 -m venv .venvsource .venv/bin/activatepip install -U pippip install -r requirements.txt ``` ### Uploading a file {#upload-file} While it's possible to enter the following into the Python shell, we've gathered some code samples and wrote it in script form. For each of the code samples, make sure to add in the following to the top of your Python file if you're going to run it as a script - the examples won't run without it. ``` import logging, os# Sets the debug level. # If you're using this in production, you can change this back to INFO and add extra log entries as needed.logging.basicConfig(level=logging.DEBUG)# Initialize the Web API client.# This expects that you've already set your SLACK_BOT_TOKEN as an environment variable.# Try to resist the urge to put your token directly in your code; it is best practice not to.from slack_sdk import WebClientclient = WebClient(os.environ["SLACK_BOT_TOKEN"]) ``` Let's make sure our token is correctly configured. ``` # Tests to see if the token is validauth_test = client.auth_test()bot_user_id = auth_test["user_id"]print(f"App's bot user: {bot_user_id}") ``` Once you run this code, you'll see something similar to the following within the logs: ``` >>> auth_test = client.auth_test()DEBUG:slack_sdk.web.base_client:Sending a request - url: https://www.slack.com/api/auth.test, query_params: {}, body_params: {}, files: {}, json_body: None, headers: {}DEBUG:slack_sdk.web.base_client:Received the following response - status: 200, headers: {}, body: {"ok":true,"url":"https:\/\/example.slack.com\/","team":"Acme Corp","user":"file_writer_bot","team_id":"T1234567890","user_id":"U02PY3HA48G","bot_id":"B02P8CPE143","is_enterprise_install":false}>>> bot_user_id = auth_test["user_id"]>>> print(f"App's bot user: {bot_user_id}")App's bot user: U02PY3HA48G ``` Notice that your bot user's `user_id` can be found within these logs. Any files uploaded using a bot token will be associated with the bot user. At this point, while no files have been uploaded yet, you can call the `files.list` API method to confirm this fact. We'll do this again after we've uploaded some files to see what has changed. ``` >>> files = client.files_list(user=bot_user_id)DEBUG:slack_sdk.web.base_client:Sending a request - url: https://slack.com/api/files.list, query_params: {}, body_params: {'user': 'U02PY3HA48G'}, files: {}, json_body: None, headers: {}DEBUG:slack_sdk.web.base_client:Received the following response - status: 200, headers: {}, body: {"ok":true,"files":[],"paging":{"count":100,"total":0,"page":1,"pages":0}} ``` Let's try uploading a file using text supplied to the `content` parameter. This will upload a text file with the specified `content`. ``` new_file = client.files_upload_v2( title="My Test Text File", filename="test.txt", content="Hi there! This is a text file!",) ``` Doing this within the Python shell will display the following: ``` >>> new_file = client.files_upload_v2(... title="My Test Text File",... filename="test.txt",... content="Hi there! This is a text file!",... )DEBUG:slack_sdk.web.base_client:Sending a request - url: https://www.slack.com/api/files.getUploadURLExternal, query_params: {}, body_params: {'filename': 'test.txt', 'length': 30}, files: {}, json_body: None, headers: {'Content-Type': 'application/x-www-form-urlencoded', 'Authorization': '(redacted)', 'User-Agent': 'Python/3.11.6 slackclient/3.27.1 Darwin/23.3.0'}DEBUG:slack_sdk.web.base_client:Received the following response - status: 200, headers: {'date': 'Fri, 08 Mar 2024 08:27:40 GMT', 'server': 'Apache', 'vary': 'Accept-Encoding', 'x-slack-req-id': '9rj4io2i10iawjdasdfas', 'x-content-type-options': 'nosniff', 'x-xss-protection': '0', 'pragma': 'no-cache', 'cache-control': 'private, no-cache, no-store, must-revalidate', 'expires': 'Sat, 26 Jul 1997 05:00:00 GMT', 'content-type': 'application/json; charset=utf-8', 'x-accepted-oauth-scopes': 'files:write', 'x-oauth-scopes': 'chat:write,files:read,files:write', 'access-control-expose-headers': 'x-slack-req-id, retry-after', 'access-control-allow-headers': 'slack-route, x-slack-version-ts, x-b3-traceid, x-b3-spanid, x-b3-parentspanid, x-b3-sampled, x-b3-flags', 'strict-transport-security': 'max-age=31536000; includeSubDomains; preload', 'referrer-policy': 'no-referrer', 'x-slack-unique-id': 'ZerL-asd3k201a0sdfa', 'x-slack-backend': 'r', 'access-control-allow-origin': '*', 'content-length': '257', 'via': '1.1 slack-prod.tinyspeck.com, envoy-www-iad-upnvxyvr, envoy-edge-nrt-ixozsome', 'x-envoy-attempt-count': '1', 'x-envoy-upstream-service-time': '195', 'x-backend': 'main_normal main_canary_with_overflow main_control_with_overflow', 'x-server': 'slack-www-hhvm-main-iad-bgpy', 'x-slack-shared-secret-outcome': 'no-match', 'x-edge-backend': 'envoy-www', 'x-slack-edge-shared-secret-outcome': 'no-match', 'connection': 'close'}, body: {"ok":true,"upload_url":"https:\/\/files.slack.com\/upload\/v1\/CwABAASWWgoAAZOR9CgFYdQZCgACF7q8rQ4fIhASAAAVDFERDNKSDNLCwACAAAAC1UwNk5GNjdGNUxNCwADAAAAC0YwNk40VDdGWk5LAAoABAAAAAAAAAAeAAsAAgAAABRmH2dkKc07lhAASAWWpZAA","file_id":"F2910294A"}DEBUG:slack_sdk.web.base_client:('Received the following response - ', "status: 200, headers: {'Content-Type': 'text/plain; charset=utf-8', 'Content-Length': '7', 'Connection': 'close', 'x-backend': 'miata-prod-nrt-v2-5976497578-js557', 'date': 'Fri, 08 Mar 2024 08:27:40 GMT', 'x-envoy-upstream-service-time': '401', 'x-edge-backend': 'miata', 'x-slack-edge-shared-secret-outcome': 'shared-secret', 'server': 'envoy', 'via': 'envoy-edge-nwt-aaoskwwo, 1.1 f752a4d41a2512ine9asfa.cloudfront.net (CloudFront)', 'X-Cache': 'Miss from cloudfront', 'X-Amz-Cf-Pop': 'NRT51-C4', 'X-Amz-Cf-Id': 'jxcP2ao0fs4KXanisi9aiswiaKBiJQ==', 'Cross-Origin-Resource-Policy': 'cross-origin'}, body: OK - 30")DEBUG:slack_sdk.web.base_client:Sending a request - url: https://www.slack.com/api/files.completeUploadExternal, query_params: {}, body_params: {'files': '[{"id": "F2910294A", "title": "My Test Text File"}]'}, files: {}, json_body: None, headers: {'Content-Type': 'application/x-www-form-urlencoded', 'Authorization': '(redacted)', 'User-Agent': 'Python/3.11.6 slackclient/3.27.1 Darwin/23.3.0'}DEBUG:slack_sdk.web.base_client:Received the following response - status: 200, headers: {'date': 'Fri, 08 Mar 2024 08:27:41 GMT', ... body: {"ok":true,"files":[{"id":"F2910294A","created":1709886459,"timestamp":1709886459,"name":"test.txt","title":"My Test Text File","mimetype":"text\/plain","filetype":"text","pretty_type":"Plain Text","user":"U10AWOAW","user_team":"T12391022","editable":true,"size":30,"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\/T12391022-F2910294A\/test.txt","url_private_download":"https:\/\/files.slack.com\/files-pri\/T12391022-F2910294A\/download\/test.txt","permalink":"https:\/\/platform-ce.slack.com\/files\/U10AWOAW\/F2910294A\/test.txt","permalink_public":"https:\/\/slack-files.com\/T12391022-F2910294A-100e14d15f","edit_link":"https:\/\/platform-ce.slack.com\/files\/U10AWOAW\/F2910294A\/test.txt\/edit","preview":"Hi there! This is a text file!","preview_highlight":"
\n
\n
Hi there! This is a text file!<\/pre><\/div>\n<\/div>\n<\/div>\n","lines":1,"lines_more":0,"preview_is_truncated":false,"comments_count":0,"is_starred":false,"shares":{},"channels":[],"groups":[],"ims":[],"has_more_shares":false,"has_rich_preview":false,"file_access":"visible"}]}
```

We can confirm that a file has been uploaded using the `files.list` API method mentioned earlier. Wait a moment before calling this method, as there may be a bit of a lag before files are reflected within the results.

```
>>> files = client.files_list(user=bot_user_id)DEBUG:slack_sdk.web.base_client:Sending a request - url: https://www.slack.com/api/files.list, query_params: {}, body_params: {'user': 'U02PY3HA48G'}, files: {}, json_body: None, headers: {}DEBUG:slack_sdk.web.base_client:Received the following response - status: 200, headers: {}, body: {"ok":true,"files":[{"id":"F02P5J88137","created":1638414790,"timestamp":1638414790,"name":"test.txt","title":"My Test Text File","mimetype":"text\/plain","filetype":"text","pretty_type":"Plain Text","user":"U02PY3HA48G","editable":true,"size":30,"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\/T03E94MJU-F02P5J88137\/test.txt","url_private_download":"https:\/\/files.slack.com\/files-pri\/T03E94MJU-F02P5J88137\/download\/test.txt","permalink":"https:\/\/seratch.slack.com\/files\/U02PY3HA48G\/F02P5J88137\/test.txt","permalink_public":"https:\/\/slack-files.com\/T03E94MJU-F02P5J88137-e3fda671e9","edit_link":"https:\/\/seratch.slack.com\/files\/U02PY3HA48G\/F02P5J88137\/test.txt\/edit","preview":"Hi there! This is a text file!","preview_highlight":"
\n
\n
Hi there! This is a text file!<\/pre><\/div>\n<\/div>\n<\/div>\n","lines":1,"lines_more":0,"preview_is_truncated":false,"channels":[],"groups":[],"ims":[],"comments_count":0}],"paging":{"count":100,"total":1,"page":1,"pages":1}}
```

Back in Slack, you'll notice that nothing has happened. How curious...

### Sharing a file within a channel {#sharing}

At this point, we have indeed uploaded a file to Slack, but only the bot user is able to view it.

Let's share this file with other users within our workspace. To do so, we'll use the `chat.postMessage` API method to post a message.

In this example, we've used the channel name `C123456789`, but you'll need to find the ID of the channel that you want to share your file to. When you're in your channel of choice, you can find the channel ID by clicking on the channel name at the top and then scrolling to the bottom of the screen that shows up.

Just like in the image below, mention the File Writer Bot and invite it to the `#random` channel.

![Invite to channel](/assets/images/upload-files-invite-bot-be913d35009e8b8b64cd65b2b510fc2b.gif)

Next, use the following code to retrieve the file's permalink and post it within a channel.

```
file_url = new_file.get("file").get("permalink")new_message = client.chat_postMessage(    channel="C123456789",    text=f"Here is the file: {file_url}",)
```

By doing this, you'll be able to see the file within Slack.

![Upload file](/assets/images/upload-files-first-upload-19c0648e36c9a0e990dff20ce72ea972.png)

### Specifying a channel when uploading a file {#specifying-channel}

While this exercise was very informative, having to do these two steps every time we upload a file is a bit cumbersome. Instead, we can specify the `channel` parameter to the function. This is the more common way of uploading a file from an app.

```
upload_and_then_share_file = client.files_upload_v2(    channel="C123456789",    title="Test text data",    filename="test.txt",    content="Hi there! This is a text file!",    initial_comment="Here is the file:",)
```

By running the above code, you'll share the same file without having to send a message with the file URL.

![Share file with message](/assets/images/upload-files-with-channel-db8e956b90e64b090046b46294435eca.png)

### Uploading local files {#upload-local-files}

If you have an image file lying around, feel free to use that but for simplicity's sake. We'll continue using a text file here. You can create a local file by using the following command:

```
echo 'Hi there! This is a text file!' > test.txt
```

Next, within the same directory, execute the following code. We'll specify the file path as the `file` parameter.

```
upload_text_file = client.files_upload_v2(    channel="C123456789",    title="Test text data",    file="./test.txt",    initial_comment="Here is the file:",)
```

Again, we'll see that the file has been uploaded to Slack and shared within the `#random` channel.

![File uploaded](/assets/images/upload-files-local-file-fe811b82b0991cabbc81d9cb00cebfea.png)

## Deleting a file {#deleting}

Next, we'll cover how to delete a file.

We've just uploaded 3 different files above (even though they may look the same). We can confirm that again using the `files.list` method.

```
file_ids = []# The Python SDK will automatically paginate for you within a for-loop.for page in client.files_list(user=bot_user_id):    for file in page.get("files", []):        file_ids.append(file["id"])print(file_ids)
```

```
>>> file_ids['F02P5J88137', 'F02P8H5BN9G', 'F02P5K7T8SZ']
```

Let's remove these files from our Slack workspace.

```
for page in client.files_list(user=bot_user_id):    for file in page.get("files", []):        client.files_delete(file=file["id"])
```

Once we run this, the `files` array should be empty. The count for files found within the `paging` object may take a moment to reflect the actual number of files. You'll also notice within Slack that there are several `This file was deleted.` messages being shown.

![Delete a file](/assets/images/upload-files-delete-ea790fcbbd1873d7141ff40d7a6e94ce.png)

## Next steps {#next}

This tutorial summarized how to use the Slack API to upload files and share them within a channel, using the Python SDK. The same principles apply to other languages as well, so if Python isn't your fancy, feel free to try out our other SDKs:

*   [Java Slack SDK](/tools/java-slack-sdk/)
*   [Node Slack SDK](/tools/node-slack-sdk/)
*   [Deno Slack SDK](/tools/deno-slack-sdk/)

---

Source: https://docs.slack.dev/tools/python-slack-sdk/v3-migration

# Migrating from v2.x to v3.x

You may still view the legacy `slackclient` v2 [documentation](/tools/python-slack-sdk/legacy/). However, the **slackclient** project is in maintenance mode and this **slack\_sdk** project is the successor.

## From slackclient 2.x {#fromv2}

There are a few changes introduced in v3.0:

*   The PyPI project has been renamed from `slackclient` to `slack_sdk`.
*   Importing `slack_sdk.*` is recommended. You can still use `slack.*` with deprecation warnings.
*   `slack_sdk` has no required dependencies. This means `aiohttp` is no longer automatically resolved.
*   `WebClient` no longer has `run_async` and `aiohttp` specific options. If you still need the option or other `aiohttp` specific options, use `LegacyWebClient` (`slackclient` v2 compatible) or `AsyncWebClient`.

We're sorry for the inconvenience.

* * *

**Change:** The PyPI project has been renamed from `slackclient` to `slack_sdk`.

**Action**: Remove `slackclient`, add `slack_sdk` in `requirements.txt`.

Since v3, the PyPI project name is [slack\_sdk](https://pypi.org/project/slack_sdk/) (technically `slack-sdk` also works).

The biggest reason for the renaming is the feature coverage in v3 and newer. The SDK v3 provides not only API clients, but also other modules. As the first step, it starts supporting OAuth flow out-of-the-box. The secondary reason is to make the names more consistent. The renaming addresses the long-lived confusion between the PyPI project and package names.

* * *

**Change:** Importing `slack_sdk.*` is recommended. You can still use `slack.*` with deprecation warnings.

**Action**: Replace `from slack import`, `import slack`, etc. in your source code.

Most imports can be simply replaced by `find your_app -name '*.py' | xargs sed -i '' 's/from slack /from slack_sdk /g'` or similar. If you use `slack.web.classes.*`, the conversion is not so simple that we recommend manually replacing imports for those.

That said, all existing code can be migrated to v3 without any code changes. If you don't have time for it, you can use the `slack` package with deprecation warnings saying `UserWarning: slack package is deprecated. Please use slack_sdk.web/webhook/rtm package instead. For more info, go to https://docs.slack.dev/tools/python-slack-sdk/v3-migration/` for a while.

* * *

**Change:** `slack_sdk` has no required dependencies. This means `aiohttp` is no longer automatically resolved.

**Action**: Add `aiohttp` to `requirements.txt` if you use any of `AsyncWebClient`, `AsyncWebhookClient`, and `LegacyWebClient`.

If you use some modules that require `aiohttp`, your `requirements.txt` needs to explicitly list `aiohttp`. The `slack_sdk` dependency doesn't resolve it for you, unlike `slackclient` v2.

* * *

**Change:** `WebClient` no longer has `run_async` and `aiohttp` specific options.

**Action:** If you still need the option or other `aiohttp` specific options, use `LegacyWebClient` (`slackclient` v2 compatible) or `AsyncWebClient`.

The new `slack_sdk.web.WebClient` doesn't rely on `aiohttp` internally at all. The class provides only the synchronous way to call Web APIs. If you need a v2 compatible one, you can use `LegacyWebClient`. Apart from the name, there is no breaking change in the class.

If you're using `run_async=True` option, we highly recommend switching to `AsyncWebClient`. `AsyncWebClient` is a straightforward async HTTP client. You can expect the class properly works in the nature of `async/await` provided by the standard `asyncio` library.

* * *

## Migration from v1.x to v2.x {#fromv1}

If you're migrating from v1.x of `slackclient` to v2.x, here's what you need to change to ensure your app continues working after updating.

We have completely rewritten this library and you should only upgrade once you have fully tested it in your development environment.

If you don't wish to upgrade yet, be sure to pin your module for the Python `slackclient` to `1.3.1`.

### Minimum Python versions {#minimum-versions}

`slackclient` v2.x requires Python 3.7 (or higher).

Client v1 support:

*   Python 2: Python 2.7 was supported in the 1.x version of the client up until Dec 31st, 2019.
*   We’ll continue to add support for any new Slack features that are released as they become available on the platform. Support for token rotation is an example of a Slack feature.
*   We will no longer be adding any new client-specific functionality to v1. Support for “asynchronous programming” is an example of a client feature. Another example is storing additional data on the client.
*   We are no longer addressing bug or security fixes.
*   Github branching: The `master` branch is used for v2 code. The `v1` branch is used for v1 code.

### Import changes {#import-changes}

The goal of this project is to provide a set of tools that ease the creation of Python Slack apps. To better align with this goal, we’re renaming the main module to `slack`. From `slack`, developers can import various tools.

```
# Before:# import slackclient# After:from slack import WebClient
```

### RTM API changes {#RTM-changes}

An RTMClient allows apps to communicate with the Slack platform's Legacy RTM API. This client allows you to link callbacks to their corresponding events. When an event occurs, this client executes your callback while passing along any information it receives.

Example app in v1:

Here's a simple example app that replies "Hi <@userid>!" in a thread if you send it a message containing "Hello".

```
from slackclient import SlackClientslack_token = os.environ["SLACK_API_TOKEN"]client = SlackClient(slack_token)def say_hello(data):    if 'Hello' in data['text']:        channel_id = data['channel']        thread_ts = data['ts']        user = data['user']        client.api_call('chat.postMessage',            channel=channel_id,            text="Hi <@{}>!".format(user),            thread_ts=thread_ts        )if client.rtm_connect():    while client.server.connected is True:        for data in client.rtm_read():            if "type" in data and data["type"] == "message":                say_hello(data)else:    print "Connection Failed"
```

Example App in v2:

Here's that same example app that replies "Hi <@userid>!" in a thread if you send it a message containing "Hello".

```
import slackslack_token = os.environ["SLACK_API_TOKEN"]rtmclient = slack.RTMClient(token=slack_token)@slack.RTMClient.run_on(event='message')def say_hello(**payload):    data = payload['data']    if 'Hello' in data['text']:        channel_id = data['channel']        thread_ts = data['ts']        user = data['user']        webclient = payload['web_client']        webclient.chat_postMessage(            channel=channel_id,            text="Hi <@{}>!".format(user),            thread_ts=thread_ts        )rtmclient.start()
```

**We no longer store any team data.** In the current 1.x version of the client, we store some channel and user information internally on [`Server.py`](https://github.com/slackapi/python-slackclient/blob/master/slackclient/server.py) in `client`. This data will now be available in the open event for consumption. Developers are then free to store any information they choose. Here's an example:

```
# Retrieving the team domain.# Before:# team_domain = client.server.login_data["team"]["domain"]# After:@slack.RTMClient.run_on(event='open')def get_team_data(**payload):    team_domain = payload['data']['team']['domain']
```

RTM usage has been completely redesigned.

For new projects, we recommend using [Events API](/apis/events-api). This package `slackclient` v2 doesn't have any support for Events API but you can try [https://github.com/slackapi/python-slack-events-api](https://github.com/slackapi/python-slack-events-api) that works as an enhancement of Flask web framework.

In the near future, we'll be providing better support for Events API in the official SDK.

### Web Client API changes {#web-client-changes}

**Token refresh removed**:

This feature originally shipped as a part of workspace tokens. Since we've [gone in a new direction](https://medium.com/slack-developer-blog/the-latest-with-app-tokens-fe878d44130c) it's safe to remove this along with any related attributes stored on the client.

*   refresh\_token
*   token\_update\_callback
*   client\_id
*   client\_secret

**`#api_call()`**:

*   `timeout` param has been removed. Timeout is passed at the client level now.
*   `kwargs` param has been removed. You must specify where the data you pass belongs in the request. e.g. 'data' vs 'params' vs 'files'...etc

```
# Before:# from slackclient import SlackClient## client = SlackClient(os.environ["SLACK_API_TOKEN"])# client.api_call('chat.postMessage',#     timeout=30,#     channel='C0123456',#     text="Hi!")# After:import slackclient = slack.WebClient(os.environ["SLACK_API_TOKEN"], timeout=30)client.api_call('chat.postMessage', json={    'channel': 'C0123456',    'text': 'Hi!'})# Note: That while the above is allowed, the more efficient way to call that API is like this:client.chat_postMessage(    channel='C0123456',    text='Hi!')
```

The WebClient provides built-in methods for the Slack Web API. These methods act as helpers, enabling you to focus less on how the request is constructed. Here are a few things this provides:

*   Basic information about each method through the docstring.
*   Easy file uploads: You can pass in the location of a file and the library will handle opening and retrieving the file object to be transmitted.
*   Token type validation: This gives you better error messaging when you're attempting to consume an API method your token doesn't have access to.
*   Constructs requests using Slack preferred HTTP methods and content-types.

### Error handling changes {#error-handling-changes}

In version 1.x, a failed API call would return the error payload to you and expect you to handle the error. In version 2.x, a failed API call will throw an exception. To handle this in your code, you will have to wrap API calls with a `try except` block.

---

Source: https://docs.slack.dev/tools/python-slack-sdk/web

# Web client

The Slack Web API allows you to build applications that interact with Slack in more complex ways than the integrations we provide out of the box.

Accessing Slack API methods requires an OAuth token — read more about [installing with OAuth](/authentication/installing-with-oauth).

Each of these [API methods](/reference/methods) is fully documented on our developer site at [docs.slack.dev](/).

## Sending a message {#sending-messages}

One of the primary uses of Slack is posting messages to a channel using the channel ID, or as a DM to another person using their user ID. This method will handle either a channel ID or a user ID passed to the `channel` parameter.

Your app's bot user needs to be in the channel (otherwise, you will get either `not_in_channel` or `channel_not_found` error code). If your app has the [chat:write.public](/reference/scopes/chat.write.public) scope, your app can post messages without joining a channel as long as the channel is public. See the [chat.postMessage](/reference/methods/chat.postMessage) API method for more info.

```
import logginglogging.basicConfig(level=logging.DEBUG)import osfrom slack_sdk import WebClientfrom slack_sdk.errors import SlackApiErrorslack_token = os.environ["SLACK_BOT_TOKEN"]client = WebClient(token=slack_token)try:    response = client.chat_postMessage(        channel="C0XXXXXX",        text="Hello from your app! :tada:"    )except SlackApiError as e:    # You will get a SlackApiError if "ok" is False    assert e.response["error"]    # str like 'invalid_auth', 'channel_not_found'
```

### Sending ephemeral messages {#sending-ephemeral-messages}

Sending an ephemeral message, which is only visible to an assigned user in a specified channel, is nearly the same as sending a regular message but with an additional `user` parameter.

```
import osfrom slack_sdk import WebClientslack_token = os.environ["SLACK_BOT_TOKEN"]client = WebClient(token=slack_token)response = client.chat_postEphemeral(    channel="C0XXXXXX",    text="Hello silently from your app! :tada:",    user="U0XXXXXXX")
```

See the [`chat.postEphemeral`](/reference/methods/chat.postEphemeral) API method for more details.

### Sending streaming messages {#sending-streaming-messages}

You can have your app's messages stream in to replicate conventional AI chatbot behavior. This is done through three Web API methods:

*   [`chat_startStream`](/reference/methods/chat.startStream)
*   [`chat_appendStream`](/reference/methods/chat.appendStream)
*   [`chat_stopStream`](/reference/methods/chat.stopStream)

You can streamline calling these methods by using the [`chat_stream()`](#chat_stream) helper.

#### Starting the message stream {#starting-stream}

First you need to begin the message stream:

```
# Example: Stream a response to any message@app.message()def handle_message(message, client):    channel_id = event.get("channel")    team_id = event.get("team")    thread_ts = event.get("thread_ts") or event.get("ts")    user_id = event.get("user")        # Start a new message stream    stream_response = client.chat_startStream(        channel=channel_id,        recipient_team_id=team_id,        recipient_user_id=user_id,        thread_ts=thread_ts,    )    stream_ts = stream_response["ts"]
```

#### Appending content to the message stream {#appending-stream}

With the stream started, you can then append text to it in chunks to convey a streaming effect.

The structure of the text coming in will depend on your source. The following code snippet uses OpenAI's response structure as an example:

```
# continued from above    for event in returned_message:        if event.type == "response.output_text.delta":            client.chat_appendStream(                channel=channel_id,                 ts=stream_ts,                 markdown_text=f"{event.delta}"            )        else:            continue
```

#### Stopping the message stream {#stopping-stream}

Your app can then end the stream with the `chat_stopStream` method:

```
# continued from above    client.chat_stopStream(        channel=channel_id,         ts=stream_ts    )
```

The method also provides you an opportunity to request user feedback on your app's responses using the [feedback buttons](/reference/block-kit/block-elements/feedback-buttons-element) block element within the [context actions](/reference/block-kit/blocks/context-actions-block) block. The user will be presented with thumbs up and thumbs down buttons which send an action to your app when pressed.

```
def create_feedback_block() -> List[Block]:    blocks: List[Block] = [        ContextActionsBlock(            elements=[                FeedbackButtonsElement(                    action_id="feedback",                    positive_button=FeedbackButtonObject(                        text="Good Response",                        accessibility_label="Submit positive feedback on this response",                        value="good-feedback",                    ),                    negative_button=FeedbackButtonObject(                        text="Bad Response",                        accessibility_label="Submit negative feedback on this response",                        value="bad-feedback",                    ),                )            ]        )    ]    return blocks@app.message()def handle_message(message, client):    # ... previous streaming code ...        # Stop the stream and add interactive elements    feedback_block = create_feedback_block()    client.chat_stopStream(        channel=channel_id,         ts=stream_ts,         blocks=feedback_block    )
```

See [Formatting messages with Block Kit](#block-kit) below for more details on using Block Kit with messages.

#### Using the chat_stream() helper {#chat_stream}

The Python Slack SDK provides a [`chat_stream()`](https://docs.slack.dev/tools/python-slack-sdk/reference/web/client.html#slack_sdk.web.client.WebClient.chat_stream) helper utility to streamline calling these methods. Here's an excerpt from our [Assistant template app](https://github.com/slack-samples/bolt-python-assistant-template):

```
streamer = client.chat_stream(    channel=channel_id,    recipient_team_id=team_id,    recipient_user_id=user_id,    thread_ts=thread_ts,)# Loop over OpenAI response stream# https://platform.openai.com/docs/api-reference/responses/createfor event in returned_message:    if event.type == "response.output_text.delta":        streamer.append(markdown_text=f"{event.delta}")    else:        continuefeedback_block = create_feedback_block()streamer.stop(blocks=feedback_block)
```

## Formatting messages with Block Kit {#block-kit}

Messages posted from apps can contain more than just text; they can also include full user interfaces composed of blocks using [Block Kit](/block-kit).

The [`chat.postMessage method`](/reference/methods/chat.postMessage) takes an optional blocks argument that allows you to customize the layout of a message. Blocks can be specified in a single array of either dict values or [slack\_sdk.models.blocks.Block](https://docs.slack.dev/tools/python-slack-sdk/reference/models/blocks/index.html) objects.

To send a message to a channel, use the channel's ID. For DMs, use the user's ID.

```
client.chat_postMessage(    channel="C0XXXXXX",    blocks=[        {            "type": "section",            "text": {                "type": "mrkdwn",                "text": "Danny Torrence left the following review for your property:"            }        },        {            "type": "section",            "text": {                "type": "mrkdwn",                "text": " \n :star: \n Doors had too many axe holes, guest in room " +                    "237 was far too rowdy, whole place felt stuck in the 1920s."            },            "accessory": {                "type": "image",                "image_url": "https://images.pexels.com/photos/750319/pexels-photo-750319.jpeg",                "alt_text": "Haunted hotel image"            }        },        {            "type": "section",            "fields": [                {                    "type": "mrkdwn",                    "text": "*Average Rating*\n1.0"                }            ]        }    ])
```

You can use [Block Kit Builder](https://app.slack.com/block-kit-builder/) to prototype your message's look and feel.

## Threading messages {#threading-messages}

Threaded messages are a way of grouping messages together to provide greater context. You can reply to a thread or start a new threaded conversation by simply passing the original message's `ts` ID in the `thread_ts` attribute when posting a message. If you're replying to a threaded message, you'll pass the `thread_ts` ID of the message you're replying to.

A channel or DM conversation is a nearly linear timeline of messages exchanged between people, bots, and apps. When one of these messages is replied to, it becomes the parent of a thread. By default, threaded replies do not appear directly in the channel, but are instead relegated to a kind of forked timeline descending from the parent message.

```
response = client.chat_postMessage(    channel="C0XXXXXX",    thread_ts="1476746830.000003",    text="Hello from your app! :tada:")
```

By default, the `reply_broadcast` parameter is set to `False`. To indicate your reply is germane to all members of a channel and therefore a notification of the reply should be posted in-channel, set the `reply_broadcast` parameter to `True`.

```
response = client.chat_postMessage(    channel="C0XXXXXX",    thread_ts="1476746830.000003",    text="Hello from your app! :tada:",    reply_broadcast=True)
```

While threaded messages may contain attachments and message buttons, when your reply is broadcast to the channel, it'll actually be a reference to your reply and not the reply itself.

When appearing in the channel, it won't contain any attachments or message buttons. Updates and deletion of threaded replies works the same as regular messages.

Refer to the [threading messages](/messaging#threading) page for more information.

## Updating a message {#updating-messages}

Let's say you have a bot that posts the status of a request. When that request changes, you'll want to update the message to reflect it's state.

```
response = client.chat_update(    channel="C0XXXXXX",    ts="1476746830.000003",    text="updates from your app! :tada:")
```

See the [`chat.update`](/reference/methods/chat.update) API method for formatting options and some special considerations when calling this with a bot user.

## Deleting a message {#deleting-messages}

Sometimes you need to delete things.

```
response = client.chat_delete(    channel="C0XXXXXX",    ts="1476745373.000002")
```

See the [`chat.delete`](/reference/methods/chat.delete) API method for more details.

## Conversations {#conversations}

The Slack Conversations API provides your app with a unified interface to work with all the channel-like things encountered in Slack: public channels, private channels, direct messages, group direct messages, and shared channels.

Refer to [using the Conversations API](/apis/web-api/using-the-conversations-api) for more information.

### Direct messages {#direct-messages}

The `conversations.open` API method opens either a 1:1 direct message with a single user or a multi-person direct message, depending on the number of users supplied to the `users` parameter. (For public or private channels, use the `conversations.create` API method.)

Provide a `users` parameter as an array with 1-8 user IDs to open or resume a conversation. Providing only 1 ID will create a direct message. providing more IDs will create a new multi-party direct message or will resume an existing conversation.

Subsequent calls with the same set of users will return the already existing conversation.

```
import osfrom slack_sdk import WebClientclient = WebClient(token=os.environ["SLACK_BOT_TOKEN"])response = client.conversations_open(users=["W123456789", "U987654321"])
```

See the [`conversations.open`](/reference/methods/conversations.open) API method for additional details.

### Creating channels {#creating-channels}

Creates a new channel, either public or private. The `name` parameter is required and may contain numbers, letters, hyphens, or underscores, and must contain fewer than 80 characters. To make the channel private, set the optional `is_private` parameter to `True`.

```
import osfrom slack_sdk import WebClientfrom time import timeclient = WebClient(token=os.environ["SLACK_BOT_TOKEN"])channel_name = f"my-private-channel-{round(time())}"response = client.conversations_create(    name=channel_name,    is_private=True)channel_id = response["channel"]["id"]response = client.conversations_archive(channel=channel_id)
```

See the [`conversations.create`](/reference/methods/conversations.create) API method for additional details.

### Getting conversation information {#getting-conversation-info}

To retrieve a set of metadata about a channel (public, private, DM, or multi-party DM), use the `conversations.info` API method. The `channel` parameter is required and must be a valid channel ID. The optional `include_locale` boolean parameter will return locale data, which may be useful if you wish to return localized responses. The `include_num_members` boolean parameter will return the number of people in a channel.

```
import osfrom slack_sdk import WebClientclient = WebClient(token=os.environ["SLACK_BOT_TOKEN"])response = client.conversations_info(    channel="C031415926",    include_num_members=1)
```

See the [`conversations.info`](/reference/methods/conversations.info) API method for more details.

### Listing conversations {#listing-conversations}

To get a list of all the conversations in a workspace, use the `conversations.list` API method. By default, only public conversations are returned. Use the `types` parameter specify which types of conversations you're interested in. Note that `types` is a string of comma-separated values.

```
import osfrom slack_sdk import WebClientclient = WebClient(token=os.environ["SLACK_BOT_TOKEN"])response = client.conversations_list()conversations = response["channels"]
```

Use the `types` parameter to request additional channels, including `public_channel`, `private_channel`, `mpdm`, and `dm`. This parameter is a string of comma-separated values.

```
import osfrom slack_sdk import WebClientclient = WebClient(token=os.environ["SLACK_BOT_TOKEN"])response = client.conversations_list(    types="public_channel, private_channel")
```

Archived channels are included by default. You can exclude them by passing `exclude_archived=True` to your request.

```
response = client.conversations_list(exclude_archived=True)
```

See the [`conversations.list`](/reference/methods/conversations.list) API method for more details.

### Getting members of a conversation {#getting-conversation-members}

To get a list of members for a conversation, use the `conversations.members` API method with the required `channel` parameter.

```
import osfrom slack_sdk import WebClientclient = WebClient(token=os.environ["SLACK_BOT_TOKEN"])response = client.conversations_members(channel="C16180339")user_ids = response["members"]
```

See the [`conversations.members`](/reference/methods/conversations.members) API method for more details.

### Joining a conversation {#joining-conversations}

Channels are the social hub of most Slack teams. Here's how you hop into one:

```
response = client.conversations_join(channel="C0XXXXXXY")
```

If you are already in the channel, the response is slightly different. The `already_in_channel` attribute will be true, and a limited `channel` object will be returned. Bot users cannot join a channel on their own, they need to be invited by another user.

See the [`conversations.join`](/reference/methods/conversations.join) API method for more details.

### Leaving a conversation {#leaving-conversations}

To leave a conversation, use the `conversations.leave` API method with the required `channel` parameter containing the ID of the channel to leave.

```
import osfrom slack_sdk import WebClientclient = WebClient(token=os.environ["SLACK_BOT_TOKEN"])response = client.conversations_leave(channel="C27182818")
```

See the [`conversations.leave`](/reference/methods/conversations.leave) API method for more details.

## Opening a modal {#opening-modals}

Modals allow you to collect data from users and display dynamic information in a focused surface. Modals use the same blocks that compose messages, with the addition of an `input` block.

```
from slack_sdk.signature import SignatureVerifiersignature_verifier = SignatureVerifier(os.environ["SLACK_SIGNING_SECRET"])from flask import Flask, request, make_response, jsonifyapp = Flask(__name__)@app.route("/slack/events", methods=["POST"])def slack_app():    if not signature_verifier.is_valid_request(request.get_data(), request.headers):        return make_response("invalid request", 403)    if "payload" in request.form:        payload = json.loads(request.form["payload"])        if payload["type"] == "shortcut" and payload["callback_id"] == "open-modal-shortcut":            # Open a new modal by a global shortcut            try:                api_response = client.views_open(                    trigger_id=payload["trigger_id"],                    view={                        "type": "modal",                        "callback_id": "modal-id",                        "title": {                            "type": "plain_text",                            "text": "Awesome Modal"                        },                        "submit": {                            "type": "plain_text",                            "text": "Submit"                        },                        "blocks": [                            {                                "type": "input",                                "block_id": "b-id",                                "label": {                                    "type": "plain_text",                                    "text": "Input label",                                },                                "element": {                                    "action_id": "a-id",                                    "type": "plain_text_input",                                }                            }                        ]                    }                )                return make_response("", 200)            except SlackApiError as e:                code = e.response["error"]                return make_response(f"Failed to open a modal due to {code}", 200)        if (            payload["type"] == "view_submission"            and payload["view"]["callback_id"] == "modal-id"        ):            # Handle a data submission request from the modal            submitted_data = payload["view"]["state"]["values"]            print(submitted_data)    # {'b-id': {'a-id': {'type': 'plain_text_input', 'value': 'your input'}}}            # Close this modal with an empty response body            return make_response("", 200)    return make_response("", 404)if __name__ == "__main__":    # export SLACK_SIGNING_SECRET=***    # export SLACK_BOT_TOKEN=xoxb-***    # export FLASK_ENV=development    # python3 app.py    app.run("localhost", 3000)
```

See the [`views.open`](/reference/methods/views.open) API method for more details and additional parameters.

To run the above example, the following [Slack app configurations](https://api.slack.com/apps) are required:

*   Enable **Interactivity** with a valid Request URL: `https://{your-public-domain}/slack/events`
*   Add a global shortcut with the callback ID: `open-modal-shortcut`

## Updating and pushing modals {#updating-pushing-modals}

In response to `view_submission` requests, you can tell Slack to update the current modal view by having `"response_action": update` and an updated view. There are also other `response_action` types, such as `errors` and `push`. Refer to the [modals](/surfaces/modals) page for more details.

```
if (    payload["type"] == "view_submission"    and payload["view"]["callback_id"] == "modal-id"):    # Handle a data submission request from the modal    submitted_data = payload["view"]["state"]["values"]    print(submitted_data)    # {'b-id': {'a-id': {'type': 'plain_text_input', 'value': 'your input'}}}    # Update the modal with a new view    return make_response(        jsonify(            {                "response_action": "update",                "view": {                    "type": "modal",                    "title": {"type": "plain_text", "text": "Accepted"},                    "close": {"type": "plain_text", "text": "Close"},                    "blocks": [                        {                            "type": "section",                            "text": {                                "type": "plain_text",                                "text": "Thanks for submitting the data!",                            },                        }                    ],                },            }        ),        200,    )
```

If your app modifies the current modal view when receiving `block_actions` requests from Slack, you can call the `views.update` API method with the given view ID.

```
private_metadata = "any str data you want to store"response = client.views_update(    view_id=payload["view"]["id"],    hash=payload["view"]["hash"],    view={        "type": "modal",        "callback_id": "modal-id",        "private_metadata": private_metadata,        "title": {            "type": "plain_text",            "text": "Awesome Modal"        },        "submit": {            "type": "plain_text",            "text": "Submit"        },        "close": {            "type": "plain_text",            "text": "Cancel"        },        "blocks": [            {                "type": "input",                "block_id": "b-id",                "label": {                    "type": "plain_text",                    "text": "Input label",                },                "element": {                    "action_id": "a-id",                    "type": "plain_text_input",                }            }        ]    })
```

See the [`views.update`](/reference/methods/views.update) API method for more details.

If you want to push a new view onto the modal instead of updating an existing view, see the [`views.push`](/reference/methods/views.push) API method.

## Emoji reactions {#emoji}

You can quickly respond to any message on Slack with an emoji reaction. Reactions can be used for any purpose: voting, checking off to-do items, showing excitement, or just for fun.

This method adds a reaction (emoji) to an item (`file`, `file comment`, `channel message`, `group message`, or `direct message`). One of `file`, `file_comment`, or the combination of `channel` and `timestamp` must be specified. Note that your app's bot user needs to be in the channel (otherwise, you will get either a `not_in_channel` or `channel_not_found` error code).

```
response = client.reactions_add(    channel="C0XXXXXXX",    name="thumbsup",    timestamp="1234567890.123456")
```

Removing an emoji reaction is basically the same format, but you'll use the `reactions.remove` API method instead of the `reactions.add` API method.

```
response = client.reactions_remove(    channel="C0XXXXXXX",    name="thumbsup",    timestamp="1234567890.123456")
```

See the [`reactions.add`](/reference/methods/reactions.add) and [`reactions.remove`](/reference/methods/reactions.remove) API methods for more details.

## Uploading files {#upload-files}

You can upload files to Slack and share them with people in channels. Note that your app's bot user needs to be in the channel (otherwise, you will get either `not_in_channel` or `channel_not_found` error code).

```
response = client.files_upload_v2(    file="test.pdf",    title="Test upload",    channel="C3UKJTQAC",    initial_comment="Here is the latest version of the file!",)
```

If you want to share files within a thread, you can pass `thread_ts` in addition to `channel_id` as shown below:

```
response = client.files_upload_v2(    file="test.pdf",    title="Test upload",    channel="C3UKJTQAC",    thread_ts="1731398999.934122",    initial_comment="Here is the latest version of the file!",)
```

See the [`files.upload`](/reference/methods/files.upload) API method for more details.

## Adding a remote file {#adding-remote-files}

You can add a file information that is stored in an external storage rather than in Slack.

```
response = client.files_remote_add(    external_id="the-all-hands-deck-12345",    external_url="https://{your domain}/files/the-all-hands-deck-12345",    title="The All-hands Deck",    preview_image="./preview.png" # will be displayed in channels)
```

See the [files.remote.add](/reference/methods/files.remote.add) API method for more details.

## Calling API methods {#calling-API-methods}

This library covers all the public endpoints as the methods in `WebClient`. That said, you may see a bit of a delay with the library release. When you're in a hurry, you can directly use the `api_call` method as below.

```
import osfrom slack_sdk import WebClientclient = WebClient(token=os.environ['SLACK_BOT_TOKEN'])response = client.api_call(    api_method='chat.postMessage',    params={'channel': '#random','text': "Hello world!"})assert response["message"]["text"] == "Hello world!"
```

## AsyncWebClient {#asyncwebclient}

The Web API client is available in asynchronous programming using the standard [asyncio](https://docs.python.org/3/library/asyncio.html) library. You use `AsyncWebClient` instead. `AsyncWebClient` internally relies on the [AIOHTTP](https://docs.aiohttp.org/en/stable/) library, but it is an optional dependency. To use this class, run `pip install aiohttp` beforehand.

```
import asyncioimport os# requires: pip install aiohttpfrom slack_sdk.web.async_client import AsyncWebClientfrom slack_sdk.errors import SlackApiErrorclient = AsyncWebClient(token=os.environ['SLACK_API_TOKEN'])# This must be an async methodasync def post_message():    try:        # Don't forget `await` keyword here        response = await client.chat_postMessage(            channel='#random',            text="Hello world!"        )        assert response["message"]["text"] == "Hello world!"    except SlackApiError as e:        assert e.response["ok"] is False        assert e.response["error"]  # str like 'invalid_auth', 'channel_not_found'        print(f"Got an error: {e.response['error']}")# This is the simplest way to run the async method# but you can go with any ways to run itasyncio.run(post_message())
```

## RetryHandler {#retryhandler}

With the default settings, only `ConnectionErrorRetryHandler` with its default configuration (=only one retry in the manner of [exponential backoff and jitter](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/)) is enabled. The retry handler retries if an API client encounters a connectivity-related failure (e.g., connection reset by peer).

To use other retry handlers, you can pass a list of `RetryHandler` to the client constructor. For instance, you can add the built-in `RateLimitErrorRetryHandler` this way:

```
import osfrom slack_sdk.web import WebClientclient = WebClient(token=os.environ["SLACK_BOT_TOKEN"])# This handler does retries when HTTP status 429 is returnedfrom slack_sdk.http_retry.builtin_handlers import RateLimitErrorRetryHandlerrate_limit_handler = RateLimitErrorRetryHandler(max_retry_count=1)# Enable rate limited error retries as wellclient.retry_handlers.append(rate_limit_handler)
```

You can also create one on your own by defining a new class that inherits `slack_sdk.http_retry.RetryHandler` (`AsyncRetryHandler` for asyncio apps) and implements required methods (internals of `can_retry` / `prepare_for_next_attempt`). Check out the source code for the ones that are built in to learn how to properly implement them.

```
import socketfrom typing import Optionalfrom slack_sdk.http_retry import (RetryHandler, RetryState, HttpRequest, HttpResponse)from slack_sdk.http_retry.builtin_interval_calculators import BackoffRetryIntervalCalculatorfrom slack_sdk.http_retry.jitter import RandomJitterclass MyRetryHandler(RetryHandler):    def _can_retry(        self,        *,        state: RetryState,        request: HttpRequest,        response: Optional[HttpResponse] = None,        error: Optional[Exception] = None    ) -> bool:        # [Errno 104] Connection reset by peer        return error is not None and isinstance(error, socket.error) and error.errno == 104client = WebClient(    token=os.environ["SLACK_BOT_TOKEN"],    retry_handlers=[MyRetryHandler(        max_retry_count=1,        interval_calculator=BackoffRetryIntervalCalculator(            backoff_factor=0.5,            jitter=RandomJitter(),        ),    )],)
```

For asyncio apps, `Async` prefixed corresponding modules are available. All the methods in those modules are async/await compatible. Check [the source code](https://github.com/slackapi/python-slack-sdk/blob/main/slack_sdk/http_retry/async_handler.py) for more details.

## Rate limits {#rate-limits}

When posting messages to a channel, Slack allows apps to send no more than one message per channel per second. We allow bursts over that limit for short periods; however, if your app continues to exceed the limit over a longer period of time, it will be rate limited. Different API methods have other limits — be sure to check the [rate limits](/apis/web-api/rate-limits) and test that your app has a graceful fallback if it should hit those limits.

If you go over these limits, Slack will begin returning _HTTP 429 Too Many Requests_ errors, a JSON object containing the number of calls you have been making, and a _Retry-After_ header containing the number of seconds until you can retry.

Here's an example of how you might handle rate limited requests:

```
import osimport timefrom slack_sdk import WebClientfrom slack_sdk.errors import SlackApiErrorclient = WebClient(token=os.environ["SLACK_BOT_TOKEN"])# Simple wrapper for sending a Slack messagedef send_slack_message(channel, message):    return client.chat_postMessage(        channel=channel,        text=message    )# Make the API call and save results to `response`channel = "#random"message = "Hello, from Python!"# Do until being rate limitedwhile True:    try:        response = send_slack_message(channel, message)    except SlackApiError as e:        if e.response.status_code == 429:            # The `Retry-After` header will tell you how long to wait before retrying            delay = int(e.response.headers['Retry-After'])            print(f"Rate limited. Retrying in {delay} seconds")            time.sleep(delay)            response = send_slack_message(channel, message)        else:            # other errors            raise e
```

Since v3.9.0, the built-in `RateLimitErrorRetryHandler` is available as an easier way to do retries for rate limited errors. Refer to the [RetryHandler](#retryhandler) section for more details.

Refer to the [rate limits](/apis/web-api/rate-limits) page for more information.

---

Source: https://docs.slack.dev/tools/python-slack-sdk/webhook

# Webhook client

## Incoming webhooks {#incoming-webhooks}

You can use `slack_sdk.webhook.WebhookClient` for [incoming webhooks](/messaging/sending-messages-using-incoming-webhooks) and message responses using [`response_url`](/interactivity/handling-user-interaction#message_responses) in payloads.

To use [incoming webhooks](/messaging/sending-messages-using-incoming-webhooks), calling the `WebhookClient(url)#send(payload)` method works for you. The call posts a message in a channel associated with the webhook URL.

```
from slack_sdk.webhook import WebhookClienturl = "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX"webhook = WebhookClient(url)response = webhook.send(text="Hello!")assert response.status_code == 200assert response.body == "ok"
```

It's also possible to use `blocks` using [Block Kit](/block-kit).

```
from slack_sdk.webhook import WebhookClienturl = "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX"webhook = WebhookClient(url)response = webhook.send(    text="fallback",    blocks=[        {            "type": "section",            "text": {                "type": "mrkdwn",                "text": "You have a new request:\n**"            }        }    ])
```

## The response_url {#the-response_url}

User actions in channels generate a [`response_url`](/interactivity/handling-user-interaction#message_responses) and include the URL in the payload. You can use `WebhookClient` to send a message via the `response_url`.

```
import osfrom slack_sdk.signature import SignatureVerifiersignature_verifier = SignatureVerifier(    signing_secret=os.environ["SLACK_SIGNING_SECRET"])from slack_sdk.webhook import WebhookClientfrom flask import Flask, request, make_responseapp = Flask(__name__)@app.route("/slack/events", methods=["POST"])def slack_app():    # Verify incoming requests from Slack    # https://docs.slack.dev/authentication/verifying-requests-from-slack    if not signature_verifier.is_valid(        body=request.get_data(),        timestamp=request.headers.get("X-Slack-Request-Timestamp"),        signature=request.headers.get("X-Slack-Signature")):        return make_response("invalid request", 403)    # Handle a slash command invocation    if "command" in request.form \        and request.form["command"] == "/reply-this":        response_url = request.form["response_url"]        text = request.form["text"]        webhook = WebhookClient(response_url)        # Send a reply in the channel        response = webhook.send(text=f"You said '{text}'")        # Acknowledge this request        return make_response("", 200)    return make_response("", 404)
```

## AsyncWebhookClient {#asyncwebhookclient}

The webhook client is available in asynchronous programming using the standard [asyncio](https://docs.python.org/3/library/asyncio.html) library. You use `AsyncWebhookClient` instead. `AsyncWebhookClient` internally relies on the [AIOHTTP](https://docs.aiohttp.org/en/stable/) library, but it is an optional dependency. To use this class, run `pip install aiohttp` beforehand.

```
import asyncio# requires: pip install aiohttpfrom slack_sdk.webhook.async_client import AsyncWebhookClienturl = "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX"async def send_message_via_webhook(url: str):    webhook = AsyncWebhookClient(url)    response = await webhook.send(text="Hello!")    assert response.status_code == 200    assert response.body == "ok"# This is the simplest way to run the async method# but you can go with any ways to run itasyncio.run(send_message_via_webhook(url))
```

## RetryHandler {#retryhandler}

With the default settings, only `ConnectionErrorRetryHandler` with its default configuration (=only one retry in the manner of [exponential backoff and jitter](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/)) is enabled. The retry handler retries if an API client encounters a connectivity-related failure (e.g., connection reset by peer).

To use other retry handlers, you can pass a list of `RetryHandler` to the client constructor. For instance, you can add the built-in `RateLimitErrorRetryHandler` this way:

```
from slack_sdk.webhook import WebhookClienturl = "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX"webhook = WebhookClient(url=url)# This handler does retries when HTTP status 429 is returnedfrom slack_sdk.http_retry.builtin_handlers import RateLimitErrorRetryHandlerrate_limit_handler = RateLimitErrorRetryHandler(max_retry_count=1)# Enable rate limited error retries as wellwebhook.retry_handlers.append(rate_limit_handler)
```

You can also create one on your own by defining a new class that inherits `slack_sdk.http_retry.RetryHandler` (`AsyncRetryHandler` for asyncio apps) and implements required methods (internals of `can_retry` / `prepare_for_next_attempt`). Check out the source code for the ones that are built in to learn how to properly implement them.

```
import socketfrom typing import Optionalfrom slack_sdk.http_retry import (RetryHandler, RetryState, HttpRequest, HttpResponse)from slack_sdk.http_retry.builtin_interval_calculators import BackoffRetryIntervalCalculatorfrom slack_sdk.http_retry.jitter import RandomJitterclass MyRetryHandler(RetryHandler):    def _can_retry(        self,        *,        state: RetryState,        request: HttpRequest,        response: Optional[HttpResponse] = None,        error: Optional[Exception] = None    ) -> bool:        # [Errno 104] Connection reset by peer        return error is not None and isinstance(error, socket.error) and error.errno == 104webhook = WebhookClient(    url=url,    retry_handlers=[MyRetryHandler(        max_retry_count=1,        interval_calculator=BackoffRetryIntervalCalculator(            backoff_factor=0.5,            jitter=RandomJitter(),        ),    )],)
```

For asyncio apps, `Async` prefixed corresponding modules are available. All the methods in those modules are async/await compatible. Check [the source code](https://github.com/slackapi/python-slack-sdk/blob/main/slack_sdk/http_retry/async_handler.py) for more details.