概要
Slackで対話的なBotをさくっと作る。
こんな感じのやつ↓
大まかな流れ
- Slack App 作成(Botトークン取得)
- Bot から Slack へメッセージ投稿
- Slack からアクションの受信、メッセージ書き換え
0. 準備
Botはローカルマシンで動かす想定。
サンプルは Node.js で書く。(仕組みがわかれば何でもいい)
$ node -v
v7.8.0
3. Slack からアクションの受信
では Slack から POST が飛んでくる為、外部からアクセス出来るようにしておく。
ngrok を使えば簡単。
$ ngrok http 3000
...
Forwarding http://3f62ee8a.ngrok.io -> localhost:3000
Forwarding https://3f62ee8a.ngrok.io -> localhost:3000
これで https://なんちゃら.ngrok.io
をローカルの3000番にフォワーディングしてくれる。
1. Slack App 作成(Botトークン取得)
Create an App から適当な名前でアプリ作成。
Bots と Interactive Components を有効にする。
Interactive Components の Request URL には、外部公開したURLを設定
(公式ストアにアプリ登録するならHTTPS必須らしいけど、自分で使う分には関係なさそう)
Permissions からワークスペースにアプリインストールする。
これで Bot ユーザ用のトークンが貰える。
2. Bot から Slack へメッセージ投稿
Slack Web API の chat.postMessage を使う。
https://slack.com/api/chat.postMessage
に適当な JSON 投げれば良い。
Attachment を含めると、ボタンやメニュー等の Interactive messages も含むリッチなメッセージを表現できる。
npm にシンプルでいい感じのラッパーがあるので使わせて頂く。
const Slack = require('slack');
const slack = new Slack();
slack.chat.postMessage({
token: 'xoxb-316871944213-oq9BcdxFWcaU2vIaxUrGXF6R', // ※Bot token
channel: 'C6N4G0BHS', // ※チャンネルID 詳細後述
text: 'クソ動物園',
attachments: [{
callback_id: 'animals_button',
text: '',
actions: ['パンダ', 'コアラ', 'タスマニアタイガー', 'ディンゴ', 'タスマニアデビル'].map(v => ({
type: 'button',
text: v,
name: v
}))
}]
}).then(console.log).catch(console.error);
トークンと Channel ID を設定して実行。
npm install slack
node post_sample.js
ボタンが投稿できた。
補足: Channel ID
Slack のチャンネル名は変更される事があるので、内部的に常に一意なIDを持っている。
channels.list の API を叩くか、ブラウザのURLから知ることが出来る。
3. Slack からアクションの受信、メッセージ書き換え
ユーザがボタンを押すと、Slack から Interactive Components で登録した URL にリクエストが来る。
適当に受け取ってパースしよう。
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.post('/', bodyParser.urlencoded({ extended: false }), (req, res) => {
const payload = JSON.parse(req.body.payload);
console.log(payload);
res.json({
text: '押されたよ',
attachments: [{
text: payload.actions[0].name
}]
});
});
app.listen(3000);
ここで JSON を返すとメッセージを書き換え可能。
さっきのボタン投稿のやつと合わせて実行。
npm install express body-parser
node post_sample.js
node receive_sample.js
ボタンを押すとメッセージが書き換えられる!
補足:メッセージを変更する他の方法
実は上の方法だと、レスポンスを3秒以内に返す必要がある。(Timeoutになる)
その為、時間がかかる処理をする場合は一旦空のレスポンスを返しておき、後から改めて書き換える必要がある。
-
payload.response_url
で渡される URL に JSON を POST - chat.update の Web API で書き換え
の2通りがあるが、 response_url
の方は「30分以内に5回まで」という制限がある。
トークンを持っているならばchat.updateで、無ければpayload.response_url
を使うのが良さそう。
var request = require('request');
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.post('/', bodyParser.urlencoded({ extended: false }), (req, res) => {
const payload = JSON.parse(req.body.payload);
res.end();
setTimeout(() => {
request.post({
uri: payload.response_url,
headers: { 'Content-type': 'application/json' },
json: {
text: '後から変更(response_url)',
attachments: [{
text: payload.actions[0].name
}]
}
});
}, 5000);
});
app.listen(3000);
const Slack = require('slack');
const slack = new Slack();
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.post('/', bodyParser.urlencoded({ extended: false }), (req, res) => {
const payload = JSON.parse(req.body.payload); console.log(payload);
res.end();
setTimeout(() => {
slack.chat.update({
token: 'xoxb-316871944213-oq9BcdxFWcaU2vIaxUrGXF6R',
channel: payload.channel.id,
text: '後から変更(chat.update)',
attachments: [{
text: payload.actions[0].name
}],
ts: payload.message_ts
});
}, 5000);
});
app.listen(3000);
クソ動物園Bot
クソロジックを追加して完成。
const Slack = require('slack');
const slack = new Slack();
const BOT_TOKEN = 'xoxb-316871944213-oq9BcdxFWcaU2vIaxUrGXF6R';
const CHANNEL_ID = 'C6N4G0BHS';
let animalLog = [];
slack.chat.postMessage({
token: BOT_TOKEN,
channel: CHANNEL_ID,
text: 'パンダ楽しみー!',
attachments: getAttachments()
}).then(console.log).catch(console.error);
// Interactive Message 待ち受け
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.post('/', bodyParser.urlencoded({ extended: false }), (req, res) => {
const payload = JSON.parse(req.body.payload);
animalLog.push(payload.actions[0].name);
res.json({
text: 'パンダ楽しみー!',
attachments: getAttachments()
});
});
app.listen(3000);
function getAttachments() {
let attachments = [{
callback_id: 'animals_button',
color: '#36a64f',
text: '',
actions: ['パンダ', 'コアラ', 'タスマニアタイガー', 'ウォンバット', 'ディンゴ'].map(v => ({
type: 'button',
text: v,
name: v
}))
}];
// animalLog の数だけクソレスポンスを追加
let memo = {};
animalLog.forEach(animal => {
attachments.push({
text: `あっ ${animal} だ! ` + (() => {
if (memo[animal]) return 'もう見た';
memo[animal] = true;
if (animal === 'ディンゴ') return '知らなーい';
return 'カワイイーッ';
})() + ' >'
});
});
return attachments;
}
なお、Interactive messages のガイドライン によると
Though messages may contain up to 20 attachments, messages containing buttons or menus shouldn't have more than one or two attachments.
との事なので、attachments どんどん増やすこの使い方は良くない。
アクション用のメッセージとは別に、レスポンス用のメッセージを用意しましょう。
補足:よりBotらしく
ユーザのアクションを捕捉する方法は他にも色々提供されており、組み合わせれば大体何でも出来る。
外部からのアクセスを許可出来るなら Events API を、そうでないなら RTM API が使いやすい。
参考
Slack Web API https://api.slack.com/web
Interactive messages https://api.slack.com/interactive-messages