リモートワークも増えSlackなしでは業務が円滑に進まないという会社も増えてきています。そんなユーザの人の中から毎日使っているSlackだから何か別の用途でSlackを利用することで業務を効率化できないだろうかと考える人もいるかと思います。そんな人のためにSlackでは他のアプリと連携を行うためのAPIを公開しています。APIを一般に公開してくれているおかげでGoogleやTrello, Dropox, Boxなどのメジャーなアプリとの連携だけではく自社のアプリケーションとも連携することができます。

ここ数年で以前よりもSlackのAPI開発の敷居も下がり比較的簡単にインタラクティブなUIを持つ機能を追加することも可能です。

Slackの開発をチャレンジしてみたい人、また数年前にIncoming Webhooksで通知機能は実装したけどそれ以降は何もSlack開発をしてないという人、SaaSのサービスを使ってるけどSlack開発で自社用の同程度のサービスを作れないだろうかという人向けに本書を公開しています。

本文書では外部からSlackへの外部からのメッセージ送信からサーバを使ったユーザとのインタラクティブなアプリ開発の基礎まで一から説明を行っています。

Slackを今以上に活用できるようにぜひ本文書を参考にアプリ開発にチャレンジしてみてください。

本文書を利用してアプリ開発を行うためにはJavaScriptの知識が必要となります。node,npmもインストールしておく必要があります。本書ではSlack上でのユーザ間のメッセージ送信、Slackの通常の利用方法についての説明は一切行っておりません。

Slackのアカウント登録

Slackは無料の場合は検索できるメッセージの件数や外部アプリとの連携数、複数でのビデオ通話などいくつか制限がありますが無料の範囲で十分に業務に活用させることができます。

Slackを利用するためにはアカウント登録(ワークスペースの作成)が必要となるためSlackのサイトにアクセスして右上にある”無料で試してみる”ボタンをクリックしてください。

Slackトップページ
Slackトップページ

Slackで利用するメールアドレスの入力画面が表示されます。英語で表示されている場合は”Change region”(地域を変更)をクリックすることで日本語に変更することができます。

言語の切り替え
言語の切り替え

Asia Pacificの日本(日本語)を選択してください。

言語を選択
言語を選択

日本語が表示されるので、業務で利用する場合は仕事用のメールアドレスを入力してください。(Slack推奨)

メールアドレスを入力する
メールアドレスを入力する

メールアドレスを入力して”続行する”ボタンを押すとコードの入力画面が表示されます。

コードの入力画面
コードの入力画面

登録したメールに6文字のコードが送信されてくるのでそのコードを入力してください。

6桁の文字コード
6桁の文字コード

メールに表示されている6文字のコードを入力するとワークスペースの新規作成画面が表示されるので”ワークスペースを作成する”ボタンをクリックしてください。

ワークスペースを作成する画面
ワークスペースを作成する画面

ワークスペースの名前を入力してください。会社名または部署名などを入力します。

ワークスペースの名前を入力
ワークスペースの名前を入力

ここで入力する名前は、ワークスペースで利用するチャンネルの名前になります。後ほど変更することが可能です。

チャネルの入力画面
チャネルの入力画面

Slackを利用する場合は会社または部署のメンバーとメッセージをやりとりすることになるのでメッセージをやりとりする予定の他のメンバーのメールアドレスを入力してください。この手順はスキップすることが可能なのでスキップする場合は”この手順をスキップする”をクリックしてください。

他のメンバーのメールアドレスを入力
他のメンバーのメールアドレスを入力

スキップする場合は下の画面が表示されるので”ステップをスキップ”をクリックしてください。

メンバー入力をスキップする
メンバー入力をスキップする

以上でSlackの新規アカウント(ワークスペース)の作成は完了です。2番目のステップで入力した名前がチャンネルの下に作成されていることが確認できます。

Slackのワークスペースの作成完了
Slackのワークスペースの作成完了

ユーザのメールアドレスのみでパスワードを設定しませんでしたがパスワードなしでもサインインの画面でメールアドレスを入力すると6桁の文字列が登録したメールアドレス向けに送信されてくるのでサインインすることができます。パスワードも設定することは可能です。

Slackのアプリケーションを作成する

Slackで最も行われている開発の一つが外部のアプリケーションからSlackに向けて行う通知(メッセージ送信)です。外部アプリケーションから一方的に通知を行うのみなのでSlack側から外部アプリケーションにメッセージを送ることはありませんSlackの開発を行う上での基礎となるのでまずはメッセージ送信機能を作成してみましょう。

Slackではメッセージ送信機能を作成する場合でも必ずslack apiでアプリを作成する必要があります。作成というよりもアプリの登録という表現の方が正しいかもしれません。

ここでいうメッセージ通信の例では購買申請、休暇申請などを外部のアプリケーションで行った場合に申請が完了したら申請完了メッセージが申請者にSlackで通知されるといったものがあります。
fukidashi

slack apiでアプリケーションを作成し外部からメッセージを送信してslackのユーザが受信するまでに行う処理の流れは下記の通りです。外部からメッセージを送信し、送信したメッセージをSlack上のユーザが受信するだけのアプリであれば初めての人でも簡単に行うことができます。

  • slack apiでのSlack Appの作成
  • Permissions(権限)の設定
  • 作成したアプリのワークスペースへのインストール
  • Tokenの取得(自動で作成されます)
  • curlコマンドによりメッセージの送信
