RAKUS Developers Blog | ラクス エンジニアブログ

株式会社ラクスのITエンジニアによる技術ブログです。

Slack API入門 -Boltを使ってSlack アプリを開発する-

こんにちは。楽楽勤怠バックエンドチームのmako_makokです。

皆様はSlack アプリを開発されるときはどうやって開発されていますか?
Hubotでしょうか?それともHttp Clientから直で叩いていますか?
今回はSlack APIを簡単に使え、爆速でSlack アプリを開発するためのフレームワークBoltのご紹介をさせていただきます。

Boltとは

Slack APIチーム謹製のSlack API Client + スラッシュコマンド用のレシーバを兼ね備えた、Slack アプリ開発用のフレームワークです。
現時点ではJavaScript(TypeScript), Java, PythonSDKが配布されています。
オープンソースで開発されていおり、リポジトリはそれぞれ以下です。

JavaScript

主にTypeScriptで開発されています。
型フレンドリーにSlack APIを使用することができます。 github.com

Java

kotlin用のextensionやSpring BootやKtorなどJava, KotlinのWeb Frameworkへのextensionやサンプルなども豊富にあります。

github.com

Python

type hintフレンドリーな開発ができます。
一部独自のアプローチでSlack アプリが抱える問題を解決したりしています。 github.com

どの言語でも細かな差はありますが、基本的なインターフェースは同じなので自身の好きな言語を選択してください。
今回はbolt-jsを使用して解説していこうと思います。

スラッシュコマンドについて

Boltの説明に入る前に、スラッシュコマンドについて説明します。
スラッシュコマンドは、Slack上で/fooのようにし、それをトリガーとして様々なアクションを行うことができます。
もし既にSlackをインストールしてあるのであれば、/と入力してみてください。
デフォルトで登録されているいくつかのスラッシュコマンドが一覧表示されます。
続けて/remindと入力してみると、リマインダー登録用のモーダルが表示されると思います。

スラッシュコマンドの概要

スラッシュコマンドを簡略化すると、以下のように動いています。

スラッシュコマンドを入力すると、指定したエンドポイントにSlackからHttp Requestが送られてきます。

スラッシュコマンドを登録する

スラッシュコマンドはhttps://api.slack.com/apps/{app_id}/slash-commandsにアクセスし、Create New Commandを押すと以下の画面が出てきます。

Commandに登録したいスラッシュコマンド、Request URLにSlackからのリクエストを受け取るエンドポイントを設定します。

Boltを使う

Slack APIを使うために、Boltの初期化をしていきます。
今回はbolt-jsを利用するので、npmでインストールします。
プロジェクトを作成し、npm install @slack/boltとするだけでインストールが完了します。
TypeScriptはお好みで入れてください。
最初にAppをインスタンス化します。

import { App } from '@slack/bolt'

const app = new App({
  token: process.env.SLACK_BOT_TOKEN,
  signingSecret: process.env.SLACK_SIGNING_SECRET,
})

(async () => {
  // Start your app
  await app.start(process.env.PORT || 3000);

  console.log('⚡️ Bolt app is running!');
})();

tokenとsingleSecretは以下から取得することができます。

  • singleSecret https://api.slack.com/apps/{app_id}

  • token https://api.slack.com/apps/{app_id}

Slack API を叩いてみる

Boltの初期化は完了したので、実際にSlack APIを叩いていきます。
以下はchat.postMessageを叩いて、チームメンバーが参加したらメッセージを送信するサンプルです。

app.event('team_join', async ({ event, client }) => {
  try {
    await client.chat.postMessage({
      channel: 'foo',
      text: `いらっしゃい、<@${event.user.id}>! 🎉`,
    })
  } catch (error) {
    console.error(error)
  }
})

第一引数にイベント名、第二引数にリスナーを設定します。
リスナーのclientは認証済みのものが渡されるので、すぐにSlack APIを叩くことができます。
また、リスナーを介さずSlack APIを叩くことも可能です。
App自体がWebClientを持っているので、tokenを都度渡して実行します。

await app.client.chat.postMessage({
  token: process.env.SLACK_BOT_TOKEN,
  channel: 'foo',
  text: `いらっしゃい、<@${event.user.id}>! 🎉`,
})

さらに、BoltにはSlack APIを更に簡単に扱うためのユーティリティが用意されています。
例えば、更に簡単にchat.postMessageを実行できるsay()関数などがあります。
以下は「おはよう」というメッセージに反応して、「今日もいい天気!」と返すスクリプトです。

app.message('おはよう', async ({ message, say }) => {
  await say(`今日もいい天気!`);
});

say()関数はチャンネルが特定できるイベントであれば、使用することができます。

スラッシュコマンドを作る

Boltでは簡単にスラッシュコマンドを作ることができます。

レシーバーの作成

Slackからのリクエストを受け取ってレスポンスを返すレシーバー、以下のように設定します。

