LoginSignup
5
3

More than 1 year has passed since last update.

[Slack App] 動的にメッセージを更新するテクニック

Last updated at Posted at 2022-06-06

画面収録_2022-06-06_19_20_28_AdobeCreativeCloudExpress.gif

はじめに

本記事ではSlackアプリでの動的なメッセージ更新のtipsを紹介します。基本的なSlackアプリの作り方は割愛します。巷に良記事が溢れているのでそちらをご覧ください。
対象読者としてはある程度Slackアプリ開発をかじったことがある人を想定しています。

使用ツール

上の二つはもうSlackアプリ開発においては外すことができませんね。
AWS lambdaでのSlackアプリ作成に関しては、Slack の bot を自作する(AWS Lambda + API Gateway)の記事が大変参考になります。

Slackにおけるmessage構造

Slackはプレーンなメッセージの他にもボタンや画像、セクションメニューやカレンダーなど、さまざまなコンポーネントを表示することができます。これらは内部的にはjson形式のblockという単位で定義されており、それらを組み合わせることでとてもリッチなメッセージを作ることができます。

Block Kit Builderで実際のblockを見てみる

実際にBlock Kit Builderを使ってメッセージがどんなblock構造になっているか見てみます。
左側が実際に表示されるメッセージ、右側がそのblock構造になります。


二枚目の画像ではplain textとinputのblockが順にリストになっているのがわかります。
それぞれのコンポーネントを定義するblockを、表示したい順番にリストにしていけばいいのでとてもシンプルな構造です。

Block idという存在

各blockにはblock_idという要素を持たせることができます。
これはblockを一意に識別するためのもので、任意の文字列(最大255文字)をセットすることができます。
何かユーザがインタラクティブなアクションをした時に、このidによってどのblockソースを使ったのかを識別することができるようになります。
たとえば、複数の入力フォームが用意されている以下のようなメッセージをご覧ください。一つ目の入力フォームのblock_idにはform1、二つ目にはform2としました。処理を受け取る側ではこのidを参照することによって、どちらのフォームによる入力なのかを識別することができます。

任意の文字列を指定できることを活用する

さて、ここが本題です。先述の通りblock_idには任意の文字列を指定することができます。このidをそのblockが保持しているステータスと捉えると、たとえばこんな機能が簡単にできます。

画面収録_2022-06-06_19_20_28_AdobeCreativeCloudExpress.gif

はじめblock_id = '1'という状態で保持しておき、それをボタンアクションごとに参照して1を足したものをtextとして表示しています。それを再度block_idに渡すことで値の更新が再起的にできるようになります。
chat_updateと組み合わせることで、新たなメッセージを表示することなく動的に更新することができるようになりました。

以下はこのデモに使ったlambda_functionのコードになります。アクションごとにblock_idを取り出す、処理を加えた後にまたblock_idに戻す、という流れを確認してください。

lambda_function.py
import os
from slack_bolt import App
from slack_bolt.adapter.aws_lambda import SlackRequestHandler

app = App(
    process_before_response=True,
    token=os.environ.get("SLACK_BOT_TOKEN"),
    signing_secret=os.environ.get("SLACK_SIGNING_SECRET"),
)

def lambda_handler(event, context):
    slack_handler = SlackRequestHandler(app=app)
    return slack_handler.handle(event, context)

@app.event("app_mention")
def say_hello(say, body, client):
    text = "1"
    say(blocks=generate_block(text))

@app.action("button-action")
def button(say, body, client):
    channel = body['container']['channel_id']
    ts = body['message']['ts']
    block_id = int(body['message']['blocks'][0]['block_id']) + 1
    client.chat_update(
    	channel=channel,
        ts=ts,
        blocks=generate_block(str(block_id)),
        text = "alt_message"
    )
    
def generate_block(text):
	return [
		{
			"type": "section",
			"block_id": text,
			"text": {
				"type": "mrkdwn",
				"text": text
			},
			"accessory": {
				"type": "button",
				"text": {
					"type": "plain_text",
					"text": "Click",
					"emoji": True
				},
				"value": "click_me_123",
				"action_id": "button-action"
			}
		}
	]

終わりに

もちろんボタンアクションに限らず全てのアクションで活用することができます。
blockにユーザのアクションの文脈を持たせることで、異なるコンポーネントの組み合わせ処理を実現できます。
inputアクションなども織り交ぜると結構複雑なことも実現できるのでぜひお試しください。

5
3
1

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
5
3