メッセージ送信には本書でも説明していますがImcoming Webhookでも行うことができます。Incoming Webhookの場合はTokenを利用せずSlackが発行する一意のURLに対してPOSTリクエストを送信します。
fukidashi

Slack appの作成

Slackでアプリケーションを開発するためにはSlackにアプリケーションの登録を行う必要があります。アプリケーションを作成するためにslack apiの画面にアクセスします。slack apiが開いたら”Create a custom app”ボタンをクリックします。

Slack API
Slack API

アプリケーション名の入力とワークスペースの選択画面が表示されるので任意のアプリケーション名と先ほど作成したワークスペースを選択してください。ここではFirst Appという名前をつけています。入力が完了したら”Create App”ボタンをクリックしてください。

アプリケーション名の入力
アプリケーション名の入力
1つのアカウントで複数のワークスペースに所属することができるのでワークスペースの選択が必要となります。
fukidashi

作成が完了すると作成したアプリケーションに関する画面が表示されます。

作成したアプリの管理画面
作成したアプリの管理画面

Permissions(権限)の設定

Slackのアプリを動作させるためにはアプリが行える処理の範囲を決めるためScopeを設定する必要があります。Scopeを設定しなければ作成したアプリは何も行うことができません。先ほどの画面の”Permissions”を選択してください。

Permissionを選択
Permissionを選択

Permissionsをクリックすると”OAuth & Permissions”の画面が表示されます。スクロールして”Scopes”を表示してください。

OAuth & Permissions画面
OAuth & Permissions画面

ScopesではBot TokenかUser TokenのScopesを選択することができます。User Tokenを選択するとアプリを作成したユーザとして与えられた権限内で処理を行うことができます。Botを選択するとユーザではなくBotユーザとして与えられた権限内で処理を行うことができます。本文書ではBotとして処理を実行させるのでBot Token Scopesのみ利用します。

Scopesの設定画面
Scopesの設定画面

Botに対してchat:writeというScopeを与えて動作確認を行います。char:writeのScopeではチャンネルやユーザに対して外部からメッセージを送信するメソッド(chat.postMessage)をBotが利用することができます。

BotにScopeを与える
BotにScopeを与える

Scopeを選択すると選択したScopeが表示されます。Scopeは複数選択することも可能で削除したい場合はゴミ箱のアイコンをクリックしてください。

Scopeの設定
Scopeの設定

アプリのワークスペースへのインストール

作成したアプリをワークスペースにインストールします。インストールしなければアプリを利用することはできません。インストールと聞くとなにか大袈裟なものに聞こえてきますが、ワークスペースで作成したアプリを有効化するものだと思ってください。

アプリの作成(create)、インストール(install)という言葉をslackでは利用していますがそれよりもアプリの登録、登録したアプリのワークスペースでの有効化と理解したほうがわかりやすいと思います。
fukidashi

サイドバーにある”Basic Information”をクリックします。BotにScopeを設定したことで”Add features and functionality”にチェックが入っていることがわかります。

Install your appから”Install to Workspace”をクリックします。

Basic Informationの画面
Basic Informationの画面

作成したアプリケーションがワークスペースにアクセスする権限を許可するかどうか確認画面が表示されるので”許可する”ボタンをクリックしてください。

ワークスペースへのアクセス許可
ワークスペースへのアクセス許可

アプリケーションへのインストールが完了するとInstall your appにチェックが入ります。

アプリケーションのインストール完了
アプリケーションのインストール完了

Slackへのアプリのインストールが完了するとSlackの画面に追加したアプリの情報が表示されていることが確認できます。

追加したアプリが表示される
追加したアプリが表示される

Tokenの取得、ユーザへのメッセージの送信

アプリのワークスペースへのインストールが完了したらcurlコマンドを利用してメッセージを送信してみましょう。メッセージの送信にはchat.postMessageメソッドを利用します。

botを利用する場合はscopeでchat:writeを設定しておく必要があります。(本書では設定済)。chat.postMessageメソッドについては詳細はhttps://api.slack.com/methods/chat.postMessageから確認を行ってください。

curlコマンドでメッセージを送信する際は以下の通りとなります。channelとありますがここでは@ユーザ名でユーザに対してメッセージを送信しています。


 % curl -X POST -H 'Authorization: Bearer xoxb-XXX..XX' -H 'Content-type: application/json;charset=UTF-8' --data '{"channel":"@username","text":"Hello"}' https://slack.com/api/chat.postMessage
チャンネルではなくユーザに直接メッセージを送信する場合は@(アットマーク)の後にユーザ名を指定してください。
fukidashi

Bearerの後ろに入っている値はTokenで各自の環境で異なり、OAuth & PermissionsのBot User OAuth Access Tokenから取得します。Tokenは値がわかると他の人も利用することができるので公開しないように注意してください。

Token(トークン)が聞き慣れない人であればSlackにアクセスするための秘密の鍵だと考えてください。この秘密の鍵を知っていなければ外部からSlackにアクセスすることはできません。秘密の鍵なので他の人に見せてはいけません。
fukidashi
Tokenの確認方法
Tokenの確認方法

