Slack Boltをためす
ゴール
api gateway + lambda上でslack boltを動かす
Boltのチュートリアル 作業手順
- slack app作成
- slack boltのパッケージインストール
- slack appのevent subscriptionsを設定
- 動作確認
- アクションの追加
まとめ
BoltでAppインスタンス作り、そのappが何をするのか定義する
app.startするとサーバーが立ち上がってる感じになる
app.startしたサーバーのurlをEvent Subscriptionsのrequest urlとして設定することで
listenしたイベントをBoltで定義したappへ渡してくれる
slack app作成
普通にslack app作ればいいだけだけど、workspaceにインストールするときに
App HomeでDisplay Nameとか設定しないとインストールできないので注意
slack boltのパッケージインストール
npm install @slack/bolt
slack appのevent subscriptionsを設定
slack appの設定画面でevent subscriptionsを選択
localhostで検証できるようにrequest Urlはngorkでhttpsの値をセット
request urlはeventをどこにリクエスト送るか
slack boltのデフォルトのpathは/slack/events
になってるっぽい
boltの定義側で変更できそう
Subscribe to bot events
でslack appがどんな時にsubscribeするか(slack appをaddしたチャネルでメッセージがpostされた瞬間とか)設定できるしここでaddすると必要なscopeも自動で足してくれる
動作確認
hello
って発言すると発言した人に対してHey there <@${message.user}>!
って言ってくれる
app.message("hello", async ({ message, say }) => {
await say(`Hey there <@${message.user}>!`);
});
アクションの追加
インタラクティブ機能の設定
slackメッセージで使えるボタンや日付ピッカーなどの機能を利用できるように
slack appのInteractivity & Shortcuts
の設定をONにする
request urlはEvent Subscriptions
と同じものを使用する
実装
hello
と発言したらボタン付きのメッセージを返信する
app.message("hello", async ({ message, say }) => {
await say({
blocks: [
{
type: "section",
text: {
type: "mrkdwn",
text: `Hey there <@${message.user}>!`,
},
accessory: {
type: "button",
text: {
type: "plain_text",
text: "Click Me",
},
action_id: "button_click",
},
},
],
text: `Hey there <@${message.user}>!`,
});
});
ボタン付きメッセージのボタンが押された時のアクション
app.action("button_click", async ({ body, ack, say }) => {
await ack();
await say(`<@${body.user.id}> clicked the button`);
});
Slack Boltの基本的な概念を読む
message()
say()
event()
client
action()
shortcut()
command
- モーダル
options()
message()
第一引数に文字列か正規表現を受け取り、メッセージイベントで受け取った値と合致した場合に
第二引数で定義した処理を実行する
say()
イベントリスナーに関連づけれた処理をする場合はsay()
でメッセージ送信ができる
引数には文字列や、オブジェクトを定義して、リッチなメッセージも送信できる
event()
slackで発生したeventをlistenする
event一覧
client
slackのweb apiを呼び出すときに使用する
import { App } from "@slack/bolt";
const app = new App({
// tokenの設定
});
app.client.(web apiのメソッド)
で呼び出せる
action()
第一引数で定義したaction_idがトリガーされると、第二引数の内容が実行される
action_idの設定はインタラクティブコンポーネント(ボタンとかモーダルとか)のトリガーとして設定できる
全てのアクション(command, options)でack()
を呼ぶ必要がある
ack()
を実行するとslack側にイベントが正常に受信されたことを知らせることができる
ack()
はerrorを渡すこともできる
shortcut()
ショートカットの設定場所
メッセージショートカット
-
Create New Shortcut
する -
On messages
を選ぶ - ショートカットの名前と説明と
callback id
を設定
グローバルショートカット
メッセージショートカットと同じ工程
On message
をGlobal
に変更するだけ
実装
app.shortcut()
の第一引数にcallbackId、第二引数でショートカットが呼ばれた際の実装をする
app.shortcut("{callback idをいれる}", async ({ shortcut, ack, context }) => {
ack();
try {
const result = await app.client.views.open({
token: context.botToken,
trigger_id: shortcut.trigger_id,
view: {
type: "modal",
title: {
type: "plain_text",
text: "foobar",
},
close: {
type: "plain_text",
text: "Close",
},
blocks: [
{
type: "section",
text: {
type: "mrkdwn",
text: "I like Apple",
},
},
{
type: "context",
elements: [
{
type: "mrkdwn",
text:
"",
},
],
},
],
},
});
console.log(result);
} catch (error) {
console.log(error);
}
});
command()
Slach Commands
からCreate New Command
する
command、request urlを入力する
request urlはhttps://{ドメイン}/slack/events
で良さげ
app.command("/command-name", async ({ command, ack, say }) => {
await ack();
await say(`${command.text}`);
});
モーダル
アクションするモーダルを作る
ショートカットが選択されたら表示するモーダルを定義
modalのjson定義はめんどくさいので用意されてるblock-kit-builder
使う
app.shortcut("open_modal_hada_g", async ({ shortcut, ack, context }) => {
ack();
try {
const result = await app.client.views.open({
token: context.botToken,
trigger_id: shortcut.trigger_id,
view: {
type: "modal",
// NOTE: submitからcallback idをとりたい場合はここに定義する
callback_id: "submit_modal",
title: {
type: "plain_text",
text: "ハツジからの質問です",
emoji: true,
},
submit: {
type: "plain_text",
text: "送信する",
emoji: true,
},
close: {
type: "plain_text",
text: "キャンセル",
emoji: true,
},
blocks: [
{
type: "divider",
},
{
type: "section",
text: {
type: "plain_text",
text: "何か一言喋ってみて",
emoji: true,
},
},
{
type: "input",
block_id: "text_input",
element: {
type: "plain_text_input",
action_id: "plain_text_input-action",
},
label: {
type: "plain_text",
text: "ラベル",
emoji: true,
},
},
],
},
});
console.log(result);
} catch (error) {
console.log(error);
}
});
こんな感じのモーダルできる
モーダル2
作ったモーダルからcallback idを受け取って処理する
app.view()
の第一引数にcallback idをいれる
app.view("submit_modal", async ({ ack, view, body, client }) => {
await ack();
const input =
view["state"]["values"]["text_input"]["plain_text_input-action"]["value"];
const user = body["user"]["id"];
try {
await client.chat.postMessage({
channel: user,
text: input,
});
} catch (error) {
console.error(error);
}
});
inputの値取るのがview["state"]["values"]["text_input"]["plain_text_input-action"]["value"]
かなりめんどかったけど、console.logでviewの中身を解読すればいける
TODO: api gateway + lambdaにデプロイする
余裕があればoptions()
メソッドも調べる
AWS Lambdaへのデプロイ
import { App, ExpressReceiver } from "@slack/bolt";
import { APIGatewayProxyEvent, Context } from "aws-lambda";
// TODO: バージョンアップデート
// TODO: https://github.com/vendia/serverless-express/tree/mainline
import * as awsServerlessExpress from "aws-serverless-express";
// レシーバーの定義
const processBeforeResponse = true;
const expressReceiver = new ExpressReceiver({
signingSecret: process.env.SLACK_SIGNING_SECRET || "",
processBeforeResponse,
});
expressReceiver.app.options("*", (req, res) => {
res.sendStatus(202);
});
// レシーバをlambdaとapi gatewayでよしなに扱えるようにしてくれる
const server = awsServerlessExpress.createServer(expressReceiver.app);
export const ProxyStart = (event: APIGatewayProxyEvent, context: Context) => {
awsServerlessExpress.proxy(server, event, context);
};
// boltのapp設定
export const app = new App({
token: process.env.SLACK_BOT_TOKEN || "",
receiver: expressReceiver,
processBeforeResponse,
});
// botでやることの定義
app.message()