Skip to content

Commit

Permalink
docs: document modal-related functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
DonDebonair committed Nov 22, 2024
1 parent 21e2bba commit 8927815
Show file tree
Hide file tree
Showing 10 changed files with 128 additions and 22 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class DeploymentPlugin(MachineBasePlugin):
**Dropped support for Python 3.8** (v0.38.0)

As of [v0.38.0](https://github.com/DonDebonair/slack-machine/releases/tag/v0.38.0), support for Python 3.8 has been
dropped. Python 3.7 has reached end-of-life on 2024-10-07.
dropped. Python 3.8 has reached end-of-life on 2024-10-07.

## Features

Expand All @@ -59,6 +59,7 @@ dropped. Python 3.7 has reached end-of-life on 2024-10-07.
- Support for [blocks](https://api.slack.com/reference/block-kit/blocks)
- Support for [message attachments](https://api.slack.com/docs/message-attachments) [Legacy 🏚]
- Support for [interactive elements](https://api.slack.com/block-kit)
- Support for [modals](https://api.slack.com/surfaces/modals)
- Listen and respond to any [Slack event](https://api.slack.com/events) supported by the Events API
- Store and retrieve any kind of data in persistent storage (currently Redis, DynamoDB, SQLite and in-memory storage are
supported)
Expand All @@ -68,7 +69,6 @@ dropped. Python 3.7 has reached end-of-life on 2024-10-07.

### Coming Soon

- Support for modals
- Support for shortcuts
- ... and much more

Expand Down
6 changes: 6 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ The following classes form the basis for Plugin development.

### ::: machine.plugins.block_action.BlockAction

### ::: machine.plugins.modals.ModalSubmission

### ::: machine.plugins.modals.ModalClosure


## Decorators

Expand All @@ -34,6 +38,8 @@ These classes represent base objects from the Slack API

### ::: machine.models.channel.Channel

### ::: machine.models.interactive.View

## Storage

Storage is exposed to plugins through the `self.storage` field. The following class implements the interface plugins
Expand Down
4 changes: 2 additions & 2 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class DeploymentPlugin(MachineBasePlugin):
**Dropped support for Python 3.8** (v0.38.0)

As of [v0.38.0](https://github.com/DonDebonair/slack-machine/releases/tag/v0.38.0), support for Python 3.8 has been
dropped. Python 3.7 has reached end-of-life on 2024-10-07.
dropped. Python 3.8 has reached end-of-life on 2024-10-07.

## Features

Expand All @@ -59,6 +59,7 @@ dropped. Python 3.7 has reached end-of-life on 2024-10-07.
- Support for [blocks](https://api.slack.com/reference/block-kit/blocks)
- Support for [message attachments](https://api.slack.com/docs/message-attachments) [Legacy 🏚]
- Support for [interactive elements](https://api.slack.com/block-kit)
- Support for [modals](https://api.slack.com/surfaces/modals)
- Listen and respond to any [Slack event](https://api.slack.com/events) supported by the Events API
- Store and retrieve any kind of data in persistent storage (currently Redis, DynamoDB, SQLite, and in-memory storage
are supported)
Expand All @@ -68,6 +69,5 @@ dropped. Python 3.7 has reached end-of-life on 2024-10-07.

### Coming Soon

- Support for modals
- Support for shortcuts
- ... and much more
9 changes: 5 additions & 4 deletions docs/plugins/block-kit-actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ Slack Machine makes it easy to listen to _actions_ triggered by these interactiv

## Defining actions

When you're defining [blocks](https://api.slack.com/reference/block-kit ) for your interactive surfaces, each of
these blocks can be given a `block_id`. Within certain blocks, you can place
When you're defining [blocks](https://api.slack.com/reference/block-kit ) for your interactive surfaces - either by
providing a [dict][] or by leveraging the [models of the Slack SDK for Python](https://tools.slack.dev/python-slack-sdk/api-docs/slack_sdk/models/blocks/index.html)
- each of these blocks can be given a `block_id`. Within certain blocks, you can place
[block elements](https://api.slack.com/reference/block-kit/block-elements) that are interactive. These interactive
elements can be given an `action_id`. Given that one block can contain multiple action elements, each `block_id` can
be linked to multiple `action_id`s.
Expand All @@ -19,7 +20,7 @@ Whenever the user interacts with these elements, an event is sent to Slack Machi

## Listening to actions

With the [`action`][machine.plugins.decorators.action] decorator you can define which plugin methods should be
With the [`@action`][machine.plugins.decorators.action] decorator you can define which plugin methods should be
called when a certain action is triggered. The decorator takes 2 arguments: the `block_id` and the `action_id` that
you want to listen to. Both arguments are optional, but **one of them always needs to be set**. Both arguments accept a
[`str`][str] or [`re.Pattern`][re.Pattern]. When a string is provided, the handler only fires upon an exact match,
Expand All @@ -34,7 +35,7 @@ Your block action handler will be called with a [`BlockAction`][machine.plugins.
contains useful information about the action that was triggered and the message or other surface in which the action
was triggered.

You can optionally pass the `logger` argument to get a
You can optionally add the `logger` argument to your handler get a
[logger that was enriched by Slack Machine](misc.md#using-loggers-provided-by-slack-machine-in-your-handler-functions)

The [`BlockAction`][machine.plugins.block_action.BlockAction] contains various useful fields and properties about
Expand Down
2 changes: 1 addition & 1 deletion docs/plugins/interacting.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ You can read [the events section][slack-machine-events] to see how your plugin c
## Using the Slack Web API in other ways

Sometimes you want to use [Slack Web API](https://api.slack.com/web) in ways that are not directly exposed by
[`MachineBaserPlugin`][machine.plugins.base.MachineBasePlugin]. In these cases you can use
[`MachineBasePlugin`][machine.plugins.base.MachineBasePlugin]. In these cases you can use
[`self.web_client`][machine.plugins.base.MachineBasePlugin.web_client]. `self.web_client` references the
[`AsyncWebClient`](https://slack.dev/python-slack-sdk/api-docs/slack_sdk/web/async_client.html#slack_sdk.web.async_client.AsyncWebClient)
object of the underlying Slack Python SDK. You should be able to call any
Expand Down
104 changes: 104 additions & 0 deletions docs/plugins/modals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Modals

In Slack, [modals](https://api.slack.com/surfaces/modals) are a way to ask users for input or display information in a
dialog/popup-like form. Modals are a great way to collect information from users, display information, or confirm an
action.

Modals can only be triggered by [actions the user takes](https://api.slack.com/interactivity#user). The most
common types of user actions that can trigger a modal are:

- Shortcuts
- [Slash Commands][slash-commands]
- [Block Kit interative components][block-kit-actions]

For each of these actions, Slack provides a `trigger_id` which can be used to open a modal. **This needs to be done
within 3 seconds of receiving the `trigger_id`**. Slack Machine abstracts most of this away for you and lets you
open modals from Slash Commands and Block Kit actions without having to worry about the `trigger_id`.

## Defining and opening modals

When you want to open a modal, you first need to define the
[_view_](https://api.slack.com/surfaces/modals#composing_views) with the content you want to show. This view has
some additional properties that define how the modal should behave. One important property is the `callback_id` which
is used to identify the modal when it is submitted or closed.

You can define a modal view in 2 ways:

- As a [dict][] that conforms to the [View schema](https://api.slack.com/reference/surfaces/views#modal)
- By constructing a [View](https://tools.slack.dev/python-slack-sdk/api-docs/slack_sdk/models/views/index.html#slack_sdk.models.views.View)
object from the Slack SDK for Python

When you have defined the view, you can open the modal by calling the `open_modal` method on the
[`Command`][machine.plugins.command.Command] object that is passed to your Slash Command handler or on the
[`BlockAction`][machine.plugins.block_action.BlockAction] object that is passed to your Block Kit action handler.

## Listening for modal interactions

Once a modal is opened, the user can interact with the block kit elements within the modal, such as buttons, input
fields, datepickers etc. When the user interacts with these elements, a [block kit action](block-kit-actions.md) can be
triggered which lets you deal with input.

Additionally, when the user _submits_ the modal, this triggers a `view_submission` event that you can listen to with
the [`@modal`][machine.plugins.decorators.modal] decorator. This decorator takes a `callback_id` as
an argument, which is used to identify the modal that was submitted. The `callback_id` can be a [`str`][str] or a
[`re.Pattern`][re.Pattern]. When a string is provided, the handler only fires upon an exact match, whereas with a regex
pattern, you can have the handler fired for multiple matching `callback_id`s. This is convenient when you want one
handler to process multiple modals, for example.

Unless you want to listen for changes to specific input fields - for example to update the modal in-place - it's
probably easiest to use the `@modal` decorator and process the entire input upon modal submission.

### The modal handler function

The handler function will be called with a
[`ModalSubmission`][machine.plugins.modals.ModalSubmission] object that contains useful information about the
modal that was submitted and the user that submitted it. The `ModalSubmission` object has a property
[`view`][machine.plugins.modals.ModalSubmission.view] that contains the complete view that was submitted, including the
state of the input fields of the modal. The object also has a [`user`][machine.plugins.modals.ModalSubmission.user]
property that corresponds to the user that submitted the modal.

You can optionally add the `logger` argument to your handler get a
[logger that was enriched by Slack Machine](misc.md#using-loggers-provided-by-slack-machine-in-your-handler-functions)

The `ModalSubmission` contains methods for
[updating the current modal view][machine.plugins.modals.ModalSubmission.update_modal],
[pushing a new view on top of the current one][machine.plugins.modals.ModalSubmission.push_modal] or even
[opening a completely new modal][machine.plugins.modals.ModalSubmission.open_modal].

You can also send a message to the user that submitted the modal with the
[`send_dm`][machine.plugins.modals.ModalSubmission.send_dm] method.

The modal handler function can be defined as a regular `async` function or a generator. When you define it as a
generator, you can use the `yield` statement to:

- [Update the modal view in-place](https://api.slack.com/surfaces/modals#updating_response)
- [Push a new view on top of the current one](https://api.slack.com/surfaces/modals#add_response)
- [Close the current view](https://api.slack.com/surfaces/modals#close_current_view) (which is the default behavior when
nothing is yielded)
- [Close all the views on the stack](https://api.slack.com/surfaces/modals#close_all_views)
- [Display errors in the modal](https://api.slack.com/surfaces/modals#displaying_errors), which is useful when you want
to show validation errors to the user.

!!! warning

You must yield a response to Slack within 3 seconds of receiving the `view_submission` event. If you don't, Slack
will show an error to the user.


## Listening for modal closures

Sometimes you want to know when a user closes a modal without submitting it. This can be useful to clean up
resources or store the state of the modal for later continuation. You can listen for modal closures with the
[`@modal_closed`][machine.plugins.decorators.modal_closed] decorator. This decorator takes a `callback_id` as parameter
and works the same way as the `@modal` decorator.

The handler function will be called with a
[`ModalClosure`][machine.plugins.modals.ModalClosure] object that contains information about the modal that was
closed. Just like the `ModalSubmission` object, the `ModalClosure` object has a
[`view`][machine.plugins.modals.ModalSubmission.view] property that contains the complete view of the modal that was
closed, including the state of the input fields. The object also has a
[`user`][machine.plugins.modals.ModalClosure.user] property that corresponds to the user that submitted the
modal.

You can send a message to the user that submitted the modal with the
[`send_dm`][machine.plugins.modals.ModalClosure.send_dm] method.
16 changes: 6 additions & 10 deletions docs/plugins/slash-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ features:
## Defining your Slash Command in code
The next step is to use the [`command`][machine.plugins.decorators.command] decorator on the function that should be
The next step is to use the [`@command`][machine.plugins.decorators.command] decorator on the function that should be
triggered when the user uses the Slash Command you defined. The decorator takes only 1 parameter: the slash command
that should trigger the decorated function. It should be the same as the Slash Command you just defined in the App
dashboard.
Expand All @@ -55,7 +55,7 @@ information about the slash command invocation. The most important property is p
[`text`][machine.plugins.command.Command.text], which contains any additional text that was passed when the slash
command was used.

You can optionally pass the `logger` argument to get a
You can optionally add the `logger` argument to your handler get a
[logger that was enriched by Slack Machine](misc.md#using-loggers-provided-by-slack-machine-in-your-handler-functions)

### Responding to a command
Expand Down Expand Up @@ -96,14 +96,10 @@ async def hello_again(self, command):
await command.say("This will be sent after the initial acknowledgement")
```

## Other types of responses
## Opening modals

The [`Command`][machine.plugins.command.Command] object that your handler receives, contains an extra piece of
information you can use to trigger more varied reponses: the [`trigger_id`][machine.plugins.command.Command.trigger_id]
The `trigger_id` can used specifically to trigger
[_modal responses_](https://api.slack.com/interactivity/handling#modal_responses). For now, creating a modal is
something you have to take care of yourself. More information on this can be found
[here](https://api.slack.com/surfaces/modals/using).

In future releases, Slack Machine will make working with modals much easier by allowing modals to be opened directly
through the provided command object, and responding to interactions happening in modals through new decorators.
The `trigger_id` can used specifically to trigger [_modal responses_][modals]. You don't have to worry about the
`trigger_id` and instead can just call the [`open_modal`][machine.plugins.command.Command.open_modal] method on the
`Command` object to open a modal.
1 change: 0 additions & 1 deletion machine/plugins/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,6 @@ async def update(
:param text: message text
:param attachments: optional attachments (see [attachments])
:param blocks: optional blocks (see [blocks])
:param thread_ts: optional timestamp of thread, to send a message in that thread
:param ephemeral_user: optional user name or id if the message needs to visible
to a specific user only
:return: Dictionary deserialized from [`chat.update`](https://api.slack.com/methods/chat.update) request
Expand Down
2 changes: 1 addition & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ nav:
- 'plugins/slash-commands.md'
- 'plugins/interacting.md'
- 'plugins/block-kit-actions.md'
- 'plugins/modals.md'
- 'plugins/settings.md'
- 'plugins/storage.md'
- 'plugins/misc.md'
Expand Down Expand Up @@ -67,4 +68,3 @@ plugins:
members_order: source
import:
- https://docs.python.org/dev/objects.inv
- https://slack.dev/python-slack-sdk/objects.inv
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ mock_use_standalone_module = true
addopts = "--verbose --cov-report term-missing --cov-report xml --junit-xml pytest.xml --cov=machine"

[tool.mypy]
python_version = "3.12"
python_version = "3.13"
ignore_missing_imports = true
show_column_numbers = true
show_error_codes = true
Expand Down

0 comments on commit 8927815

Please sign in to comment.