curlコマンドが利用できない環境の場合は動作確認用のtestページTesterをslack側で用意しているのでそこからメッセージを送信することができます。tokenなど必要な項目を入力して実行してみださい。

https://api.slack.com/methods/chat.postMessage/test

postMessageメソッドのTester
postMessageメソッドのTester

動作確認のため数回メッセージを送信していますがchannelで指定したユーザのSlack画面では送信したメッセージが表示されています。

SlackのBotからのメッセージ
SlackのBotからのメッセージ

chat.postMessageメソッドにより外部からSlackにメッセージが送信できることが確認できました。

Incoming Webhooksによる送信

postMessageメソッドを利用してcurlコマンドで外部からSlackのユーザにメッセージを送信することができました。外部からのメッセージ送信にはIncoming Webhooksという方法で行うことも可能です。

Basic Informationの画面からAdd features and functionalityからIncoming Webhooksを選択します。

Incoming Webhooksを選択
Incoming Webhooksを選択

Incoming WebhooksはデフォルトではOffになっているのでOnに変更します。

Incoming Webhooks OFF
Incoming Webhooks OFF

ONにするとWebhook URLs for Your Workspaceが表示され”Add New Webhook to Workspace”ボタンが表示されます。ボタンをクリックします。

Incoming WebhooksをON
Incoming WebhooksをON

First AppでIncoming WebhooksをONにしたので権限の確認画面が表示されます。Incoming Webhooksではどのチャンネル、ユーザ、Botにメッセージを送信するか選択することができます。ここではアカウント作成時に追加したチャンネルの”slack新規導入プロジェクト”を選択して”許可する”ボタンをクリックします。

WorkspaceへのIncoming Webhooksの追加確認
WorkspaceへのIncoming Webhooksの追加確認

Webhookを利用してメッセージを送信する場合はトークンではなく表示されているWebhook URLに対してPOSTリクエストを送信することでメッセージを送信することができます。curlコマンドのサンプルコマンドも表示されているのでコピーをしてコマンドラインで実行してください。何も変更することなくチャンネルにメッセージを送信できることが確認できます。

WebhooksのURLが表示
WebhookのURLが表示

postMessageメソッドとWebhookはどちらもcurlコマンドで外部からメッセージを送信することができましたがここまでの動作確認だけでも以下の違いが確認できました。

  • postMessageメソッドはアプリのScopeを設定し、メッセージを送信する際にTokenが必要となる。
  • postMessageメソッドは送信時に送信するチャンネルを指定することができるがWebhookの場合はワークスペースに追加する際にチャンネルを指定する必要がある。
  • Webhookの場合はTokenを利用せずSlackによって生成される一意のWebhook URLに対してメッセージを送信する必要がある。

決められたチャンネルにメッセージを送信したいのであればWebhookの方が簡単に設定を行うことができます。

Slackでの開発

Slackで開発を行う際に参考になるドキュメントはhttps://slack.dev/で公開されています。こちらに公開されているドキュメントを元に開発を行っていきます。

JavaScript、Python, Javaの3つの言語のSDKがSlackから提供されているのでいずれかを利用することで開発を効率的に行うことができます。本文書ではJavaScriptで開発を進めています。

WebhookやpostMessageで動作確認を行いましたが2つの処理は外部からSlackに対してHTTPリクエストのPOSTメソッドでメッセージを送信するという処理でした。ここまでの処理とは逆のSlackから外部に対してHTTPリクエストを送信することができます。HTTPリクエストを受け取るためにはサーバが必要となります。開発の段階ではサーバは決まっていない場合はローカルで開発を行う必要があります。ローカルで開発を行うためにSlackの公式ドキュメントで紹介されているngrokを利用します。

ngrokをローカルのPCで実行するとngrokから一意のURLから提供されます。ngrokのURLとローカルPCのポートが紐づけられngrokインターネット上からそのURLにアクセスを行うとローカルで起動したサーバに直接アクセスを行うことができます。ngrokは仲介(proxy)の役割をします。ngrokのURLはインターネット上に公開されるURLなのでインタネットに繋がっていれば誰でもアクセスすることができます。そのURLをSlackに教えることでSlackはそのURLに向けてHTTPリクエストを送信し、ngrokを経由してローカルのサーバがリクエストを処理することができます。

ngrokのアカウントの作成と設定

ngrokを利用するためにはアカウントの作成が必要となります。アカウントを作成するためngrokのサイトにアクセスします。ngrokはフリープランが準備されているので無料で利用することができます。サインアップ(アカウント作成)するために”Get started for free”ボタンをクリックしてください。

ngrokのトップページ
ngrokのトップページ

Github、Googleアカウントまたはメールアドレスでアカウントの作成を行うことができます。

メールアドレスの入力
メールアドレスの入力

アカウントの作成が完了するとアプリケーション(バイナリの実行ファイル)のダウンロードを行う必要があります。アカウント作成後にはダッシュボードにmacOS用のダウンロードリンクが表示されます。本文書ではmacOSを利用しているためです。

ダッシュボード画面
ダッシュボード画面

アプリケーションのダウンロードを行ってください。アプリケーションはzipコマンドに入っているので解凍する必要があります。解凍するとバイナリの実行ファイルであるngrokファイルが作成されます。

任意の場所にフォルダを作成して解凍したngrokファイルを移動していください。ここではDesktopにslack_devフォルダを作成してngrokファイルを保存しています。

