Closed

Slack Boltをためす

21

Boltのチュートリアル 作業手順

  1. slack app作成
  2. slack boltのパッケージインストール
  3. slack appのevent subscriptionsを設定
  4. 動作確認
  5. アクションの追加

まとめ

BoltでAppインスタンス作り、そのappが何をするのか定義する
app.startするとサーバーが立ち上がってる感じになる

app.startしたサーバーのurlをEvent Subscriptionsのrequest urlとして設定することで
listenしたイベントをBoltで定義したappへ渡してくれる

slack app作成

普通にslack app作ればいいだけだけど、workspaceにインストールするときに
App HomeでDisplay Nameとか設定しないとインストールできないので注意

https://api.slack.com/apps

slack boltのパッケージインストール

npm install @slack/bolt

slack appのevent subscriptionsを設定

slack appの設定画面でevent subscriptionsを選択
localhostで検証できるようにrequest Urlはngorkでhttpsの値をセット

slack appの設定変えた時は都度、再インストールする
ヘッダに黄色の注意バーができてるはず

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の基本的な概念を読む

https://slack.dev/bolt-js/ja-jp/concepts#basic
  • message()
  • say()
  • event()
  • client
  • action()
  • shortcut()
  • command
  • モーダル
  • options()

message()

第一引数に文字列か正規表現を受け取り、メッセージイベントで受け取った値と合致した場合に
第二引数で定義した処理を実行する

say()

イベントリスナーに関連づけれた処理をする場合はsay()でメッセージ送信ができる
引数には文字列や、オブジェクトを定義して、リッチなメッセージも送信できる

リッチメッセージ
Block Kit Builder

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()

ショートカットの設定場所

メッセージショートカット

  1. Create New Shortcut する
  2. On messagesを選ぶ
  3. ショートカットの名前と説明とcallback idを設定

グローバルショートカット

メッセージショートカットと同じ工程
On messageGlobalに変更するだけ

実装

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使う

https://app.slack.com/block-kit-builder

submitのcallback idと取りたい場合に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をいれる

say()がないのでclientから直接apiメソッドを叩く

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()メソッドも調べる

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()
このスクラップは8時間前にクローズされました
ログインするとコメントできます