今回は、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は多数あり、わかりにくいです。
ですので、もしかしたら使い方が間違っているかもしれませんので、ご指摘いただけると助かります。
以上