SlackやTwitterなどさまざまなプラットフォームに対応するボット作成ツール「Hubot」

近年では自動的にチャットツールやSNSなどに情報を投稿したり、それらのプラットフォーム上でユーザーと対話的にやり取りできる「ボット(bot)」が注目を集めている。今回はこのようなボットをJavaScriptで簡単に作成できるツール「Hubot」を紹介する。

ボットを使ってチャットツールをリアルタイム通知ツールとして活用する

近年、Slackなどに代表されるチャットツールを開発者間でのコミュニケーションに利用するケースが増えている。こういったツールは、ほぼリアルタイムにメッセージをやり取りできるのが特徴だ。さらに、電話や直接会ってのミーティングなどとは異なり、利用者は時間的な拘束を受けずに見たいときに送信されたメッセージを確認できる。

こういったチャットツールは開発者同士のコミュニケーションだけでなく、各種通知にも有用だ。利用例としては、ソフトウェアのビルドやテストといった比較的時間のかかる処理について、処理が完了したらその旨をメッセージで通知する、といったものが挙げられる。

こういった通知については電子メールが使われるケースも少なくないが、電子メールは環境によっては送信されてから届くまでに時間がかかるケースがあるほか、大量のメールがやり取りされるような環境だとほかのメールに紛れて見過ごしてしまう可能性もある。また、メールを送信するための環境構築や送信先の設定などの下準備が必要だ。さらに、メールはメールボックスに蓄積されていくため、その後通知メールを削除する手間もかかってしまう。

いっぽうチャットツールを利用した通知では、あらかじめチャットツールにメッセージを流すための設定は必要となるものの、その後のメンテナンスはほぼ不要だ。また、ほぼリアルタイムで通知を送ることができるといったメリットがある。

こういったチャットツールを利用した通知は、以前からIRCなどのツールではよく利用されていた。しかし近年では開発者間のコミュニケーションに「Slack」や「HipChat」、「ChatWork」、「Yammer」といったWebベースのツールを導入する例が増えている。これらチャットツールでは外部から投稿を行うためのAPIが提供されているが、当然ながらサービス/ソフトウェアごとに用意されているAPIはばらばらだ。こういったプラットフォームの差異を吸収し、さまざまなプラットフォームに対応するチャットボットを容易に開発できるツールが今回紹介する「Hubot」だ(図1)。

図1 HubotのWebサイト
図1 HubotのWebサイト

Hubotの特徴

HubotはJavaScriptで実装されたチャットボット作成フレームワークだ。実行環境にはNode.jsを利用する。Hubotの最大の特徴は、チャットツールとのやり取りを「Adapter」というモジュールに切り出すことで、本体部分のコードを変更せずにさまざまなチャットツールに対応できる点だ。公式のAdapterとして提供されているのはビジネス向けコラボレーションツール「Campfire」向けのものと、デバッグ向けシェル上で対話的に各種操作を行える「Shell」の2つだけだが、サードパーティによってさまざまなAdapterが開発されており、これらを利用することで容易にさまざまなチャットツール向けのボットを開発できる。

サードパーティ製のAdapter一覧はHubotのドキュメントにまとめられているが、現在広く使われているSlackやChatWork、YammerといったサービスやRocket.Chat、Mattermostなどのオープンソースのチャットツール、そしてAIMやIRC、Jabbr、Skypeといった比較的歴史の長いツール向けのAdapterが公開されている。また、Twitter向けのAdapterもあり、いわゆる「Twitterボット」も作成可能だ。

以下では、このHubotを使ってチャットツールに投稿を行ったり、ほかのユーザーからの投稿に反応して処理を行うボットを作成する流れを解説する。

Node.jsとHubotのインストール

まずはHubotを利用するための環境構築について紹介しておこう。HubotはJavaScriptライブラリとして提供されており、実行にはJavaScriptエンジンであるNode.jsが必要だ。また、HubotのインストールにはNode.jsが利用するパッケージマネージャであるnpmを利用する。

Node.jsのインストール

DebianやUbuntuの場合、Node.jsやnpmは標準パッケージとして提供されており、yumやapt-getといったコマンドでインストールできる。

# apt-get install nodejs npm

また、Red Hat Enterprise Linux/CentOS等の場合はEPELという拡張パッケージリポジトリでNode.jsやnpmのパッケージが提供されており、こちらを利用するのがもっとも簡単だ。

