Slack Botを作る

前回のSlack 通知を Node.js でもやってみるに引き続き、Slack 関連です。
これまでは、ruby と node で Slack へ一方的に通知する機能を作りました。
今回は、一方的に通知するのではなく入力した内容に応答させてみます。


目次

参考

動作環境

  • windows10 ver1909
  • v12.12.0

今回の困ったポイント

Bolt 入門ガイドの通りに行うと、
パブリックにアクセスできるようボットのプログラムを置く必要があります。
bolt では、そのため bot プログラムが応答する URL を登録する必要があるのですが、他に EC2 で運用しているサービスと共存できるようにリバースプロキシの振り分け設定がうまくできませんでした。
仕方がないので、Web API と Real Time Messaging API を使用することに。
受信に使用する Real Time Messaging API は、Websocket で slack のサービスに接続するので、EC2 にプログラムをおいておく必要がなくなりました。

bolt の件はまた別でやる(やらない)やもしれません。

準備

今回はアクセスキーの取り方が異なるので、そこから順に実施します。
slack apiにアクセスし、「Start Building」をクリック。

App Name に任意の名前を設定し、bot を稼働させるワークスペースを Development Slack Workspace に設定します。

「Bots」を開きます。

「Add a Bot User」を押します。

「Add Bot User」を押します。(表示名に変更は後でもできるので、ここで必須ではありません。)

「Install App」を押して、「Install App to Workspace.」を押す。

「許可する」を押す。

あとで使うので「Bot User OAuth Access Token」を控えておきます。

チャンネルへの登録

ここまでの作業で testapp が最近使用したアプリに表示されているので、チャンネルに参加させます。

実装

以下実装です。
とりあえず、general チャンネルで書き込むとオウム返しする物を作ります。

実装 slackbots のパターン

slackbots の導入

1
npm install --save slackbots

slackbots を利用した bot

use_slackbots.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const SlackBot = require("slackbots");

const bot = new SlackBot({
token: "[控えておいた Bot User OAuth Access Token]",
});

bot.on("start", () => {
bot.postMessageToChannel("general", "Hello");
});

bot.on("message", (data) => {
console.log(data);
if (data.type != "message" || data.subtype == "bot_message") return;

bot.postMessageToChannel("general", data.text);
});

実装 @slack/web-api @slack/rtm-api のパターン

@slack/web-api @slack/rtm-api の導入

1
npm install --save @slack/web-api @slack/rtm-api

@slack/web-api @slack/rtm-api を利用した bot

use_direct_api.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const { WebClient } = require("@slack/web-api");
const { RTMClient } = require("@slack/rtm-api");

const token = "[控えておいた Bot User OAuth Access Token]";

const web = new WebClient(token);
const rtm = new RTMClient(token);

rtm.on("connected", () => {
web.chat.postMessage({
text: "Hello world!",
channel: "general",
});
});

rtm.on("message", (event) => {
console.log(event);
if (event.type != "message" || event.subtype == "bot_message") return;
web.chat.postMessage({
channel: "general",
text: event.text,
});
});

rtm.start();

確認

以下のコマンドで bot のプログラムを起動する。

1
2
3
node use_slackbots.js
# もしくは
node use_direct_api.js

Slack 画面で「Test 」を入力すると以下のようになります。

ちゃんとオウム返しできました。

複数チャンネル対応させてみる

現在のプログラムでは、オウム返しできましたが general にしか投稿できません。
random で書き込んだのに、general に書き込んでしまします。
既知のチャンネルすべてで応答できるようにしました。

実装 複数チャンネル対応 slackbots のパターン

use_slackbots_multi.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const SlackBot = require("slackbots");

const bot = new SlackBot({
token: "[控えておいた Bot User OAuth Access Token]",
});

const channels = new Map(); //チャンネルIDとチャンネル名を保持

bot.on("start", async () => {
//チャンネル情報を取得
const result = await bot.getChannels();

result.channels.forEach((channel) => {
//チャンネル情報をMap形式で作成
channels.set(channel.id, channel.name);
});

bot.postMessageToChannel("general", "Hello");
});

bot.on("message", async (data) => {
console.log(data);
if (data.type != "message" || data.subtype == "bot_message") return;

bot.postMessageToChannel(
channels.get(data.channel), //ここを変更
data.text
);
});

接続した時に、.getChannels()でチャンネル一覧を取得し、Map オブジェクトにチャンネル ID とチャンネル名を保管します。
メッセージが流れてきたときには、チャンネル ID からチャンネル名を取得して使用します。

実装 複数チャンネル対応 @slack/web-api @slack/rtm-api のパターン

use_direct_api_multi.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const { WebClient } = require("@slack/web-api");
const { RTMClient } = require("@slack/rtm-api");

const token = "[控えておいた Bot User OAuth Access Token]";

const web = new WebClient(token);
const rtm = new RTMClient(token);

rtm.on("connected", () => {
web.chat.postMessage({
text: "Hello world!",
channel: "general",
});
});

rtm.on("message", (event) => {
console.log(event);
if (event.type != "message" || event.subtype == "bot_message") return;
web.chat.postMessage({
channel: event.channel, //ここを変更
text: event.text,
});
});

rtm.start();

@slack/web-api @slack/rtm-api の場合は、slackbots よりも簡単です。
.chat.postMessage()はチャンネル ID で送信できるので、event.channelをそのまま渡します。

以上で複数チャンネルへの送信に対応できます。


今回は SlackBot を作成しました。
Slack 投稿を受信できるようになったので、外出先からの自宅のデバイスとの通信インフラとして Slack を使うことができそうです。
まだ触っていませんが、ファイルアップロードもできるようなので、写真をアップロードに手を出しておくことにします。

ではでは。