ダッシュボードの説明に記載されている通りngrokコマンドを実行してngrokのアカウントとの接続を行います。各自異なるauthtokenが表示されているのでページからコピー&ペーストしてngrokを保存した場所からコマンドを実行してください。

ngrokコマンドでアカウントとの接続
ngrokコマンドでアカウントとの接続

実行すると”ngrokは、開発元を検証できないため開けません”のメッセージが表示された場合は”キャンセル”ボタンをクリックしてください。

開発元を検証できないため開けませんエラー
開発元を検証できないため開けませんエラー

キャンセルボタンをクリック後にシステム環境設定→セキュリティとプライバシーの一般タブを開いて”このまま許可”ボタンをクリックしてください。

セキュリティとプライバシーの一般タブ
セキュリティとプライバシーの一般タブ

再度実行すると”ngrok”の開発元を検証できません。開いてよろしいですか?と表示されるので”開く”ボタンをクリックしてください。

実行するとユーザのホームディレクトリ直下に隠しフォルダ.ngrokにngrok.ymlが作成されます。


Authtoken saved to configuration file: /Users/ユーザ名/.ngrok2/ngrok.yml

作成されたファイルには指定したauthtokenが保存されています。

これから動作確認のローカルのポート3000を利用するのでngrokへのアクセスがローカルのポート3000に転送される(forwarding)ようにngrokを起動します。


ngrok by @inconshreveable                                                                                                                  (Ctrl+C to quit)
                                                                                                                                                           
Session Status                online                                                                                                                       
Account                       slack_dev (Plan: Free)                                                                                                     
Version                       2.3.35                                                                                                                       
Region                        United States (us)                                                                                                           
Web Interface                 http://127.0.0.1:4040                                                                                                        
Forwarding                    http://514aXXXXXXX.ngrok.io -> http://localhost:3000                                                                        
Forwarding                    https://514aXXXXXXX.ngrok.io -> http://localhost:3000                                                                       
                                                                                                                                                           
Connections                   ttl     opn     rt1     rt5     p50     p90                                                                                  
                              0       0       0.00    0.00    0.00    0.00   

http://514aXXXXXXX.ngrok.ioへのアクセスがhttp://localhost:3000に転送される設定になっていることが確認できます。

ngrokのURLにブラウザからアクセスするとローカルのポート3000で起動しているサーバは何もないためエラーが表示されます。

接続エラーメッセージ
接続エラーメッセージ

Expressサーバによる動作確認

Slackの開発の理解を深めるためにまずはExpressサーバを利用します。Expressサーバで動作確認後、Slackから提供されているBoltというJavaScriptフレームワークを利用します。

Expressサーバを設定してローカルのポート3000で起動させます。

npm init -yコマンドをslack_envフォルダで実行します。slack_envフォルダにはpackage.jsonファイルが作成されます。


 % npm init -y  

Expressのパッケージをインストールします。


 % npm install express

index.jsファイルを作成しポート3000でExpressサーバは起動し、ルートにGETリクエストが来た場合はHello World!を返します。


const express = require('express');
const app = express();
const port = process.env.PORT || 3000;

app.get('/', (req, res) => res.send('Hello World!'));

app.listen(port, () => console.log(`listening on port ${port}!`));

index.jsを作成したらnodeコマンドでExpressサーバを起動します。


 % node index.js 
listening on port 3000!

再度ブラウザからhttp://514aXXXXXXX.ngrok.ioにアクセスを行います。Hello World!が表示されればngrokからポート3000に転送、ブラウザへのExpressサーバからのレスポンスが正常に返されていることが確認できます。

Hello World!表示
Hello World!表示

Slackからのイベントの取得

Expressサーバの動作確認が完了したのでSlackからサーバに向けて送信されるHTTPリクエストの処理方法を確認していきます。

Slackからのイベントを取得できるように設定を行っていきます。これまで外部からSlackに向けての動作を確認できていましたがここでようやくSlackから外部向けに送信されるメッセージを取得できるかの確認を行います。

イベントをサーバ側でリスニングできるように@slack/events-apiパッケージをインストールします。リスニングとはSlackから送信されてくるHTTPリクエストを待ち受けておくことを言います。


 % npm install @slack/events-api

@slack/events-apiパッケージに入っている検証サーバをコマンドを利用して起動することで動作確認を行います。ポート3000(ngrokでフォーワードしているポート)でイベントをリスニングしますが実行するためにはsigning_secretが必要となります。このsigninig_secretを利用することでSlackからメッセージが送信されてきていることを検証します。


./node_modules/.bin/slack-verify --secret <signing_secret> [--path=/slack/events] [--port=3000]
デフォルトではpathは/slack/events、ポートは3000に設定されるため変更が必要ない場合は–path、–portオプションをつけて実行する必要はありません。
fukidashi

signing_secretはSlack APIのBasic InformationのApp Credentialsから取得することができます。

Signing Secretの取得
Signing Secretの取得

Siginig Secretを設定して検証サーバを起動します。signing_secretにはslack apiのApp Credentialsから取得した値を設定してください。環境により異なります。


 % ./node_modules/.bin/slack-verify --secret siginig_secret