↓EPELを利用するためのパッケージをインストールする
# rpm -ivh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
↓nodejsおよびnpmをインストールする
# yum install --enablerepo=epel nodejs npm

ただし、現在EPELで公開されているNode.jsのバージョンは0.10系とやや古く、一部のモジュールが利用できない場合がある。より新しいバージョンのNode.jsを利用したい場合、Red Hatが提供している「Software Collections(SCL)」という拡張パッケージ集を使うと良い。たとえばCentOSでは、「centos-release-scl-rh」パッケージをインストールするとSCLが利用可能になる。このパッケージをインストールしたうえで、「rh-nodejs4」というパッケージをインストールするとNode.js 4系が利用できるようになる。

# yum install centos-release-scl-rh
# yum install rh-nodejs4

ただし、これらパッケージをインストールしただけで自動的にNode.js 4系が利用できるというわけではない点に注意したい。SCLで提供されているソフトウェアはディストリビューションの標準パッケージやEPELで提供されるソフトウェアに干渉しないよう/opt/rhディレクトリ以下にインストールされる。たとえばrh-nodejs4では、/opt/rh/rh-nodejs4/root/usr/bin以下にnodeコマンドがインストールされるようになっており、利用時にはPATH環境変数にこのディレクトリを追加しておく必要がある。この作業は、SCLをインストールすると利用可能になる「scl」コマンドを使って行うことが可能だ。

たとえばSCLで導入したNode.jsを利用したい場合、「scl enable rh-nodejs4 <実行したいコマンド>」というコマンドを実行することで、環境変数が設定された状態で指定したコマンドが実行される。たとえば次のようにbashシェルを起動すると、そのシェル環境下では自動的にSCLで提供されるNode.js(nodeコマンド)やnpmコマンドが使われるようになる。

$ scl enable rh-nodejs4 bash

また、次のように実行することで、SCLで提供されるnodeコマンドを使って指定したスクリプトファイルを実行できる。

$ scl enable rh-nodejs4 <実行するファイル>

Hubotのインストール

Hubotについては、作業用のディレクトリ(今回は「hubot-test」とした)でnpmコマンドを実行することでインストールできる。

$ mkdir hubot-test
$ cd hubot-test
$ npm install generator-hubot

また、Hubotでは「yo」コマンドを使ってプログラムや設定ファイルのひな形を生成することができるようになっている。yoは次のようにしてインストールできる。

$ npm install yo

このようにインストールを行った場合、yoはnpmコマンドを実行したディレクトリ以下のnode_modules/.bin/ディレクトリ内にインストールされる。

なお、「npm install -g」コマンドを使ってyoをシステム全体から利用できるようにインストールすることも可能だ。ただしこの場合、インストールにはルート権限が必要となる。

# npm install -g yo generator-hubot

Slackに自動投稿を行うボットを作る

まずはチュートリアル代わりに、テキストベースのチャットツール「Slack」にコンテンツを自動投稿するボットを作成してみよう。なお、下記ではHubot 2.19.0を使用して検証を行っている。

ひな形の作成

まず作業ディレクトリ内で以下のようにyoコマンドを実行し、プログラムのひな形を作成する。途中で開発者の連絡先やボット名その説明、使用するAdapterの入力が求められるので、適切なものを入力する。今回は使用するAdapterとして「slack」を指定した。なおAdapterは後からでもインストール可能で、コードを作成した後に使用するチャットツールを変更することも簡単に行える。

$ ./node_modules/.bin/yo hubot
? ==========================================================================
We're constantly looking for ways to make yo better!
May we anonymously report usage statistics to improve the tool over time?
More info: https://github.com/yeoman/insight & http://yeoman.io
========================================================================== No  ←統計情報の送信を拒否
                     _____________________________
                    /                             \
   //\              |      Extracting input for    |
  ////\    _____    |   self-replication process   |
 //////\  /_____\   \                             /
 ======= |[^_/\_]|   /----------------------------
  |   | _|___@@__|__
  +===+/  ///     \_\
   | |_\ /// HUBOT/\\
   |___/\//      /  \\
         \      /   +---+
          \____/    |   |
           | //|    +===+
            \//      |xx|

