やりたいこと
何らかのSlack app(Bot)からボタン付きのメッセージをPOSTし、押されたボタンに応じて元のメッセージを更新する。
個人的には機械学習のアノテーションに使おうと思っていて、ボタンに応じた値を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ステップが必要なので、順に説明します。
- Lambdaの実装
- Lambda(API Gateway)のURLをSlack appの
Interactivity & Shortcuts > Interactivity > Request URL
に登録する - 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
Slack appにメッセージを送る権限をつける
同じくSlack appの管理画面上のOAuth & Permissions > Scopes
にchat:write
, chat:write:customize
を付与します。
そもそもこれがないと元のボタン付きのメッセージも送れないと思います。
以上の手順で、最初に書いた「何らかのSlack app(Bot)からボタン付きのメッセージをPOSTし、押されたボタンに応じて元のメッセージを更新する」ができるはずです。