Slack App + Botkit4 + TypeScriptでbotを作る


概要

以下の技術構成で、Single Teamな(一つのワークスペースでのみ稼働する)Botを実装する方法を紹介します。

  • Slack App
    • Event APIを使用します。
  • Botkit 4
    • Nodeはv12.7.0を使用します。
  • TypeScript

サーバへのデプロイはこの記事のスコープ外です、ローカルで立ち上げたBotプロセスとSlackが繋がるところまでを記載します。

Slack App

Slackに自作の機能を連携させる方法の最新版はSlack Appです。

Custom Integrationはdeprecatedなので、今から新しく作る場合はSlack Appを作りましょう。

Slack Appの種類

Slack App自体はSlackに機能を連携させるための汎用的な箱で、作成したSlack AppにBotユーザー機能だったりSlash Command機能だったりを追加してアプリケーションを作ります。

ちなみに、提供したい機能にとって本当にBotユーザーが必要かどうかは一度考えた方が良いと思います。
ただ単にSlackから何かしらの機能を実行したいだけであればSlach Commandで事足りることも多いと思います。

このページ書きながら「あ、実装しようとしてた機能、Slach Commandでいいじゃん」って思った。

Botkit 4

Botkit4のSlackに関する公式ドキュメントはこちら

左上のBotkit versionを変更すると古い書き方も見れます。

Botkit4での書き方とAdapter

Botkit4からは、Botの機能をAdapterという単位に切り出し、必要なAdapterをimportして使う方法になっています。
SlackのBotを作る際に必要なアダプタは botbuilder-adapter-slack です。

このアダプタには以下の2種類のモードが存在します。

  1. Single Teamモード
    • 単一のワークスペースでのみ使えるモード。
    • 社内のSlackでだけ使えるBotとかの場合はこっち。
  2. Multi Teamモード
    • 複数のワークスペースで使えるモード。
    • サービスとして色んなワークスペースで使ってもらう想定の場合はこっち。

今回の目的は1なので、1の方で書きます。

1. Slack Appを作成する

1.1 Slack Appを作成する

にアクセスし、 Create New App ボタンを押します。
アプリケーションの名前と使用するワークスペースを選択します。

Signing Secretをコピーする

そうすると App Credentials という欄に色々重要っぽい値が払い出されてるのが分かります。
その中の Signing Secret の値をコピーしておきます。

1.2 Bot Userを作成する

さて、Botを作るためにまずはBotユーザーを追加しましょう。

下記の画面で、Slack Appに機能を追加できるので、 Botsをクリックします。
(この画像はこの作業が完了した後にスクショしたので既に緑になっていますが、最初は全て灰色のはずです)

このボタンを押す。

と、こんな画面になるので、Botの名前を入れましょう。
今回の実装ではRTM APIを使わないのでAlways Show My Bot as OnlineはONで良いです。

saveが完了したら、このBot Userをワークスペースに招待します。
下記のメニューをクリックで開いて、Install App to Workspaceボタンを押します。

そうするとよくあるOAuthログインみたいな感じの画面が開くので、「許可する」を押します。

この時点でワークスペースにBot Userが追加された状態になるので、確認しましょう💡

これは使うチャンネルにinviteした図です。Bot Userとして参加してくれてます🤖

Bot Tokenをコピーする

ここで作成したBotに色々喋らせるので、このBotを識別する必要があります。
そのために、Permissionsをクリックし、表示される Bot User OAuth Access Token の値をコピーしておいてください。
二つ出てくると思うのですが、xoxb-から始まる方です。

こいつをBotkit的には Bot Token と呼びます。

2. TypeScriptの用意をする

ではやっとコード書き始めます。
もう型のないjsなんて書けないよ!ということでTypeScriptで書きます。

$ npm install --save-dev typescript

Botkitのコードの場合、サーバのnodeで走らせるのでwebpackとかは不要です。

TypeScriptのコンパイルに必要なtsconfig.jsonを作ります。