? Owner hylom <hylom@example.com>  ←開発者の名前とメールアドレスを入力
? Bot name hubottest  ←ボットの名前を入力
? Description hubot test script  ←ボットの説明を入力
? Bot adapter slack  ←ボットで使用するAdapterを指定
   create bin/hubot
   create bin/hubot.cmd
   create Procfile
   create README.md
   create external-scripts.json
   create hubot-scripts.json
   create .gitignore
   create package.json
   create scripts/example.coffee
   create .editorconfig
                     _____________________________
 _____              /                             \
 \    \             |   Self-replication process   |
 |    |    _____    |          complete...         |
 |__\\|   /_____\   \     Good luck with that.    /
   |//+  |[^_/\_]|   /----------------------------
  |   | _|___@@__|__
  +===+/  ///     \_\
   | |_\ /// HUBOT/\\
   |___/\//      /  \\
         \      /   +---+
          \____/    |   |
           | //|    +===+
            \//      |xx|

npm http GET https://registry.npmjs.org/hubot
  以下、必要なパッケージがインストールされる
  
  

これでボットの実行に必要なファイル一式が生成される。なお、ここで生成されるファイルは表1の通りだ。

表1 生成されたひな形ファイル
ファイル/ディレクトリ名 説明
Procfile Herokuへのデプロイ時に利用する設定ファイル
README.md READMEファイル
bin/ 実行ファイルが含まれるディレクトリ
bin/hubot ボットを起動するためのshスクリプト
bin/hubot.cmd ボットを起動するためのバッチファイル
external-scripts.json 実行時にロードする外部スクリプトを指定するファイル
hubot-scripts.json 実行時にロードするスクリプトを指定するファイル
node_modules/ 依存するモジュールが格納されているディレクトリ
npm-debug.log インストール時のログファイル。削除しても問題ない
package.json npmが使用する設定ファイル
scripts/ ボットで実行する処理を実装したソースコードを格納するディレクトリ
scripts/example.coffee CoffeeScriptで書かれたサンプルコード
.gitignore Git向けの設定ファイル。Gitを利用しないなら不要
.editorconfig 対応エディタ用の設定ファイル。エディタ側でこの機能を利用しないなら不要

なお、このひな形は「Heroku」というクラウド環境での実行が想定されているため、external-scripts.jsonファイル内にはあらかじめそのための設定が記述されている。今回はHerokuを利用しないので、このファイル自体を削除しておこう。さらに、「hubot-scripts.json」ファイルについては将来の廃止が予定されており、存在するとボットの実行時に警告メッセージが表示される。そのためこのファイルも削除しておく。

$ rm external-scripts.json
$ rm hubot--scripts.json

コードの作成

yoコマンドで作成したひな形では、scriptsディレクトリ内に格納されているJavaScript(拡張子は.js)もしくはCoffeeScript(JavaScriptコードに変換できる拡張スクリプト。拡張子は.coffee)形式のソースコードを自動的に読み込んで実行するようになっている。scriptsディレクトリ内には「example.coffee」というCofeeScriptで書かれたサンプルコードが作成されているが、現状HubotのためだけにCoffeeScriptを新規に学ぶ意味は少ないため、今回は一般的なJavaScriptコードでコードを記述することにし、このファイルは削除しておく。

$ rm scripts/example.coffee

さて、scriptsディレクトリ内に配置したソースコードでは引数として「robot」オブジェクトを受け取る関数と、それをエクスポートする処理を記述する。

module.exports = function (robot) {
  
  robotオブジェクトのメソッドを実行してボットが行う処理を実装していく
  
}

この関数はボットの初期化時に実行され、関数内で引数として渡されたrobotオブジェクトの各種メソッドを利用して各種イベントハンドラを登録したり、メッセージを送信するメソッド(表2)を使ってメッセージ送信を行うことでボットの動作を記述できるようになっている。

表2 robotオブジェクトに用意されているメッセージ送信用のメソッド
メソッド名 引数 説明
robot.send (envelope, strings…) envelope引数で指定した相手もしくはチャンネルにメッセージを送信する
robot.reply (envelope, strings…) envelope引数で指定した相手にメッセージを返信する
robot.messageRoom (room, strings…) room引数で指定したチャンネルにメッセージを送信する

まずは最もシンプルなBotの例として、10秒おきに時刻を投稿するBotを作ってみよう。今回はscriptsディレクトリ内に「clock.js」というファイルを作成し、以下の内容を記述した。

module.exports = function (robot) {
  setInterval(function () {
    var dt = new Date();
    robot.messageRoom('<チャンネルID>', dt.toString());
  }, 10000);
}

