見出し画像

Slack Bolt for Python における ack の使い方徹底解説

Slack アプリ開発では、ユーザーからの各種インタラクション(スラッシュコマンド、ボタン、モーダル送信、ショートカットなど)に対し、必ず「リクエストを受領した」という応答を Slack に返す必要があります。これを行うのが ack 関数です。Slack は 3 秒以内に HTTP 200 の応答を返さないとタイムアウトエラーとなるため、すぐに ack を呼ぶことが必須となります。

本記事では、Slack Bolt for Python を用いて、ack の基本的な役割と主要なユースケース(スラッシュコマンド、ボタンアクション、モーダル送信、選択肢の動的提供)について、具体例とともに詳しく解説します。


1. ack の基本的な役割

ack は、Slack からのリクエストに対して「受信しました」と即座に返すための関数です。

  • 応答が遅れるとタイムアウトエラーとなり、ユーザー側にエラー表示される。

  • インタラクティブな操作の場合、UI 上のローディング状態が解除される。

通常、リスナー関数の冒頭で必ず ack() を呼び出し、その後に重い処理や追加の API 呼び出しを実行します。これにより Slack には即座に 200 OK を返すことができ、ユーザー体験が向上します。


2. スラッシュコマンドでの ack の使い方

スラッシュコマンドは、ユーザーがコマンドを入力した際に発生するイベントです。
以下の例は、/hello コマンドを受け取り、発行者に挨拶メッセージを返信するサンプルコードです。

from slack_bolt import App

app = App(token="xoxb-***", signing_secret="***")

@app.command("/hello")
def handle_hello_command(ack, command, respond):
    # まず即座に ack() を呼んで応答を返す
    ack()
    # ユーザーへ返信(デフォルトは発行者のみのエフェメラルメッセージ)
    respond(f"こんにちは、<@{command['user_id']}> さん!")

if __name__ == "__main__":
    app.start(port=3000)

ポイント:

  • 必ず最初に ack() を呼ぶ。

  • 応答メッセージは respond() を使って送信可能。

  • respond() に response_type: "in_channel" を指定すると、チャンネル全体に公開することもできる。


3. ボタンなどのブロックアクションでの ack の使い方

メッセージ内のボタンがクリックされた場合、app.action で登録したリスナーが呼ばれます。
以下は、ボタン(action_id が "delete_btn")がクリックされたときに、対象メッセージを削除する例です。

@app.action("delete_btn")
def handle_delete_button(ack, body, client):
    # まず ack() を呼び、ボタンのローディング状態を解除
    ack()
    channel_id = body["channel"]["id"]
    message_ts = body["message"]["ts"]
    # メッセージを削除する API 呼び出し
    client.chat_delete(channel=channel_id, ts=message_ts)

ポイント:

  • ボタン操作の場合も、最初に必ず ack() を呼ぶ

  • その後、必要な Web API 呼び出し(例:chat_delete、chat_update 等)で UI の更新を行う。


4. モーダル送信(View Submission)での ack の使い方

モーダル内のフォーム送信時には、送信内容の検証を行い、条件に応じてエラーを返すかモーダルを閉じるかを選択します。
以下は、モーダル送信時にフィードバック入力の検証を行い、入力が不足している場合にエラーを返す例です。

@app.view("feedback_modal")
def handle_feedback_modal(ack, body, view, client):
    state_values = view["state"]["values"]
    feedback = state_values["feedback_block"]["feedback_input"]["value"]

    # 入力値が空の場合、エラー情報を返す
    if not feedback:
        ack(response_action="errors", errors={
            "feedback_block": "フィードバックを入力してください。"
        })
        return

    # 正常な場合は単に ack() を呼んでモーダルを閉じる
    ack()

    # その後、たとえばユーザーへサンクスメッセージを送信する
    user_id = body["user"]["id"]
    client.chat_postMessage(
        channel=user_id,
        text="フィードバックありがとうございます!"
    )

ポイント:

  • 入力エラーがある場合は、ack() に response_action="errors" と errors オブジェクトを渡す。

  • 条件を満たす場合は、単に ack() を呼ぶだけでモーダルは自動的に閉じる。

  • エラー応答を返した後は、以降の処理を中断(return)すること。


5. 選択肢(Options)の動的提供での ack の使い方

外部セレクトメニューなど、動的に選択肢を返す場合は、app.options を利用します。
以下は、選択肢として「寿司」と「ラーメン」を返す例です。

@app.options("food_menu")
def show_food_options(ack):
    options = [
        {
            "text": {"type": "plain_text", "text": "寿司"},
            "value": "sushi"
        },
        {
            "text": {"type": "plain_text", "text": "ラーメン"},
            "value": "ramen"
        }
    ]
    # 選択肢情報を ack() で返す
    ack(options=options)

ポイント:

  • ack() の引数に options キーを指定し、Slack が期待するフォーマットの選択肢リストを返す。

  • 外部セレクトなど、動的に選択肢を生成する場合に便利。


6. その他の注意点

  • 3秒ルールの厳守:
    すべてのインタラクションでは、3秒以内に ack() を返さないとタイムアウトエラーとなるため、重い処理は ack() 後に非同期で実行するなどの工夫が必要です。

  • エラー処理:
    リスナー内で予期しないエラーが発生した場合も、可能な限り ack() を返すようにして、Slack 側でタイムアウトとならないように注意しましょう。

  • 1リクエストにつき1回の ack():
    同一リクエストに対して複数回 ack() を呼ばないように、各分岐後は必ず return するなどの対策を取りましょう。

  • Lazy Listener の活用:
    AWS Lambda などサーバーレス環境で動作させる場合、先に ack() を返し、後続処理を別途非同期に実行する Lazy Listener の仕組みも検討するとよいでしょう。


まとめ

Slack Bolt for Python では、各種インタラクションに対して 必ず ack() を呼び、Slack に即座に 200 OK を返す ことが求められます。

  • スラッシュコマンド: コマンド受信時に即座に ack() を呼び、続けて respond() などで応答メッセージを送信。

  • ブロックアクション(ボタンなど): ユーザー操作に対して、まず ack() を実行し、必要に応じた Web API 呼び出しで UI を更新。

  • モーダル送信: 入力検証後、エラーがあれば ack(response_action="errors") でエラー情報を返し、正常な場合は単に ack() でモーダルを閉じる。

  • 選択肢提供: 外部セレクト等で、ack(options=...) を利用して動的な選択肢リストを返す。

これらのパターンを正しく実装することで、ユーザーにとってスムーズかつ応答の速い Slack アプリを構築することができます。実装時は「まず ack() を呼ぶ」という基本を忘れずに、各種ユースケースに応じた応答内容を適切に返すようにしましょう。

以上が、Slack Bolt for Python における ack の使い方の解説とサンプルコードになります。この記事を参考に、ぜひ実際のアプリ開発にお役立てください。

いいなと思ったら応援しよう!