tsconfig.json
{
  "compilerOptions": {
    "module": "esnext",
    "target": "esnext",
    "moduleResolution": "node"
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

tsファイルはsrc/配下に置いて、dist/にjsファイルを吐き出すとしましょう。

package.json
"scripts": {
  "start": "node dist/index.js",
  "build": "npx tsc --outDir ./dist ./src/index.ts"
}

こんな感じ。

これでTypeScriptの準備は終わりです。

3. Botkitのコードを書く

$ touch src/index.ts

$ npm install --save botkit botbuilder-adapter-slack
index.ts
import { Botkit } from "botkit";
import { SlackAdapter, SlackEventMiddleware } from "botbuilder-adapter-slack";

const adapter = new SlackAdapter({
  clientSigningSecret: process.env.CLIENT_SIGNING_SECRET,
  botToken: process.env.BOT_TOKEN,
  // Single Teamモードでは不要なパラメータなのですが、型で必須と言われてしまうので仕方なく書いてます。
  redirectUri: ""
});
// Slackのイベントを受け取れるようにします。
adapter.use(new SlackEventMiddleware());

const controller = new Botkit({
  adapter: adapter,
});

controller.on("app_mention", async (bot, message) => {
  await bot.reply(message, "I received an app_mention event.");
});

ここで、Slack Appを作った時にコピーしていた2つの値を使います。
Signing SecretBot Tokenの二つです。

僕の場合はdirenvを使っているので.envrcに追記します。

export CLIENT_SIGNING_SECRET=hogehoge
export BOT_TOKEN=piyopiyo

4. コンパイルして走らせる。

package.jsonに記載済みなので、

$ npm run build
$ npm start

だけです。

ちなみにここでSigning Secretが空だったりすると

****************************************************************************************
* WARNING: Your bot is operating without recommended security mechanisms in place.     *
* Initialize your adapter with a clientSigningSecret parameter to enable               *
* verification that all incoming webhooks originate with Slack:                        *
*                                                                                      *
* var adapter = new SlackAdapter({clientSigningSecret: <my secret from slack>});       *
*                                                                                      *
****************************************************************************************
>> Slack docs: https://api.slack.com/docs/verifying-requests-from-slack

こういうWarningが出たり、Bot Tokenが間違ってたりすると

Error: An API error occurred: invalid_auth

というエラーが出たりします。

Enabling plugin:  Slack Adapter
Webhook endpoint online:  http://localhost:3000/api/messages

だけ出ていれば成功です。

5. ngrokでローカルのサーバを公開

さて、これでBotのサーバが立ち上がってる状態なわけなんですが、Event APIでの実装なので、実際に試すにはこのサーバの公開されたエンドポイントが必要です。
でも、ローカルで立ち上げている場合はそんなものあるはずもなく...

ということでngrokを使いましょう。
日本語だと、こちらのmininobuさんの記事で概要が掴めます。

ngrokの導入は他の方の情報にお任せするとして、先ほどのサーバを立ち上げた状態で、別プロセスでngrokします。

$ ngrok http 3000

そうするとローカルのマシンのポート3000番が公開されてURLが作成されます、最高〜〜〜

6. Event SubscriptionsをONにする。

ここで一度Slack Appに戻ります。

RTM APIとEvent API

SlackのBotの実装方法は何種類かあり、今回はEvent APIを使用する方法で作成します。
これについてはnamutakaさんのこちらの記事を読むと理解しやすいです。

また、Slackの公式のブログで以下のようにEvent APIの使用を推奨しています。

Generally, we recommend using the Events API, except for in a few specific cases (for instance, for an internal integration behind a firewall). When it comes down to it, the RTM API sends a lot of unnecessary information to your server over the connected WebSockets. This is particularly important if you have multiple connected clients, or just a large team using your app.

6.1. SlackにURLを登録する

さて実際の操作としては、まずEvent Subscriptionsをクリックし、

Enable EventsをONにします。

そうするとURLを入力するフィールドが出てくるので、先ほど立ち上げたngrokのURLに、パスを追記したものを入力します。
URLを入力すると自動でリクエストが飛んで、正しいURLか判定されます。
この際に飛ぶイベントはurl_verificationイベントで、本当なら実装して適切なレスポンスを返す必要があるんですが、Botkitはもうそこは実装されているようで、コードに何も書かなくてもVerified!になります👏

6.2. イベントを登録する

今回はBotがメンションされた場合のイベントをSubscribeするとして、Subscribe to bot eventsapp_mentionを選びましょう。

これでSave Changesボタンを押してとりあえず設定完了。

ちなみに、ngrokのプロセスは立ち上げ直すとURLが変わるので、この設定を再度する必要があります。
ローカルで検証を続けたい場合はngrokのプロセスは殺さないでください。

7. Slackから確認

さてここまで終わると実際にSlackで試すことが出来ます。

わーい!

ここまで来ればもう後はなんでも出来ますね。

手順としては、Slack Appの設定でSubscribeするイベントを設定して、コード上の controller.on に処理を書くという感じです。
Event APIで発火されるイベントはSlack上のだいたいのアクティビティをカバーするので、基本的にSlackでやりたいと思ったことは何でも出来るんじゃないでしょうか。

おしまい

なんだかんだそれなりにステップがある上に、SlackもBotkitも新旧の情報がネットに混在しているので中々に惑わされました。
良いBotライフを送りましょう!!

僕はこれから本来実装する予定だった機能をBotではなくSlash Commandで作りますが.........