ここでは、setInterval関数を使って一定間隔で関数を実行させるよう設定している。setInterval関数では第1引数に実行する関数を、第2引数で実行間隔をミリ秒で指定する。

一定間隔で実行される関数内ではまず現在時刻を格納したDateオブジェクトを作成し、robot.messageRoomメソッドを使って指定したチャンネルに時刻を文字列として投稿している。また、実行間隔は10000ミリ秒、つまり10秒とした。

なお、messageRoomメソッドの第1引数(送信先)ではSlackの場合ルーム名ではなくチャンネルIDを指定する必要がある。チャンネルIDを取得するには、まず投稿させたいチャンネルをWebブラウザで開き、設定メニューの「Add an app or integration」リンクのURLをコピーする。

図2 投稿させたいチャンネルで設定メニュー内にある「Add an app or integration」項目のリンクURLをコピーする
図2 投稿させたいチャンネルで設定メニュー内にある「Add an app or integration」項目のリンクURLをコピーする

このURLは以下のような形式になっており、「channel_id=」に続く部分がチャンネルIDとなる。この文字列をmessageRoomメソッドの第1引数として与えれば良い。

https://<チーム名>.slack.com/services/new?channel_id=<チャンネルID>

コンソール上でのボットのテスト

コードを記述したら、続いてコンソール(仮想ターミナル)上でボットが正しく動作するかをテストしてみよう。binディレクトリ以下に作成されているhubotコマンドを引数なしで実行すると、コンソールで対話的にボットとメッセージをやり取りできる「Shell」モードでボットが起動する。

$ ./bin/hubot
  
  環境によってはいくつかここで警告メッセージが表示される
  
Tue Sep 13 2016 21:13:59 GMT+0900 (JST)
Tue Sep 13 2016 21:14:09 GMT+0900 (JST)
Tue Sep 13 2016 21:14:19 GMT+0900 (JST)
Tue Sep 13 2016 21:14:29 GMT+0900 (JST)
Tue Sep 13 2016 21:14:39 GMT+0900 (JST)
Tue Sep 13 2016 21:14:49 GMT+0900 (JST)

環境やNode.js、npmのバージョンによってはいくつか警告が表示されるが、エラーメッセージが表示されていなければとりあえず問題はない。ここではボットの起動後、10秒おきに時刻が表示されていることが確認できる。

なお、ボットはCtrl-Cを入力することで終了できる。

SlackとHubotを連携するための設定

コンソールでのテストに成功したら、今度はHubotからのSlackへの投稿を行ってみよう。

HubotからSlackへの投稿を行うには、あらかじめSlack側で連携のための設定を行っておく必要がある。連携設定は、チャンネル設定メニューの「Add an app or integration」から行える(図3)。

図3 チャンネル設定メニューの「Add an app or integration」項目
図3 チャンネル設定メニューの「Add an app or integration」項目

この項目をクリックすると、「App Directory」画面が表示される。ここで表示されている検索ボックスに「hubot」と入力して検索を行い、表示される検索結果内にある「Hubot」をクリックする(図4、5)。

図4 さまざまな連携アプリの設定を行える「App Directory」画面
図4 さまざまな連携アプリの設定を行える「App Directory」画面
図5 検索結果内の「Hubot」をクリックする
図5 検索結果内の「Hubot」をクリックする

すると詳細画面が表示されるので、画面左の「Install」を続けてクリックする(図6)。

図6 Hubot連携設定の詳細画面。左の「Install」をクリックする
図6 Hubot連携設定の詳細画面。左の「Install」をクリックする

次にボットが使用するユーザー名を入力し、「Add hubot Integration」をクリックする(図7)。

図7 ボットが使用するユーザー名を指定し、「Add Hubot Integration」をクリックする
図7 ボットが使用するユーザー名を指定し、「Add Hubot Integration」をクリックする

するとボットの実行に必要な「API Token」が表示されるので、これをコピーしておく。

図8 「API Token」としてボットの実行に使用するトークン文字列が表示される
図8 「API Token」としてボットの実行に使用するトークン文字列が表示される

また、この画面ではボットの名前やアイコンなどを変更することも可能だ。

図9 ボットの名前やユーザーアイコンなどの設定が可能
図9 ボットの名前やユーザーアイコンなどの設定が可能