import { App, ExpressReceiver } from '@slack/bolt'

export const expressReceiver = new ExpressReceiver({
 signingSecret: process.env.SLACK_SIGNING_SECRET,
 endpoints: '/events'
})

export const app = new App({
 receiver: expressReceiver,
 token: process.env.SLACK_BOT_TOKEN
})

ExpressReceiverAppに渡すだけで、簡単にレシーバーの設定をすることができます。
エンドポイントは/eventsと記載してありますが、実際のルーティングは/slack/eventsとなるので注意してください。
このエンドポイントを「スラッシュコマンドについて」で紹介した、Request URLに設定して準備完了です。

スラッシュコマンドのアクションを作成する

スラッシュコマンドを入力されたときのアクションを書いていきます。
今回は/register-wordというスラッシュコマンドが入力されると、モーダルが開き、情報を入力してsubmitすると入力された内容が送信され、それを受けとるサンプルです。

const close: PlainTextElement = {
  type: 'plain_text',
  text: 'キャンセル'
}

const submit: PlainTextElement = {
  type: 'plain_text',
  text: '登録'
}

function buildBlocks(): KnownBlock[] {
  const keywordInput: InputBlock = {
    type: 'input',
    block_id: 'keyword',
    label: {
      type: 'plain_text',
      text: '検索ワード',
    },
    element: {
      type: 'plain_text_input',
      action_id: 'keyword_input',
    },
  }
  // ...
  return [keywordInput]
}

const VIEW_ID = 'dialog_1'

export const register = (app: App) => {
  app.command('/register-word', async ({ ack, body, context, command }) => {
    await ack()

    try {
      await app.client.views.open({
        token: context.botToken,
        trigger_id: body.trigger_id,
        view: {
          type: 'modal',
          callback_id: VIEW_ID,
          title: {
            type: 'plain_text',
            text: '検索ワードの登録',
          },
          blocks: buildBlocks(),
          private_metadata: command.channel_id,
          close,
          submit
        },
      })
    } catch (err) {
      console.error(err)
    }
  })

  app.view(VIEW_ID, async ({ ack, view, context, body }) => {
    await ack()

    const values = view.state.values
    const keyword = values.keyword.keyword_input.value
    // ...
  })
}

まずapp.commandでスラッシュコマンドを待機します。

  app.command('/register-word', async ({ ack, body, context, command }) => { }

最初にack()関数を実行します。
この関数はSlackに対して即座にレスポンスを返すことができます。
スラッシュコマンドは3秒以内にレスポンスを返さないといけないというルールがあるので、まずはレスポンスを返します。
後続の処理は非同期で実行されます。

    await ack()

スラッシュコマンドを受け取ったらviews.openを叩きます。
このAPIではBlock Kitで作成されたViewを表示することができます。
submitに登録されているオブジェクトがクリックされると実際にsubmitされます。

submitされたフォームのデータはapp.viewで受け取ることができます。

  app.view(VIEW_ID, async ({ ack, view, context, body }) => {
    await ack()

    const values = view.state.values
    const keyword = values.keyword.keyword_input.value
    // ...
  })

views.openで設定されたcallback_idを検知して発火します。
フォームの中身はview.state.valuesの中にあります。
values以下のオブジェクト構造はviews.openで使用しているBlock Kit内で決めることができます。

  const keywordInput: InputBlock = {
    type: 'input',
    block_id: 'keyword',
    label: {
      type: 'plain_text',
      text: '検索ワード',
    },
    element: {
      type: 'plain_text_input',
      // action_idがvalues以下のプロパティとなる
      action_id: 'keyword_input',
    },
  }

余談: Block Kit

Block Kitを利用することで、複雑なUIを表現する事ができます。
Block Kitについては公式のドキュメントがわかりやすいです。

api.slack.com

また、Block Kit Builderというツールもあり、プレビューしながらUIを構築することが可能です。

まとめ

今回はBoltについて紹介させていただきました。
今回紹介しきれなかった機能もたくさんあるので、みなさんもぜひ触ってみてSlackアプリを作ってみてください。
改めて、Boltのメリットをまとめます。

  • Slack APIチームがメンテナンスしているので、信頼性が高い
  • 型安全にSlack APIを扱うことができる
  • Slack APIを簡単に使うための便利なユーティリティ
  • スラッシュコマンド用のレシーバーを簡単に生成できる

おまけ

Bolt + Firestore + Cloud Functions 成果物として、スラッシュコマンドからワード登録モーダルを開き、毎朝バッチで勉強会情報を通知してくれるサンプルを作ってみました。
よろしければ参考にしてみてください。

github.com

参考

※冒頭画像のSlackロゴは、公式サイトで配布されているものを使用しております。


◆TECH PLAY
techplay.jp

◆connpass
rakus.connpass.com

Copyright © RAKUS Co., Ltd. All rights reserved.