LoginSignup
1
2

More than 1 year has passed since last update.

LambdaでSlackからのButton Actionを受け取って、それに応じて元のメッセージをupdateする

Posted at

やりたいこと

何らかのSlack app(Bot)からボタン付きのメッセージをPOSTし、押されたボタンに応じて元のメッセージを更新する。
Animation.gif
個人的には機械学習のアノテーションに使おうと思っていて、ボタンに応じた値をDBに保存して、元のメッセージにアノテーション済みですみたいな文言を出したいなと考えてます。

元のボタン付きメッセージの作り方

まず、元のメッセージはこのように作りました。
例は2択ですが、elements内の要素を増やせば何択でもできると思います。

また、SLACK_TOKENはSlackアプリであればアプリの管理画面上のOAuth & Permissions > OAuth Tokens for Your Workspaceから取得できるはずです。

# !pip install slackclient
from slack import WebClient

SLACK_TOKEN = "xoxb-xxxxxxxxxxxx"
client = WebClient(token=SLACK_TOKEN)
blocks = [
    {
        "type": "section",
        "text": {
            "type": "mrkdwn",
            "text": "元気ですか?",
        },
    },
    {
        "type": "actions",
        "block_id": "button_block",
        "elements": [
            {
                "type": "button",
                "text": {"type": "plain_text", "text": "元気です!"},
                "style": "primary",
                "value": "1",
                "action_id": "yes",
            },
            {
                "type": "button",
                "text": {"type": "plain_text", "text": "そうでもない…"},
                "style": "danger",
                "value": "0",
                "action_id": "no",
            },
        ],
    },
]

response = client.chat_postMessage(
    channel="チャンネル名",
    blocks=blocks,
)
)

メッセージを更新するLambdaの作り方

ざっくり以下の2ステップが必要なので、順に説明します。

  1. Lambdaの実装
  2. Lambda(API Gateway)のURLをSlack appのInteractivity & Shortcuts > Interactivity > Request URLに登録する
  3. Slack appのOAuth & Permissions > Scopesでメッセージを送る権限をつける

Lambdaの実装

下のようにすればButton Actionを受け取り、値に応じてメッセージを更新できます。
今回は単純にボタンのテキストをそのまま返していますが、もちろん条件分岐などでもっと複雑なこともできるはずです。

import base64
import json
from urllib.parse import parse_qs

from slack import WebClient

SLACK_TOKEN = "xoxb-xxxxxxxxxxxx"
client = WebClient(token=SLACK_TOKEN)

payload = json.loads(
        parse_qs(base64.b64decode(event["body"]).decode("utf-8"))["payload"][0]
    )
response = client.chat_update(
        channel=payload["container"]["channel_id"],
        ts=payload["message"]["ts"],
        attachments=[
            {"text": f":white_check_mark: {payload['actions'][0]['text']['text']}"}
        ],
    )

個人的にハマったのは下のあたりです。

  • SlackからのPOSTはbase64エンコードかつapplication/x-www-form-urlencodedなので、適切にデコードしてパースする必要がある
  • 元のメッセージを特定するためには下の2つが必要なので、リクエスト内から取得する
    • channel_id(チャンネル名ではNG)
    • ts(タイムスタンプ)
  • メッセージを更新するやり方はいくつかあるが、今回のslackclientを使って更新するやり方では、slackclientを使ってPOSTされたメッセージしか更新できない
    • 例)requests.post()で投稿したメッセージは更新できない
  • Slack APIでメッセージ内容の記述方法はtext, attachments, blocksの3つあるが、「text, blocksが両方あるときはblocksしか表示されない」のような関係があるので
    • 元のメッセージがblocksの場合、更新をtextでやろうとしてもできない
    • 元のメッセージがblocksの場合、attachmentsで更新すると元のblocksは残ったままでattachmentsが追加される
      • 今回はこれを利用して、元のメッセージに追記する形で更新した

なお、text, attachments, blocksの関係はこのようになってるようです。

Lambda(API Gateway)をSlack appに登録

実装したLambda関数やAPI GatewayのURLをSlack appの管理画面から登録します。
Interactivity & Shortcuts > Interactivity > Request URL
image.png

Slack appにメッセージを送る権限をつける

同じくSlack appの管理画面上のOAuth & Permissions > Scopeschat:write, chat:write:customizeを付与します。
そもそもこれがないと元のボタン付きのメッセージも送れないと思います。

image.png

以上の手順で、最初に書いた「何らかのSlack app(Bot)からボタン付きのメッセージをPOSTし、押されたボタンに応じて元のメッセージを更新する」ができるはずです。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2