今回は、SlackBotを作ってみます。
いまさらながらな感はありますが、Slackアプリをスマホに常時入れているし、LINEと違って、いつでもメッセージを送信できるからです。(LINEの場合好きな時に送るには有料あるいはTrial版である必要があったります)
今回はまずはエコーバックするところまでです。
(補足)
ただし、1点だけ、残念なことがありまして、AWS Lambdaでも同じように動くようにしたかったのですが、現時点では不十分です。SlackからのWebhook呼び出しに対する応答時間の制限が短いためです。うまくやればできるのかもしれませんが、まだAWSの勉強不足のため、また今度にしたいと思います。
ですので、とりあえず、ローカルに立ち上げるSwagger環境を前提とします。
Swagger環境は、以下の投稿を参考にしてください。
SwaggerでLambdaのデバッグ環境を作る(1)
以下、参考情報です。
slack api
https://api.slack.com/
Node Slack SDK
https://github.com/slackapi/node-slack-sdk
以下、投稿の続編です。
今更ながらSlackBotを作ってみた:Slach Commands
今更ながらSlackBotを作ってみた:Interactive Components
今更ながらSlackBotを作ってみた:ダイアログ
今更ながらSlackBotを作ってみた:Incoming Webhooks
#ワークスペースを作成する
まずは、まだワークスペースを作成していない場合は作成しておきます。
slack
https://slack.com/intl/ja-jp/
右上のワークスペースボタンから、「ワークスペースを作成する」を選択
ご自身のメールアドレスを入力して、Next→ を押下
そうすると、メールアドレスに、3+3の確認コードの数字が送られてきますので、入力します。
たとえば、TestTream とでもしておきます。
TestProjectとでもしておきます。
あとでも招待できるので、skip for now します。
できあがりました。
「新規登録する」ボタンを押下します。
表示させたい自分の名前と、次にログインするときのパスワードを入力します。
SlackのURLは、自動的に割り振られていますが、以降使うので、(他人と重ならなければ)変えることができます。
招待は後回しに、「完了」ボタンを押下で完了です。
#Botを作成する
さっそくBotを作成していきます。
まずは、slack apiを開きます。
https://api.slack.com/
次にページの右上にある「Your Apps」をクリックします。
そして、「Create New App」ボタンを押下します。
AppNameには適当に「TestBot」とでもしておきます。
Development Slack Workspaceには、先ほど作成したワークスペースを選択します。
作成が完了し、以下のページが表示されます。Setting-Basic Informationです。
以降このページを中心に設定を進めます。
作ったAppをBotにするため、「Bots」を選択します。
「Add a Bot User」ボタンを押下します。
DisplayNameに「TestBot」、Default usernameには「testbot」とでもしておきます。
Always Show My Bots as Onlineは、常時サーバを立ち上げるので、Onにしておきましょう。
最後に「Add Bot User」ボタンを押下します。
今のままだと何もできないので、メッセージ送信できるようにします。
Setting-Basic Information に戻ります。
Add features and functionality を選択します。
「Permmissioins」を選択します。
ScopesのSelect Permission Scopesのところで、以下を選択します。
「Send message as user」
最後に、「Save Changes」ボタンを押下します。
同じページの最初に戻って、「Install App to Workspace」を押下します。
そうすると、許可を確認する画面が表示されます。「許可する」を押下します。
これで、ワークスペース「TestTeam」に「TestBot」が追加されました。
#WebAPIの種類
Slackにはメッセージ通信するAPIとしていくつかの種類があります。
以下、私なりの理解です。
-
Incoming Webhook
いつでも好きな時に、特定チャネルまたは特定ユーザにメッセージを送信します。(受信ではない) -
Slash Command
「/」(スラッシュ)に続けて入力するコマンドを作ります。 -
RTM(Real Time Messaging) API
WebSocketを使って、リアルタイムにイベントを受信したりメッセージを送信します。 -
Event API
メッセージ投稿やメンバー追加などのイベント発生時に通知を受け取り、メッセージで応答します。 -
Interactive Components
上記のメッセージ送受信に加えて、ボタンや選択肢などのリッチなUIを提供します。(ダイアログもこれに該当?)
今回、次回以降の記事で、ひとつひとつ説明していきます。(RTM APIを除く)
#エコーバックするBotにする
まずは、一番単純なエコーバックするBotを作成しましょう。
先に、エコーバックするためのサーバを立ち上げます。
その前に、メモっておく必要がある文字列が2つあります。
まずは、Settings-Basic Informationの、App Credentialsにある「Verification Token」です。
次に、Features - OAuth&Permissions にある「Bot User OAuth Access Token」です。
それでは、サーバの立ち上げを始めます。
以下のnpmモジュールを使います。
・@slack/web-api
・node-fetch
・dotenv
以下の2つのエンドポイントを作成します。
この2つの違いは、入出力パラメータのフォーマットの違いです。メッセージを送受信する方法はいくつかあるのですが、それぞれによって微妙にフォーマットが違うのです。
/slack-testbot:
post:
x-swagger-router-controller: routing
operationId: slack-testbot
produces:
- text/plain
parameters:
- in: body
name: body
schema:
type: object
responses:
200:
description: Success
schema:
type: string
/slack-testbot-cmd:
post:
x-swagger-router-controller: routing
operationId: slack-testbot-cmd
consumes:
- application/x-www-form-urlencoded
produces:
- text/plain
parameters:
- in: body
name: body
schema:
type: object
responses:
200:
description: Success
schema:
type: string
・・・
エンドポイントから実装へのルーティングです。
・・・
/* 関数を以下に追加する */
const func_table = {
// "test-func" : require('./test_func').handler,
"slack-testbot" : require('./slack_testbot').handler,
"slack-testbot-cmd" : require('./slack_testbot').handler,
};
・・・
エンドポイントに対応する実装です。
'use strict';
const SLACK_VERIFICATION_TOKEN = process.env.SLACK_VERIFICATION_TOKEN;
const SLACK_ACCESS_TOKEN = process.env.SLACK_ACCESS_TOKEN;
const HELPER_BASE = process.env.HELPER_BASE || '../../helpers/';
const SlackUtils = require(HELPER_BASE + 'slack-utils');
const app = new SlackUtils(SLACK_VERIFICATION_TOKEN, SLACK_ACCESS_TOKEN);
exports.handler = app.lambda();
Slackボットのためのユーティリティです。
'use strict';
const fetch = require('node-fetch');
const { WebClient } = require('@slack/web-api');
class SlackUtils{
constructor(verification_token, access_token){
this.web = new WebClient(access_token);
this.verification_token = verification_token;
this.map = new Map();
}
async initialize(){
if( this.my_bot_id )
return Promise.resolve();
return this.web.auth.test()
.then(result =>{
this.my_user_id = result.user_id;
console.log('userid=' + this.my_user_id);
return this.web.users.info({user: result.user_id})
})
.then(result =>{
this.my_app_id = result.user.profile.api_app_id;
console.log('appid=' + this.my_app_id);
return this.web.bots.info({bot: result.user.profile.bot_id})
})
.then(result =>{
this.my_bot_id = result.bot.id;
console.log('botid=' + this.my_bot_id);
})
.catch(error =>{
console.log(error);
});
}
postMessage(message){
return this.web.chat.postMessage(message);
}
dialogOpen(options){
return this.web.dialog.open(options);
}
incomingMessage(webhook_url, body){
return this.responseMessage(webhook_url, body);
}
responseMessage(response_url, body){
return fetch(response_url, {
method : 'POST',
body : JSON.stringify(body),
headers: { "Content-Type" : "application/json; charset=utf-8" }
})
.then((response) => {
if( response.status != 200 )
throw 'status is not 200';
return response.text();
});
}
ackResponse(){
var response = {
statusCode: 200,
headers: { "Content-Type": "text/plain" },
body: ""
};
return response;
}
message(handler){
this.map.set('message', handler);
}
action(handler){
this.map.set('action', handler);
}
response(handler){
this.map.set('response', handler);
}
command(handler){
this.map.set('command', handler);
}
submission(handler){
this.map.set('submission', handler);
}
cancellation(handler){
this.map.set('cancellation', handler);
}
lambda(){
return async (event, context, callback) => {
await this.initialize();
var body = JSON.parse(event.body);
if( body.payload )
body = JSON.parse(body.payload);
if( body.token != this.verification_token )
return;
if( body.type == 'url_verification' ){
var response = {
statusCode: 200,
headers: { "Content-Type": "text/plain" },
body: body.challenge
};
return response;
}
console.log('body.user_id', body.user_id);
if( body.event )
console.log('body.event.user', body.event.user);
if( body.user )
console.log('body.user.id', body.user.id);
console.log(JSON.stringify(body));
if( body.user_id == this.my_user_id || (body.event && body.event.user == this.my_user_id ) )
return this.ackResponse();
var type = 'message';
if( body.event && body.event.message )
type = 'response';
if( body.command )
type = 'command';
if( body.type == 'block_actions' )
type = "action";
if( body.type == 'dialog_submission')
type = "submission";
if( body.type == 'dialog_cancellation')
type = "cancellation";
var handler = this.map.get(type);
if( handler )
handler(body, this.web);
else
console.log(type + ' is not defined.');
callback(null, this.ackResponse());
}
}
};
module.exports = SlackUtils;
環境変数に、各人のVerification TokenとBot User OAuth Access Tokenを設定します。
SLACK_VERIFICATION_TOKEN="【Verification Token】"
SLACK_ACCESS_TOKEN="【Bot User OAuth Access Token】"
さっそく立ち上げておきます。
エコーバックのトリガとしては、Event APIを使っていきます。
以下の手順で有効にします。
・Features-Event Subscriptions を選択し、Enable EventsをOnにします。
・そして、Request URLのところに、先ほど立ち上げたサーバのエンドポイントをフルパスで指定します。/slack-testbotの方です。
無事に、「Verified」となりましたでしょうか?slack-utils.jsがVerifyに必要な処理をしています。
次に、Subscribe to Bot Events に、「message.im」を選択し、最後に右下の「Save Changes」を押下します。
これにより、メッセージを受信したときに、Eventが発火されるようになります。
そこら辺のハンドリングは、slack-utils.jsがうまくやってくれるようにしておきました。
発火されたEventは、app.message()に指定したコールバックを呼び出します。
したがって、このコールバック関数内に、処理したい内容(今回はエコーバック)を記述します。
index.jsを少し追記します。
'use strict';
const SLACK_VERIFICATION_TOKEN = process.env.SLACK_VERIFICATION_TOKEN;
const SLACK_ACCESS_TOKEN = process.env.SLACK_ACCESS_TOKEN;
const HELPER_BASE = process.env.HELPER_BASE || '../../helpers/';
const SlackUtils = require(HELPER_BASE + 'slack-utils');
const app = new SlackUtils(SLACK_VERIFICATION_TOKEN, SLACK_ACCESS_TOKEN);
// 追記ここから
app.message(async (body, web) =>{
if(body.event.text)
app.postMessage({ channel: body.event.channel, text: body.event.text + " です。", as_user: true });
});
// 追記ここまで
exports.handler = app.lambda();
ちょっと補足します。
受信しているメッセージは以下を参考にしてください。
slack api:Event API:Receiving Events
https://api.slack.com/events-api#receiving_events
slack api:message.im
https://api.slack.com/events/message.im
返信している内容は以下を参考にしてください。
slack api:Reference: Message payloads
https://api.slack.com/reference/messaging/payload
#動作確認
スマホで動作確認してみましょう。
以降は、AndroidのSlackでの画面です。
左上隅をタッチします。
ワークスペースを追加 を選択します
ワークスペース「TestTeam」を作った人と同じであれば、そのワークスペースが表れていると思います。
サインインを押下します。
さっきのパスワードを入力します。
ログインできました。
左上隅をタッチし、TestBotを選択します。
TestBotが開きましたので、「あかさたな」と投稿してみましょう。
「あかさたな です」と返ってくれば成功です。
終わりに
今回はここまでとします。
次回から、ボタンや選択肢を表示してインタラクションを高めたり、ダイアログボックスを表示したり、スラッシュコマンドを追加したりしてみます。
SlackのAPIは多数あり、わかりにくいです。
ですので、もしかしたら使い方が間違っているかもしれませんので、ご指摘いただけると助かります。
以上