The verification server is now listening at the URL: http://:::3000/slack/events
Expressサーバと同じポート3000を利用して検証サーバは起動するので検証サーバを起動する際はExpressサーバを停止してください。停止していない場合はポート3000が利用中なので起動に失敗します。
fukidashi

検証サーバの起動が完了したら、slack api側でイベントの設定を行います。イベントの設定はslack apiのダッシュボードから行います。デフォルトではEventsはOffになっているのでOnに変更する必要があります。

デフォルトではEVentはOff
デフォルトではEVentはOff

OnにするとRequest URLの入力欄が表示されるのでngrokのURLを入力します。URLには検証サーバ時にデフォルトで設定されているpathの/slack/eventsも設定してください。pathオプションを起動時に変更した場合は変更した値を設定してください。

Onの後はRequest URLを設定
Onの後はRequest URLを設定

URLを入力するとchallengeパラメータの送信が行われて、入力したURLからレスポンスをもらう必要がありまs。正常に動作しない場合は下記のように表示されます。

検証にエラーが発生
検証にエラーが発生

正常に動作した場合はverifiedが表示されます。verifiedが表示されればサーバ側でslackからのHTTPリクエストを受け取ることができるのでイベントの取得の動作確認に進めます。

検証に成功した場合
検証に成功した場合

Expressサーバでイベントを取得

ExpressサーバにおけるEvents APIの設定方法についてはslack.devのEvents APIに記述されているのでその設定を参考にしています。


const { createServer } = require('http');
const express = require('express');
const bodyParser = require('body-parser');
const { createEventAdapter } = require('@slack/events-api');
const slackSigningSecret = process.env.SLACK_SIGNING_SECRET;
const port = process.env.PORT || 3000;
const slackEvents = createEventAdapter(slackSigningSecret);

const app = express();

app.use('/slack/events', slackEvents.requestListener());

app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json())

app.get('/', (req, res) => res.send('Hello World!'));

slackEvents.on('message', (event) => {
  console.log(`Received a message event: user ${event.user} in channel ${event.channel} says ${event.text}`);
});

const server = createServer(app);

server.listen(port, () => console.log(`listening on port ${port}!`));

slackEvents.onメソッドでmessageイベントを取得しています。messageイベントを取得するためにはBotがmessageイベントを取得できるようにイベントの設定をslack apiのダッシュボード上で行う必要があります。Event SubscriptitonsのページのSubscribe to bot eventsでmessage.imイベントを追加してください。message.imイベントを設定することでFirst Appアプリにユーザからメッセージを送信するとSlackからメッセージがExpressサーバ側に送信されます。

イベントの設定
イベントの設定

message.imを追加するとアプリに対する権限を設定し直す必要がありScopeの追加も必要となります。message.imで必要となるScopeはim:historyのためScopeの設定画面に移動します。

Scopeの設定はOath & Permissionsのページから行うことができます。message.imを”Subscribe to bot events”の設定で追加すると自動でScopeにim:historyが追加されます。追加したらアプリのワークスペースへの再インストールが必要となります。

im:historyのScopeへの追加
im:historyのScopeへの追加

ワークスペースへのアプリの再インストールはBasic Informationのページのみで行えるわけではありませんが今回はBasic Informationの”Reinstall to Workspace”ボタンをクリックします。

アプリの再インストール
アプリの再インストール

再インストールする際は必ず許可の確認が表示されるので”許可する”ボタンをクリックしてください。

アプリのインストールの確認
アプリのインストールの確認

Slack上でのイベントの設定は完了です。本当にイベントが発生し、Expressサーバ側でmessageイベントを取得できるのか確認するためにSlackの画面上からFirst App(アプリ)に対して直接メッセージを送信します。”ベントをExpressサーバで受け取れるかの確認です。”を送信しています。

First Appにメッセージを送信
First Appにメッセージを送信

index.jsファイルではmessageイベントを取得するとコンソールにメッセージを表示できるように設定しているのでExpressサーバを起動したターミナルにメッセージが表示されてればSlackからイベントメッセージがngrok経由でExpressサーバに送信され、正常に受信できていることが確認できます。


% SLACK_SIGNING_SECRET=XXXXXXX node index.js
listening on port 3000!
Received a message event: user U01NX4Q7P7F in channel D01NKM0M78X says イベントをExpressサーバで受け取れるかの確認です。

もし受信できない場合はngrokのInspectを利用してSlack側からHTTPリクエストが来ているのか確認することもできます。

ngrokのInspectはlocalhost:4040/inspect/httpからアクセスすることができます。
fukidashi

イベントの設定の間違いやイベントを発生させるための処理に誤りがある場合はリクエストには何も表示されません。(HTTPリクエストがSlackから送信されてこないため)

ngrokのInspectでslackからのHTTPリクエストを確認
ngrokのInspectでslackからのHTTPリクエストを確認

イベントがSlackから送信し、ngrokが受信している場合は/slack/eventsにPOSTリクエストが来ていることがわかります。リクエストの中身の詳細もページ右側の部分で確認することができます。

Expressサーバからメッセージの送信

SlackからExpressサーバに対してメッセージの送信が確認できたので今後はExpressサーバからSlackに対してメッセージを送信します。

本文書ですでに確認済のchat.postMessageでメッセージを送信するために@slack/web-apiパッケージのインストールを行う必要があります。今回はcurlではなくExpressサーバのコード内でchat.postMessageメソッドを実行します。


 % npm install @slack/web-api

