As a Python Slack-Bolt application developer I want to create slash-command that are composed with the Python Click package. I use the FastAPI web framework in asyncio mode.
I need support for --help
--version
and any usage error to be properly
sent as Slack messages.
Check out the example application.
The following example illustrates how to device a Click group object and bind it to the Slack-Bolt command listener. In this example the code is all asyncio. Click was not written to support async io natively, so the @click_async decorator is required.
By default the Developer should pass the Slack-Bolt request instance as the Click obj value when executing the Click group/command. There is a mechanism to change this behavior, shown in a later example.
import click
from slack_bolt.request.async_request import AsyncBoltRequest as Request
from slack_click.async_click import click_async, version_option, AsyncSlackClickGroup
# -----------------------------------------------------------------------------
# Define a Click group handler for the Slack Slash-Command
#
# Notes:
# @click_async decorator must be used for asyncio mode
# @click.pass_obj inserts the click.Context obj into the callback parameters
# By default the obj is the Slack-Bolt request instance; see the
# @app.command code further down.
# -----------------------------------------------------------------------------
@click.group(name="/clicker", cls=AsyncSlackClickGroup)
@version_option(version="0.1.0")
@click.pass_obj
@click_async
async def cli_click_group(request: Request):
"""
This is the Clicker /click command group
"""
say = request.context["say"]
await say("`/click` command invoked without any commands or options.")
# -----------------------------------------------------------------------------
# Register the command with Slack-Bolt
# -----------------------------------------------------------------------------
@app.command(cli_click_group.name)
async def on_clicker(request: Request, ack, say):
await ack()
await say("Got it.")
return await cli_click_group(prog_name=cli_click_group.name, obj=request)
# -----------------------------------------------------------------------------
# Add a command the Click group
# -----------------------------------------------------------------------------
@cli_click_group.command("hello")
@click.pass_obj
async def click_hello_command(request: Request):
await request.context.say(f"Hi there <@{request.context.user_id}> :eyes:")
As a Developer you may need to pass the Click obj as something other than the
bolt request direct, as shown in the example above. You can identify
where the Slack-Bolt request object can be found in the obj by passing
a callback function to the click.command() or click.group() function called
slack_request
. This parameter expects a function that takes the click obj and
returns the slack request.
The example below uses an inline lambda to get the request from the obj as a dictionary, using a the key "here".
from random import randrange
import click
from slack_bolt.request.async_request import AsyncBoltRequest as Request
from slack_click.async_click import click_async, AsyncSlackClickCommand
# -----------------------------------------------------------------------------
# /fuzzy command to manifest an anminal
# -----------------------------------------------------------------------------
animals = [":smile_cat:", ":hear_no_evil:", ":unicorn_face:"]
@click.command(
name="/fuzzy", cls=AsyncSlackClickCommand, slack_request=lambda obj: obj["here"]
)
@click_async
@click.pass_obj
async def cli_fuzzy_command(obj: dict):
request = obj["here"]
say = request.context["say"]
this_animal = animals[randrange(len(animals))]
await say(f"Poof :magic_wand: {this_animal}")
@app.command(cli_fuzzy_command.name)
async def on_fuzzy(request: Request, ack):
await ack()
return await cli_fuzzy_command(
prog_name=cli_fuzzy_command.name, obj={"here": request}
)
If you want to change the Slack message format for help or usage methods you can
subclass AsyncSlackCLickCommand
or AsyncSlackClickGroup
and overload the methods:
- slack_format_help - returns the Slack message payload (dict) for
--help
- slack_format_usage_help - returns the Slack message payload (dict) when click exception
UsageError
is raised.