今更ながらSlack Actionを使ってみました。
良かった点やWebとの概念的違いについてのまとめになります。
Slack側の設定
ここではSlack側の設定になります。
Slack APIにアクセスする
Create New Appを選択します。
今回はFrom scratchで1から作成します。
- App Nameは日本語可能で適当でOK
- Pick a workspace to develop your app inはインサートしたいWorkspaceを選択します
API設定
今回は以下の情報をメモしておきます。
- Basic Information > App CredentialsからSigning Secret(SHOWボタンで表示されます)
OAuth & Permissionsの設定
Scopesの設定でBOT Token Scopesでスコープを付与(今回はchat:writeを設定します)
スコープの設定を実施するとWorkspaceにインストールができるので、Install Appから選択します。
インストールが完了するとOAuth & Permissions > OAuth TokensからBOT Tokenを取得します(xoxbから始まるトークンです)
このSigning SecretとBOT Tokenはアプリ内で使用しますが決して外部に漏らしてはいけないので、その点だけ注意しましょう!(環境変数 & gitignoreで管理しましょう)
Slack Action独自の設定
Slack ActionはAPI設定とは別に独自の設定が必要になります。
サイドメニューからInteractivity & Shortcutsを選択し、InteractivityをONにします。
- Request URLは後程設定します。デプロイで作成されたURLを入力する必要があります
サーバー側の設定
SlackのアクションはあくまでUIを提供するのに特化してるため、具体的な処理はサーバー側に任せます。先ほど紹介したRequet URLで処理を行います。
今回はNext.jsで作成しています。
import { App, ExpressReceiver } from "@slack/bolt";
export const receiver = new ExpressReceiver({
signingSecret: process.env.SLACK_SIGNING_SECRET ?? '',
processBeforeResponse: true,
endpoints: '/api/slack',
});
export const createInstance = () => {
const app = new App({
signingSecret: process.env.SLACK_SIGNING_SECRET, // 先ほどメモったSigning secret
token: process.env.SLACK_BOT_TOKEN, // 先ほどメモったBot Token
receiver: receiver,
});
return app;
}
今回はendpointをhttps://[domain]/api/slackに設定しています。Request URLも同様に設定する必要がありますので、そこだけ注意が必要です。
そうではないとサーバー側に届いてるのにSlackのUI上でリクエストが応答されない現象が発生します
(ここで頭を抱えました。。。💦)
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
const method = req.method?.toLowerCase();
if (method !== 'post') {
return badRequest(res);
}
// app.tsで定義したreceiverを参照します。
return await receiver.app.handle(req, res);
}
Next側のAPI Routeはこのような形
認証周りとかはインスタンスを作る際に実施してくれるので、POSTのチェックだけでOKです。
(このコードだとGETだと400、POSTだと401がこのコードでは返却されます)
このルートハンドラの前に定義するコードを解説します。
import type { NextApiRequest, NextApiResponse } from "next";
import { workOnTime, overTime, showInitMessage, showOverTimeModal } from "@/lib/slack/router";
// Next.jsのパーサーを設定(これをしないと受取れません)
export const config = {
api: {
bodyParser: false,
},
};
// Slack Appのインスタンスを作成
const app = createInstance();
// Slackのアクションを定義(後述します)
app.action(SLACK_ACTION_ID.SOME_ACTION, async ({payload, client, ack}) => {
await ack();
const value = JSON.parse(payload?.value ?? '') as ShowSlackArgs;
await client.chat.update({
// ...省略
})
});
// 先ほど紹介したハンドラ
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
const method = req.method?.toLowerCase();
if (method !== 'post') {
return badRequest(res);
}
return await receiver.app.handle(req, res);
}
receiver.app.handleで定義したものをapp.actionで拾うような形です。
app.actionの第一引数はUIを作る際に定義したaction_id、第二引数はコールバックです。
Slackは3秒以内での応答が必須になるので、まずはack()を非同期で必ず実行しましょう。
また、Slackなどに応答を返却したいなどがあればclientやviewオブジェクトで所定の操作をするというのが基本的な流れです。
client.chat
- メッセージを送信します
- 更新をしたい(メッセージを書き換えたい)場合は
updateを使用しますが、メッセージIDであるtsを指定する必要があります -
tsはpostMessageをしたタイミングで生成されるので、値を保持したいケースはこの戻り値をupdateで更新する必要があります(非常に面倒!)
// 該当のWSのチャンネルにメッセージを送信
const result = await client.chat.postMessage({
blocks: blocks, // blocksを生成
channel: CHANNEL_ID.GENERAL,
});
// 同様の内容だが、tsを付与してUpdate
await client.chat.update({
blocks: blocks,
channel: CHANNEL_ID.GENERAL,
ts: result.ts ?? '',
})
UI作成について
Slackに送信するBlockの作成についてです、上記のコードのblocksに該当するパラメータですね。
JSON形式(JavaScript(以下JS)のobject形式)で作成が可能なのですが、面倒なのでツールを使用しましょう!
こんな感じでサンプルが表示されるので、Sectionとactionsを使用してコンポーネントを配置していきます。ある程度作成して、細かい所を都度修正する形で進めましょう。
{
"blocks": [
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Click Me",
"emoji": true
},
"value": "click_me_123",
"action_id": "actionId-0" // action_idはサーバー側に合わせる
}
]
}
]
}
作成されたものをコードに貼り付けて、動的なパラメータに対応できるようにしましょう。
おわりに
今回はモーダルについては触れませんでしたが、これも実際のところ簡単です。
はまりやすいポイントとしては
- JS的なDOM挙動は出来ない
- なのでJS的な挙動はactionやリクエスト数が増える
- エンドポイントの指定の仕方に注意
- ackを最初に呼ぶ
- 応答ルールが厳しい
良かった点としてはSlack上で操作ができるのは良いですよね。
フロント部分を直感的に作成できるので、興味がある人はURLのBlock Kitだけでも遊ぶと良いと思います。
こんなかんじです。
また機会があればモーダル表示についても記載しようと思います(備忘録のため)
オブジェクティブグループではXの投稿も平日毎日行っています!
IT 関連の小ネタや便利技から、日常のアニメ・ゲーム布教なども幅広く投稿してるので、
ご興味のある方は是非フォロー・いいねをお願いします。