この状態でSlackのトップ画面に戻ると、先ほどボット用に割り当てたユーザーがチーム内に追加されているはずだ。このユーザーを投稿先のチャンネルに追加しておこう。これでボットがSlackへの投稿を行えるようになる。

Slackへの投稿を行う

HubotにSlackへの投稿を行わせるには、以下の2点の設定が必要だ。

  • 「HUBOT_SLACK_TOKEN」環境変数に先ほどコピーしたAPI TOKENを格納する
  • hubotを「-a slack」オプション付きで実行する。

具体的には、以下のようにしてhubotを起動すれば良い。

$ HUBOT_SLACK_TOKEN=<API TOKEN> ./bin/hubot -a slack

問題がなければこれでボットが起動し、Slackの指定したチャンネルに10秒おきに時刻が投稿される。

図10 ボットが正しく稼動すれば、指定したチャンネルに10秒おきに時刻が投稿される
図10 ボットが正しく稼動すれば、指定したチャンネルに10秒おきに時刻が投稿される

Twitterへの投稿を行う

Hubotでは、使用するAdapterを変更するだけで異なるチャットサービスへの投稿が行えるようになる。先ほどはSlackへの投稿を行ったが、今度はTwitter用Adapterを使用してTwitterへの投稿を行ってみよう。

Twitter用のAdapterとしては「hubot-twitter」がメジャーだが、こちらは一部機能に制限があるので、今回は「hubot-twitter-userstream」というAdapterを利用する。このAdapterをインストールするには、yoコマンドでひな形を生成したディレクトリ(今回はhubot-testディレクトリ)で次のようにnpmコマンドを実行すれば良い。

$ npm install hubot-twitter-userstream

なお、hubot-twitter-userstreamでは「twit」というTwitterアクセス用モジュールを利用しているのだが、今回使用したバージョンのhubot-twitter-userstreamでは古いバージョンのtwitを使用するよう指定しているために認証が行えなかった。この問題は、次のようにhubot-twitter-userstreamのインストールディレクトリでnpmコマンドを実行し最新版のtwitをインストールすることで解決できる。

$ cd node_modules/hubot-twitter-userstream/
$ npm install twit@latest

