はじめに
この記事は Agent Grow Advent Calendar 2019 : 12日目 の記事です。
こんにちは、はじめまして。
福岡の隅っこの方でインフラメインに活動しているMarimon(まりもん)と申します。
弊社としてはたぶん珍しく、開発らしい開発はしていない人間です。
今回はSlack BoltというSlackがリリースしているAPIフレームワークの紹介です。
実践として簡単なキーワード反応するBotが使えるようになるまで、が今回のゴールです。
今更なChatOpsかもしれませんが、どうぞお付き合いください。
いきなり逸れますが、
2019年11月頃から順次Slackのエディタが改修(個人的には改悪)され
WYSIWYGというリッチテキスト形式になりましたね。
バックスラッシュ3つで囲うと勝手にコードブロックされるんですが、
最初の文字が1バイトしか認識せず日本語がおかしくなったり、コードブロックから抜けるのにEnter3回押したりと、
全然慣れないし、改悪にしか見えないのでなんとかしてくださいSlackさん!
※Issueは行っているようです
Slack Boltについて
さて改めて、Slack Boltとは、SlackさんがリリースしたSlackアプリ開発用のフレームワークです。
日本語のドキュメント もあって良きな感じがします。
Node.js + TypeScriptで実装されていて、Nodeのバージョンは10.0以上が必要になります。
例えばBotの作成であれば今までもHubotなどを利用してお手軽に(でもHubot入れるのはだるい)
色々やろうとするとそこそこ手間な感じで出来ていたのですが、
SlackさんもHubot のアプリを Bolt に移行する方法
というページを公式に用意する、という程度には本気度が伺えます。
Slack 「Hubotをぶっ壊す!(´ω`)」
ということで簡単なBotアプリを作ってみよう
まずは Slackアプリ を作成
- SlackのAPIページ からアプリを作ります。
今回は全然すごくない「sugoi-app」という名前を作ります。
ワークスペースはプライベートのものなので伏せています。
Botユーザ を作成
- 左メニューの「Bot Users」からBotユーザを作ります。
「Add a Bot User」をクリックします。
-
Botの設定は常時オンラインのところだけOnにしました。
Slackのワークスペースにアプリ(Bot)を登録
-
左メニューの「OAuth & Permissions」(もしくは「Install App」)からワークスペースにBotさんを登録します。
「Install App to Workspace」をクリックします。
-
こんな情報にアクセスしますよーという情報が表示されるので、「許可する」をクリックします。
-
登録が完了したらアクセストークンが表示されます。
認証情報を取得
-
前の画面でアクセストークンが表示されているので、「Bot User」のアクセストークンを控えます。
xoxb から始まるものを利用します。 -
Signing Secretを取得します。
左メニューの「Basic Information」-> 「App Credentials」にある 「Signing Secret」を控えます。
ひとまずSlackアプリ側はこれで一旦完了です。
Bot側の準備
適当なサーバの準備
Botを動かすにはなんらかサーバが必要になります。
詳しくないのですが「ngrok」や「Glitch」を使えばローカルでも簡単に公開できるようです。
ふつーに便利そうなので、今度使ってみたいです。特にGlitchイイネ!
今回は適当なサーバ(GCP@CentOS7)を利用しています。
AWSはEC2やLambdaでもOK、Herokuもいけるみたいです。
GCPのCloudFunctionは自分の設定が悪いのかうまくいきませんでした。
nodeの導入
最初からnode10以上が入っていれば特に作業は不要です。
今回は13.2.0を導入しました。
導入と言っても、パッケージをダウンロード、展開、パスを通す、だけです。
※すべて一般ユーザ権限で行っています、root権限は今回の作業では不要です
$ wget https://nodejs.org/dist/v13.2.0/node-v13.2.0-linux-x64.tar.xz $ tar Jxfv node-v13.2.0-linux-x64.tar.xz $ echo "export PATH=$PATH:~/node-v13.2.0-linux-x64/bin/" >> .bashrc $ source .bashrc
Boltプロジェクトの設定
- npm init で初期化します。
- author以外はEnter叩いたのみです。
$ mkdir sugoi-app $ cd sugoi-app/ $ npm init package name: (sugoi-app) version: (1.0.0) description: entry point: (index.js) test command: git repository: keywords: author: Marimon license: (ISC)
- 認証情報を環境変数として設定します。
export SLACK_SIGNING_SECRET=e73cXXXX6a7 export SLACK_BOT_TOKEN=xoxb-951831XXXfcvS
- Boltをインストールします。
npm install @slack/bolt
イベントの設定
このあたりからがポイントですが、BotアプリとSlackを連携する設定です。
ここでのイベントとは、Slackにログインしたり、発言したり、といったことを指します。
サーバ側にSlackと連携するBotスクリプトを設置
- app.jsを作成します。
- トークン情報を読み込んで、3000番ポートでListenするだけのものです。
$ vim app.js
const { App } = require('@slack/bolt'); // Initializes your app with your bot token and signing secret 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!'); })();
- アプリを起動します。
$ node app.js ⚡️ Bolt app is running!
SlackのAPP管理画面でイベント機能を有効化
- APP管理画面
- 左メニューの「Event Subscriptions」をクリックし、「Enable Events」を有効化します。
- 左メニューの「Event Subscriptions」をクリックし、「Enable Events」を有効化します。
- 先程起動したアプリのURLを「Request URL」に指定します。
- サーバのアドレスに3000ポートを指定します
- Boltアプリは/slack/events をリスニングするため、アドレスは以下のようになります
- http://example.com:3000/slack/events
- Verifiedとでなければ残念ながらどこか設定に誤りがあります。
- アプリが起動していない、起動に失敗している
- サーバのファイアウォールがブロックしている
- etcetc
反応するイベントを設定
ここの設定をしておかないと、どれだけ頑張ってBotを作ってもうまく動かないので注意です。
※先程と同じページ(左メニューの「Event Subscriptions」)に「Subscribe to bot events」という項目があります。
例えば
* DMでキーワードに反応する
* メンションの場合にキーワードに反応する
* 普通のチャンネルでキーワードに反応する
といったように、どのタイミング(イベント)で反応するかの設定が必要になります。
キーワードに反応するBotを作成
Slackのチャンネル、もしくはDMで「hello」とつぶやくと、返事をしてくれるBotを目指します。
アプリ側のコードを修正
- マニュアルページからまるっといただきました
const { App } = require('@slack/bolt'); const app = new App({ token: process.env.SLACK_BOT_TOKEN, signingSecret: process.env.SLACK_SIGNING_SECRET }); // Listens to incoming messages that contain "hello" app.message('hello', ({ message, say }) => { // say() sends a message to the channel where the event was triggered say(`Hey there <@${message.user}>!`); }); (async () => { // Start your app await app.start(process.env.PORT || 3000); console.log('⚡️ Bolt app is running!'); })();
- アプリを起動します。
$ node app.js ⚡️ Bolt app is running!
起動しました。
Slackでテスト
ようやくテストです。テストするまでが長かった・・・。
最初はBotユーザがSlackに表示されていないかもしれないので、アプリから追加します。
- Slackの左のメニューからAppを追加します。
-
sugoi-appを「表示」します。※チャンネルに追加する場合だと「追加」でした。
-
設定をクリックするとブラウザが開きます。
-
ブラウザに飛ぶので、再度Slackで開きます。
-
sugoi-appとDMができるようになりました。
-
テストしてみます。
とりあえず反応してくれたー!
あとは如何様にでもしてくれるな・・・。
ということで今回のテーマ的な最低ラインはこの辺まで。後はオマケです。ざっくりです。
おまけ1:ボタンを表示
Botの応答にボタンを表示してみます。
ボタンの表示やセレクトアイテム、メニューボタンを作ったりするのに
Slackブロックビルダー というのが用意されていて、
これが普段コードをあまり書かない人間からすると便利でした。
Slack 設定画面
- sugoi-appの設定画面「Interactive Components」を有効にします。
- Actionsを設定、「Callback ID」を設定します。
- Actionsを設定、「Callback ID」を設定します。
Bot側のコード修正
- コード側はマニュアルを参照しつつブロックビルダーから適当にポチポチと・・・
const { App } = require('@slack/bolt'); const app = new App({ token: process.env.SLACK_BOT_TOKEN, signingSecret: process.env.SLACK_SIGNING_SECRET }); // Listens to incoming messages that contain "hello" app.message('hello', ({ message, say }) => { // say() sends a message to the channel where the event was triggered say({ blocks: [{ "type": "section", "text": { "type": "mrkdwn", "text": `Hey there !` }, "accessory": { "type": "button", "text": { "type": "plain_text", "text": "Click Me" }, "action_id": "button_click" } }, { "type": "section", "text": { "type": "mrkdwn", "text": "Pick one or more items from the list" }, "accessory": { "type": "multi_static_select", "placeholder": { "type": "plain_text", "text": "Select items", "emoji": true }, "options": [{ "text": { "type": "plain_text", "text": "Choice 1", "emoji": true }, "value": "value-0" }, { "text": { "type": "plain_text", "text": "Choice 2", "emoji": true }, "value": "value-1" }, { "text": { "type": "plain_text", "text": "Choice 3", "emoji": true }, "value": "value-2" } ] } } ] }); }); (async () => { // Start your app await app.start(process.env.PORT || 3000); console.log('⚡️ Bolt app is running!'); })();
いざテスト!
- ハロー!
hello -> Click Me ボタンをクリックすると、自分宛てにMentionが飛んできました!
- セレクトボタンはどうかな?
なるほどねー。はー簡単。
おまけ2:/slashコマンドを作る
こっちも割と使うslashコマンドです。
Slackのメッセージ入力のところでスラッシュを入れるとたくさんコマンドが出てくる、アレです。
/ticket と入力するといい感じの画面が出てこないかなぁ?
Slack 側設定画面
- 設定画面
「Slash Commands」 というメニューがあるのでそこで作れます。
-
Commandを指定して、URLは同じものを指定するぐらいです
Bot側のコード修正
- 以下を追加しました。
app.action('button_click', ({ body, ack, say }) => { // Acknowledge the action ack(); say(` clicked the button`); }); // コマンド起動をリッスン app.command('/ticket', ({ ack, payload, context }) => { // コマンドのリクエストを確認 ack(); try { const result = app.client.views.open({ token: context.botToken, // 適切な trigger_id を受け取ってから 3 秒以内に渡す trigger_id: payload.trigger_id, // view の値をペイロードに含む view: { type: 'modal', // callback_id が view を特定するための識別子 callback_id: 'view_1', title: { type: 'plain_text', text: 'Modal title' }, blocks: [ { type: 'section', text: { type: 'mrkdwn', text: 'Welcome to a modal with _blocks_' }, accessory: { type: 'button', text: { type: 'plain_text', text: 'Click me!' }, action_id: 'button_abc' } }, { type: 'input', block_id: 'input_c', label: { type: 'plain_text', text: 'What are your hopes and dreams?' }, element: { type: 'plain_text_input', action_id: 'dreamy_input', multiline: true } } ], submit: { type: 'plain_text', text: 'Submit' } } }); console.log(result); } catch (error) { console.error(error); } });
いざテスト!
-
/ticket とタイプしてエンター!
-
Modalが表示されました
最後に
意外に簡単、意外に面倒、そんな感じでした。そこまで楽さは感じてません(笑
ただ、今回利用したコード類はほとんどマニュアルから頂いたものなのでコードも殆ど書いてないですし、ブロックビルダー もあって、簡単やりたいことができる気がしてます。
今回のものを応用して、
1. とあるサイトのRSSをSlackで読み込む
2. とあるキーワードがあったらMention通知&ボタン表示する
3. ボタンが押されるととあるアクションを起
ということを行ってます!プログラム初級者でも意外に楽にいけました。
そんなわけなので、誰でも簡単(とはいかないまでも)にChatopsの構築ができそうな感じですね!