index.jsファイル内ではBotとしてメッセージを送信することになるのでBot User OAuth Access Tokenが必要となります。コードではBot User OAuth Access TokenをSLACK_BOT_TOKENに設定しています。

Tokenの確認方法
Tokenの確認方法

インストールした@slack/events-apiパッケージからWebClientクラスをimportしてSLACK_BOT_TOKENを引数としてインスタンス化しています。メッセージの送信はweb.chat.postMessageメソッドを使って送信するメッセージをtext、送信したいチャンネルに’slack新規導入プロジェクト’に設定しています。


const { createServer } = require('http');
const express = require('express');
const bodyParser = require('body-parser');
const { createEventAdapter } = require('@slack/events-api');
const { WebClient } = require('@slack/web-api'); //追加
const slackSigningSecret = process.env.SLACK_SIGNING_SECRET;
const token = process.env.SLACK_BOT_TOKEN; //追加
const port = process.env.PORT || 3000;
const slackEvents = createEventAdapter(slackSigningSecret);
const web = new WebClient(token); //追加

const app = express();

app.use('/slack/events', slackEvents.requestListener());

app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json())

app.get('/', (req, res) => res.send('Hello World!'));

slackEvents.on('message', async (event) => {
  await web.chat.postMessage({
    text: `受け取ったメッセージはこちらです。"${event.text}"`,
    channel: 'slack新規導入プロジェクト',
  });
});

const server = createServer(app);

server.listen(port, () => console.log(`listening on port ${port}!`));

Bot(First App)を”slack新規導入プロジェクト”に招待していない場合は”slack新規導入プロジェクト”のメッセージ一覧でFirst Appに対して”@First App test”でメッセージを送信してくささい。紹介されていない場合はFirst Appを招待するかどうか聞かれるので”招待する”をクリックしてください。

Slackの画面上でFirst Appに対してメッセージを送信するとチャンネルslack新規導入プロジェクトに新規のメッセージがFirst Appアプリから送られてくれば正常に動作しています。

Expressサーバからのメッセージを表示
Expressサーバからのメッセージを表示

First Appにメッセージを送信するとmessageイベントが発行されExpressサーバでmessageイベントを検知します。messageイベントを検知後にExpressサーバからpostMessageメソッドでチャンネル”slack新規導入プロジェクト”対してメッセージを送信しています。

何か設定の問題等でエラーが発生した場合に原因を特定するためにtry{}catch{}をExpressサーバのindex.jsファイルに追加しておきます。


slackEvents.on('message', async (event) => {
  try{
    await web.chat.postMessage({
      text: `受け取ったメッセージはこちらです。"${event.text}"`,
      channel: 'slack新規導入プロジェクト',
    });
  } catch(error){
    console.log(error.data)
  }
});

チャンネルにアプリを招待していない場合には下記のようなエラーメッセージ(not_in_channel)がExpressサーバを起動したターミナルに表示されます。


{
  ok: false,
  error: 'not_in_channel',
  response_metadata: {
    scopes: [ 'chat:write', 'incoming-webhook', 'im:history' ],
    acceptedScopes: [ 'chat:write' ]
  }
}

ExpressサーバからBoltへ

Boltは、Slackアプリをより直感的に効率的に開発することを目的に公開されているSlackの公式のJavaScriptフレームワークです。ここまでの動作確認ではExpressサーバを利用してきましたがBoltを利用してアプリの開発を行っていきます。これまでに記述したコードをBoltを使って作成します。

Boltのインストールと起動

任意の場所にプロジェクト用のフォルダを作成します。ここではslack_bolt_devという名前にしています。


 % mkdir slack_bolt_dev
 % cd slack_bolt_dev
 % npm init -y

npm init -yを実行するとpackage.jsonファイルが作成されます。次にnpmコマンドで@slack/boltパッケージのインストールを行います。


 % npm install @slack/bolt

index.jsファイルを作成し、Boltを起動するためのコードを記述します。このコードがベースとなります。


const { App } = require('@slack/bolt');

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

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

Slackからイベントを受け取ってメッセージをSlackに送信するコードをBoltで記述すると下記のようになります。コードは似ていますがExpressサーバの時のコードよりもすっきりしていることがわかるかと思います。


const { App } = require('@slack/bolt');

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

app.event('message',async({event,client}) => {
  try {
    const result = await client.chat.postMessage({
      text: `受け取ったメッセージはこちらです。"${event.text}"`,
      channel: 'slack新規導入プロジェクト',
    });
  }
  catch (error) {
    console.error(error);
  }
});


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

sayメソッド

メッセージを送信する際にchat.postMessageを利用していましたがイベントが発生した場所にメッセージを送信したい場合はsayメソッドを利用することができます。postMessageで指定したchannelなどを設定する必要がなくシンプルなコードで実行することができます。


app.event('message',async({event,say}) => {
  await say('sayメソッドで送信')
});

イベントが発生したその場所にメッセージを送信することができるのでFirst Appにメッセージを送信するとsayメソッドの内容はFirst Appのメッセージ一覧に表示されます。もしアプリにmessage.channelsイベント(チャンネルにメッセージが書き込まれた場合に発生するイベント)を設定している場合はメッセージを書き込んだチャンネルにsayメソッドの中身のメッセージが表示されます。