Hubotとhubot-twitter-userstreamを使用してTwitterへの投稿を行うには、Twitterへのアクセスに必要なAPI KeyおよびAPI Secret、Access Token、Access Token Secretが必要になる。これらはボットとして利用するTwitterアカウントにログインした状態でTwitterアプリの管理画面(https://apps.twitter.com/)にアクセスし、アプリの作成や設定を行うことで入手できる。詳しくはTwitterのドキュメント等を参照して欲しい。

入手したAPI Keyなどの情報は、Slackの場合と同様に環境変数に格納しておく。

$ export HUBOT_TWITTER_KEY="<API Key>"
$ export HUBOT_TWITTER_SECRET="<API Secret>"
$ export HUBOT_TWITTER_TOKEN="<Access Token>"
$ export HUBOT_TWITTER_TOKEN_SECRET="<Access Token Secret>"

また、bin/hubotスクリプト内に記述されいる「–name」オプションの値を、ボットとして使用するTwitterアカウントのIDに変更しておこう。たとえばTwitterアカウントが@hylomなら、以下のように「hylom」と設定しておく。

exec node_modules/.bin/hubot --name "hylom" "$@"

以上の設定が完了したら、hubotを「-a twitter-userstream」オプション付きで実行するとボットが起動する。

./bin/hubot -a twitter-userstream

これで、Slackへ投稿した場合と同様に10秒おきにTwitterに現在時刻が投稿される(図11)。

図11 Twitterへの投稿例
図11 Twitterへの投稿例

対話的にやり取りを行えるボットを作る

さて、作成したボットは一方的に投稿を行うだけだったが、robotオブジェクトには表3のようなコールバック関数登録メソッドが用意されており、これらを利用することでユーザーの投稿に反応して何らかの処理を行うようなボットを作成できる。

表3 robotオブジェクトに用意された各種イベントハンドラ登録用メソッド
メソッド名 引数 説明
robot.listen (matcher, options, callback) ボットが属するチャンネルで何らかのイベントが発生した際に実行される関数を登録する。イベントが発生したらまずmatcher引数で指定された関数が実行され、その結果が真であった場合続いてcallback引数で指定された関数が実行される
robot.hear (regex, options, callback) ボットが属するチャンネルに投稿されたメッセージがregexr引数で指定された正規表現にマッチした場合に実行される関数を登録する
robot.respond (regex, options, callback) ボット宛に投稿されたメッセージがregexr引数で指定された正規表現にマッチした場合に実行される関数を登録する
robot.enter (options, callback) ボットが属するチャンネルにユーザーが追加された場合に実行される関数を登録する
robot.leave (options, callback) ボットが属するチャンネルからユーザーが抜けた場合に実行される関数を登録する
robot.topic (options, callback) ボットが属するチャンネルのトピックが変更された場合に実行される関数を登録する
robot.error (callback) エラーが発生した際に実行されるエラーハンドラリストにcallback引数で指定した関数を追加する
robot.catchAll (options, callback) listenやhear、respondなどで設定された条件にマッチしなかったメッセージが投稿された場合に実行される関数を登録する

たとえば、ユーザーがチャンネルに何らかの投稿を行った場合に何らかの処理を実行させるには、robot.hearメソッドを使用する。このメソッドはregex、options、callbackという3つの引数を取り、regex引数には正規表現を、options引数にはオプション設定を格納したオブジェクトを、callback引数にはコールバック関数を指定する。

robot.hear(regex, options, callback)

このメソッドでコールバック関数を登録すると、チャンネル内のユーザーが何らかの投稿を行い、その内容がregex引数で指定した正規表現にマッチした場合、callback引数で指定した関数が実行されるようになる。なお、optiions引数は省略可能だ。また、コールバック関数にはトリガーとなった投稿に関する情報を格納したresponseオブジェクトが引数として与えられる。

たとえば次のコードは、メッセージの投稿に反応してその投稿者と投稿内容をそのまま返すような処理を行うものだ。

module.exports = function (robot) {
  robot.hear(/(.*)/, function (res) {
    res.send(res.message.user.name + ' said, ' + res.match[1]);
  });
}

今回はこのスクリプトをscriptsディレクトリ内の「parrot.js」というファイルに記述した。なお前述のとおり、Hubotはscriptsディレクトリ以下の拡張子が「.js」もしくは「.coffee」のすべてのファイルを自動的に読み込むようになっているので、先ほど作成した「clock.js」については削除するか、別の拡張子にリネームしておこう。

スクリプトをscriptsディレクトリ以下に作成したら、先ほどと同じようにまずはコンソールでテストを行う。コンソール起動後、Enterキーを入力すると「hubottest>」というプロンプトが表示される。ここで何らかの文字列を入力してEnterキーを押すと、それに反応してボットが文字列を返すはずだ。

$ ./bin/hubot
  
  
hubot-test> foo
hubot-test> Shell said, foo
hubot-test> bar hoge
hubot-test> Shell said, bar hoge

また、先ほどと同様にSlackと連携させて動作させた場合の結果が図12だ。

図12 Slack上でのボットの動作結果
図12 Slack上でのボットの動作結果

チャンネルへの投稿ではなく、ボット宛の投稿に対してのみ反応させることも可能だ。その場合、hearメソッドの代わりにrespondメソッドを使用する。さらに、返信を行う際にsendメソッドではなくreplyメソッドを使用すると、そのユーザー宛に投稿を行うことが可能だ。以下のコードは、先ほどのコードに返信を行うコードを追加したものだ。

module.exports = function (robot) {

  robot.hear(/(.*)/, function (res) {
    res.send(res.message.user.name + ' said, ' + res.match[1]);
  });

  robot.respond(/(.*)/, function (res) {
    res.reply(res.message.user.name + ' said to me, ' + res.match[1]);
  });

}

コンソールでの実行結果は下記のようになる。

$ ./bin/hubot
  
  
hubot-test> foo
hubot-test> Shell said, foo
hubot-test> @hubot-test bar
hubot-test> Shell said, @hubot-test bar
Shell: Shell said to me, bar

hearメソッドで登録したコールバック関数は、ボット宛に投稿が行われた場合でも実行される。この例では「@hubot-test」付きで行った投稿に対し、両方のコールバック関数が実行され、結果として2つの出力が行われている。

また、Slackでの実行結果は次の図13のようになる。

図13 Slack上でのボットの動作結果。リプライ機能を使って返信が行われている
図13 Slack上でのボットの動作結果。リプライ機能を使って返信が行われている

Hubot内蔵のHTTPサーバーを利用する

HubotにはHTTPサーバー機能が内蔵されており、デフォルトではボットの起動時に自動的に8080番ポートで待ち受けを行うよう設定されている。このHTTPサーバーに関連するイベントハンドラはrobotオブジェクトのrouterプロパティで設定でき、HTTPサーバーへのアクセスに対応してチャットアプリ等にメッセージを送信する、といった処理を簡単に実現できる。

下記のコードはこのHTTPサーバーを利用し、「http://<ボットが稼動しているーバー>:8080/bot/send」というURLに対してPOSTメソッドで送信された情報をチャットアプリに投稿するものだ。

module.exports = function (robot) {

  robot.hear(/(.*)/, function (res) {
    res.send(res.message.user.name + ' said, ' + res.match[1]);
  });

  # ↓「start listen」という文字列が送信されたらstartListen関数を実行する
  robot.respond(/start listen/, function (res) {
    # ↓引数には現在のルームID(チャンネルID)を与えている
    startListen(res.message.room);
    res.reply('start listening.');
  });

  function startListen(roomID) {
    # ↓/bot/sendというパスに対するハンドラを設定
    robot.router.post('/bot/send', function (req, res) {
      # ↓リクエストボディがなければ何もせず終了
      if (!req.body) {
        res.end();
        return;
      }
      # ↓指定したチャンネルにPOSTされたデータを投稿する
      robot.messageRoom(roomID, req.body.content);
      # ↓アクセスしてきたHTTPクライアントに応答を返す
      res.end('send message done.');
    });
  }

}

このコードでは、ボットに対し「start listen」というメッセージが送信された場合、現在のルームID(チャンネルID)を引数として与えて「startListen」関数を実行するようになっている。startListen関数内ではrobot.router.postメソッドを使って「/bot/send」というパスへのPOSTアクセスに対するイベントハンドラを設定している。

なお、robot.routerオブジェクトはNode.jsで広く使われている「Express」というWebフレームワークのrouterオブジェクトとなっており、「get」メソッドでGETリクエストに対するコールバック関数を、「post」メソッドでPOSTリクエストに対するコールバック関数を指定できる。Expressの詳細については今回は説明を割愛するので、ExpressのWebサイトの情報などを参照して欲しい。

このコードをまずはコンソール上で実行した例が以下となる。ボットに対し「start listen」というメッセージを送ると、/bot/send」というパスへのPOSTリクエストが受け付けられるようになる。

$ ./bin/hubot
  
  
hubottest> @hubottest start listen
hubottest> Shell said, @hubottest start listen
Shell: start listening.

ここで別のシェルからcurlコマンドで以下のようにPOSTアクセスを行ってみよう。なお、-FオプションはPOSTするデータを指定するものだ。

$ curl -F "content=This is test" http://localhost:8080/bot/send
send message done.

すると、Hubot側にPOSTされた文字列が表示される。

hubottest> This is test

もちろん、Slack向けAdapterを利用した場合は同様にSlackへの投稿が行われる(図14)。

図14 Slack向けAdapterを利用してSlackに投稿を行った場合の出力結果
図14 Slack向けAdapterを利用してSlackに投稿を行った場合の出力結果

手軽にさまざまなボットが作成可能

さて、今回はHubotを使用したボット開発の基本を紹介した。Hubotではこのように数行のコードを追加するだけで簡単にSlackやTwitterへの投稿を行えるボットを作成できる。また、ボットの処理を記述した部分の修正なしに、Adapterを変えるだけで簡単に別のプラットフォームに対応させることも可能だ。

今回は紹介していないが、Hubotではヘルプを表示したり、より高度な処理を行わせるためのメソッドも提供されており、かなり高度なボットの作成も可能だ。

また、HTTPサーバー機能を利用することで、「Webフック」機能(指定した処理が実行された際に、あらかじめ指定しておいたURLにPOSTリクエストでその情報を送信する機能)を持つサービスから送信された情報をSlackに送信したり、スクリプト中からcurlなどのHTTPクライアント経由でメッセージを送信する、といったことが可能になる。

ただ、Hubotは入門用の簡単なドキュメントは公式に提供されているものの、APIの詳細についてはドキュメントが用意されていないようだ。APIの詳細や細かい挙動を確認するにはソースコードを直接読まなければならず、その点はややハードルが高いかもしれない。とはいえ、柔軟かつ手軽にボットを作成できるのは非常に便利であり、JavaScriptに長けているのであればHubotは第一におすすめしたいボット作成ツールと言える。