sayメソッドの動作確認
sayメソッドの動作確認

Slashコマンドの設定

ユーザがSlack内で明示的に行う処理に対して何か別の処理を行えるようにSlashコマンドの動作確認を行います。

SlackではデフォルトでもSlashコマンドが登録されており、例えばチャンネルのメッセージフィールドに/whoと入力するとチャンネルに参加しているメンバーが表示されます。そのほかに/searchと入力すると検索ボックスが表示されるなど便利な機能があります。

勤怠管理のSaaSのクラウドサービスを利用している場合にSlack連携ということでSlashコマンドを利用して出退勤の打刻等に利用しているものもあります。SlashコマンドはSlackの機能を拡張する上で重要な機能です。
fukidashi

既存のものだけではなくオリジナルのSlashコマンドをアプリ内で作成してSlackで実行させることができます。slack apiのダッシュボードからSlash Commandsのページを開きます。Slashコマンドの追加を行うので”Create New Command”ボタンをクリックしてください。

Slash Commandページ
Slash Commandページ

Slashコマンドの入力フォームが表示されるので入力項目を埋めていきます。

Slashコマンドの入力フォーム
Slashコマンドの入力フォーム

実際にSlackのメッセージフィールドで入力するコマンド名をCommandに入力し、Reqeust URLはngrokのURLを設定します。イベントで設定したものと同じURLです。Short Descriptionにはコマンドの説明を入力します。入力後は入力欄の下にある”Save”ボタンをクリックしてください。

slashコマンドの情報を入力
slashコマンドの情報を入力

新規のSlashコマンドを追加するとSlackへのアプリの再インストールが必要となるので再インストールを実行してください。

再インストール完了後、ユーザのメッセージフォームで/kitayoと入力すると入力したShort Descriptionとコマンド名が下記のように表示されます。

Slashコマンドの実行前
Slashコマンドの実行前

追加したSlashコマンドを実行することができますがBolt側で何も処理を行っていないためエラーとなります。

エラーoperation_timeoutが発生
エラーoperation_timeoutが発生

ngrokを実行しているターミナルにもエラーが表示されます。


[ERROR]   An incoming event was not acknowledged within 3 seconds. Ensure that the ack() argument is called in a listener.

上記のエラーからSlackでは3秒以内にSlackに対して応答しなければならないことがわかります。HTTPリクエストを受け取ったことをslackに伝えるためイベント処理の内部でackメソッド(acknowledgeの略)を実行する必要があります。ackメソッドを実行することでサーバ側でSlackのHTTPリクエストを受け取ったことをSlackに伝えることができます。

commandメソッドでSlackから送信されてくるslashコマンドのイベントを受け取ることができます。下記ではack()でイベントを受け取ったことをSlackに伝え、sayでメッセージを送信しています。

/kitayoを実行するとサーバから戻されたメッセージが表示されます。

/kitayoコマンドの実行
/kitayoコマンドの実行

respondメソッド

メッセージを送信する方法には先ほど動作確認を行ったsayメソッドのほかにrespondメソッドがあります。sayメソッドではBotがいないチャンネルや自分のメッセージフィールドで/kitayoを実行するとchannel_not_foundエラーが発生しコマンドを実行することができません。しかし、respondメソッドを利用すると下記の図のように自分のメッセージフィールドでSlachコマンドを実行することが可能となります。


app.command('/kitayo', async({ack,respond}) =< {
  await ack();
  await respond('おはようございます。今日も一日がんばりましょう!');
});
respondでメッセージを送信
respondでメッセージを送信

ユーザへインタラクティブコンポーネントの送信

ここまではサーバからSlackに送信したメッセージはすべてテキストでした。テキストではなくリッチなUIを表現できるインタラクティブコンポーネントを利用することでボタンやセレクトボックス、ラジオボタンなどユーザがインタラクティブに操作できる要素をSlackの画面上に表示させることができます。インタラクティブコンポーネントはBlock Kitと呼ばれるSlack独自のUIフレークワークを利用して行います。Block KitはJSONで記述することができSlackからBlock Kit Builderが提供されているのでBlock Kitを作成することは難しいものではありません。

Block Kit Builder

Block Kitを作成するBlock Kit Builderのページにアクセスすると下記のように大きく3つのパートにわかれてます。右側にはJSONでコードが記述されています。右側のJSONのコードを利用することで真ん中の画面に表示されているUIをSlack上に表示することができます。左側のメニューにはSlack上で表示させたいBlockの要素一覧が表示されており、例えばボタンを表示させたい場合はButtonsをクリックすると右側にボタンに対応するJSONコードが追加され真ん中にはJSONコードに対応した画面上でのボタンが表示されます。

右側のJSONはブラウザ上で更新すると真ん中のパートに即反映されるのでBlock Kit Builderを元にして表示させたいBlock Kitを簡単に作成することができます。

Block Kit Builderページ
Block Kit Builderページ

インタラクティブなメッセージを表示

実際にBlock Kitを利用してインタラクティブなコンポーネントをSlack上に表示させてみましょう。

Block Kit Builderのページを開くとデフォルトでは画面に例が表示されてますが、一度削除するために右上にある”Clear Blocks”をクリックしてください。

右側にはblocksのみ表示された状態です。

Clear Blocks直後の状態
Clear Blocks直後の状態

ラジオボタンを利用したいので左側のメニューの”radio buttons”をクリックしてください。右側に表示されているJSONを元に作成するので右上にある”Copy Payload”をクリックしてください。これでJSONデータがコピーできます。

radio buttonsを選択
radio buttonsを選択

コピーしたデータをコードをindex.jsファイルに貼り付けてtextの中身、選択肢の言葉も変更します。/kitayoコマンドを実行するとラジオボタンで午前出社、午後出社の2つの選択肢が表示させるように設定しています。


const blocks = [
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "午前出社ですか?午後出社ですか?"
			},
			"accessory": {
				"type": "radio_buttons",
				"options": [
					{
						"text": {
							"type": "plain_text",
							"text": "午前出社",
						},
						"value": "morning"
					},
					{
						"text": {
							"type": "plain_text",
							"text": "午後出社",
						},
						"value": "evening"
					},
				],
				"action_id": "radio_buttons-action"
			}
		},
	];

このblocksをSlashコマンド実行時にSlackの画面上に表示させるようにrespondコマンドを利用して下記のように記述します。


app.command('/kitayo', async({ack,respond}) => {
  await ack();
  await respond({
    response_type:'in_channel',
    blocks:blocks,
  })
});
response_typeにはin_channelとephemeralを指定することができます。送信するチャンネルに表示させる場合はin_channelを設定します。チャンネル全員がみることができます。ephemeralの場合は一人のユーザのみ見ることができるメッセージを送信する際に利用します。
fukidashi

実際にSlack画面でkitayoコマンドを実行してみましょう。実行するとBlock Kit Builderで表示されていたようなラジオボタンがSlack画面上に表示されます。

ラジオボタンがSlack画面上に表示
ラジオボタンがSlack画面上に表示

インタラクティブなコンポーネントなのでユーザがラジオボタンを選択することができます。午前出社のボタンを選択すると右側に注意のアイコンが表示されます。

エラーメッセージが表示
エラーメッセージが表示

エラーの内容ではアプリのページでインタラクティビティの設定が行われていないということなので設定を行います。

slack apiのダッシュボードの左側のメニューからInteractivity & Shortcutsをクリックします。InteractivityがOffになっていることが確認できます。

インタラクティビティページ
インタラクティビティページ

OffからOnに設定を行うとRequest URLを入力する画面が表示されるのでこれまでイベント、Slashコマンドで設定したURLを入力してください。

 Request URLの入力
Request URLの入力

Request URLを入力後は下部にある”Save”ボタンをクリックして設定を保存してください。

Request URL入力後の画面
Request URL入力後の画面

これでInteractivityの設定は完了です。再度画面上でkitayoコマンドを実行してみましょう。今回も前回と同様にエラーが発生します。アイコンをクリックするとエラーメッセージが変わっていることがわかります。

今回はタイムアウトエラーになっています。

エラー内容を確認
エラー内容を確認

エラーの原因は表示されているラジオボタンに対応する処理が記述されていないためです。

index.jsファイルを開いて対応する処理を記述します。イベントの時はapp.event, コマンドの時はapp.commandメソッドの中で処理を記述してきました。今回の場合はapp.actionメソッドを使うことでサーバ側で処理を行うことができます。

actionの引数には先ほど説明をしていませんでしたが、index.jsファイルのblocksのaction_idに指定したradio_buttons-actionを利用します。一意のidなので複数のblockがある場合もこのidで識別することができます。

radio_buttons-actionをSlackから受け取ったらrespondメソッドでメッセージを返します。


app.action('radio_buttons-action',async({ack,respond}) => {
  await ack();
  await respond('サーバ側でacitonを受け取りました。')
});

再度kitayoコマンドをslackの画面で実行してください。ラジオボタンが表示された後どちらを選んでも同じメッセージが表示されます。

サーバからのメッセージが表示される
サーバからのメッセージが表示される

ユーザが行ったボタンの選択操作に対してサーバからSlackに向けてメッセージを送信できることが確認できました。最後に選択した内容によって返信するメッセージを変更してみましょう。

ボタンを選択した時に送信される値はBlock Kitのvalueの値です。午前出社を選択した場合にはmorning, 午後出社を選択した場合にはeveningとしています。

valueの値はSlackから送られてくるaction.selected_option.valueに入っているのでapp.actionの中でその値を取り出して分岐し選択したボタンによって返信する内容を変えています。


app.action('radio_buttons-action',async({ack, respond, action}) => {
  await ack();
  if(action.selected_option.value === 'morninig'){
    await respond('おはようございます。今日も一日がんばりましょう!')
  }else{
    await respond('こんにちは。残り半日がんばりましょう!')
  }
});

選択したボタンによってSlack上で表示される内容が変わることを確認してください。

/kitayoコマンドを実行すると選択肢が表示されます。

kitayoコマンドで選択肢が表示
kitayoコマンドで選択肢が表示

午後出社をクリックするとサーバから送られてくるメッセージが表示されます。

午後を選択した時のメッセージ
午後を選択した時のメッセージ

Slackへのメッセージ送信だけではなくSlackとサーバ間でのデータのやりとり、ユーザとのインタラクティブな処理も行えるようになりました。

次回は基礎編を踏まえてより実践的なアプリ開発の方法を説明する予定です。