GitHubが作ったチャットのbotフレームワーク、Hubotを使ってみる(Mac)。
これがあればいろんなチャットツールにbotを導入できる!
ここでは、Hubotのインストールと各種機能のスクリプトの実装の説明をする。
各種チャットツールとの連携方法やHerokuへのデプロイ方法などはココでは説明しない。
Hubotの構造
Hubotは以下の構造になっている
[チャットツール]
↑↓
[Adapter] : チャットツールとRobotとの橋渡し。ここを入れ替えることでいろんなチャットツールに対応可能。
↑↓
[Robot]:Hubot本体。
↑↓
[scripts]:botの挙動を実装したスクリプト。CoffeeScriptで書く。
Hubotのインストール
Hubotはnode.jsで動くので、まずnode.jsをインストール。(node.js参考)
$ brew install node
そしてHubotのジェネレータをインストール。
npm
はnode.jsのパッケージ管理ツールで、node.jsをインストールすると使えるようになる。
yo
は対話形式でプロジェクトのひな形を生成するYeomanというツールを構成するものの一つ。(Yeoman参考)
$ npm install -g yo generator-hubot
任意のディレクトリでプロジェクトを作成
$ mkdir myhubot
$ cd myhubot
$ yo hubot
まず、yeoman改善のために匿名でフィードバックを収集していいか聞かれるのでy/nでお返事。
[?] ==========================================================================
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
==========================================================================:
その後、対話形式でのインストールとなる。以下に答える。
はじめはデフォルト値がカッコ書きで入ってるけど、何か入力したらそちらに上書きされる。
- 製作者名
- botの名前
- botの説明
- Adapterに何を使うか(どのチャットツール向けに作るか)
adapterは、irc
twitter
hipchat
chatwork
slack
などがあるみたい。
_____________________________
/ \
//\ | Extracting input for |
////\ _____ | self-replication process |
//////\ /_____\ \ /
======= |[^_/\_]| /----------------------------
| | _|___@@__|__
+===+/ /// \_\
| |_\ /// HUBOT/\\
|___/\// / \\
\ / +---+
\____/ | |
| //| +===+
\// |xx|
? Owner: kta-m
? Bot name: myhubot
? Description: my first hubot
? Bot adapter: slack
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|
あとはいい感じにいろいろインストールしてくれる。
インストールが完了したら、試しにローカルで動作確認してみる。
$ bin/hubot --name myhubot
warningがいくつか出るかもだけど、気にせずhubotに語りかけてみる。
名前を指定するのが面倒なら、--name
は付けなくても大丈夫。ただ、その場合はhubot
で呼びかけないと反応してくれない。
Hubot> hubot ping
Hubot> PONG
他にデフォルトでできることはhelp
コマンドで見れる。
Hubot> hubot help
Hubot> Events:
debug - {user: <user object to send message to>}
Hubot <user> is a badass guitarist - assign a role to a user
Hubot <user> is not a badass guitarist - remove a role from a user
Hubot animate me <query> - The same thing as `image me`, except adds a few parameters to try to return an animated GIF instead.
Hubot die - End Hubot process
Hubot die - End Hubot process
Hubot echo <text> - Reply back with <text>
Hubot echo <text> - Reply back with <text>
Hubot fake event <event> - Triggers the <event> event for debugging reasons
Hubot help - Displays all of the help commands that Hubot knows about.
Hubot help <query> - Displays all help commands that match <query>.
Hubot image me <query> - The Original. Queries Google Images for <query> and returns a random top result.
Hubot map me <query> - Returns a map view of the area returned by `query`.
Hubot mustache me <query> - Searches Google Images for the specified query and mustaches it.
Hubot mustache me <url> - Adds a mustache to the specified URL.
Hubot ping - Reply with pong
Hubot ping - Reply with pong
Hubot pug bomb N - get N pugs
Hubot pug me - Receive a pug
Hubot show storage - Display the contents that are persisted in the brain
Hubot show users - Display all users that Hubot knows about
Hubot the rules - Make sure Hubot still knows the rules.
Hubot time - Reply with current time
Hubot time - Reply with current time
Hubot translate me <phrase> - Searches for a translation for the <phrase> and then prints that bad boy out.
Hubot translate me from <source> into <target> <phrase> - Translates <phrase> from <source> into <target>. Both <source> and <target> are optional
Hubot who is <user> - see what roles a user has
Hubot youtube me <query> - Searches YouTube for the query and returns the video embed link.
ship it - Display a motivation squirrel
スクリプトを書く
独自スクリプトは、scripts
ディレクトリに入れる。
呼びかけに応える(respond)
明示的にhubotに対して呼びかけを行なったときに反応する。
呼びかけ内容は正規表現で判別。マッチした箇所も取得できる。
呼びかけ方法はいろいろあって、どれでもOK。
MYHUBOT xxx
myhubot xxx
@myhubot xxx
myhubot: xxx
実装
module.exports = (robot) ->
robot.respond /つー/i, (msg) ->
msg.send "かー"
# 正規表現でマッチングした部分の取得もできる
robot.respond /I am (.*)/i, (msg) ->
msg.send "Hi, #{msg.match[1]}"
# msg.randomで配列の文字列の中からランダムに選択して発言する
robot.respond /おみくじ/i, (msg) ->
msg.send msg.random ["大吉", "中吉", "小吉", "凶"]
結果
Hubot> myhubot つー
Hubot> かー
Hubot> myhubot I am Ken
Hubot> Hi, Ken
Hubot> myhuobt おみくじ
Hubot> 大吉
チャット上の発言に反応する(hear)
チャット上の発言に特定の文字列が含まれていたら反応する。
実装
module.exports = (robot) ->
robot.hear /疲れた/i, (msg) ->
msg.send "がんばって!"
結果
Hubot> もう疲れたよ。。。
Hubot> がんばって!
定期的に発言する(cron)
cronモジュールが必要なので、package.json
の中にcronモジュールを追加する。
また、標準だとUTCなので、タイムゾーンを指定したい場合は別途timeモジュールが必要。
npm install
とかしなくても、初回実行時にインストールしてくれる。
{
(略)
"dependencies": {
(略)
"cron": "^1.0.5",
"time": "^0.11.0"
},
(略)
}
実行時間の指定はLinuxのcronと同じようにできるけど、
こちらは秒単位で指定できるので、要素が6つあるので注意。
実装
cronJob = require('cron').CronJob
module.exports = (robot) ->
cronjob = new cronJob(
cronTime: "0 0 9 * * *" # 実行時間
start: true # すぐにcronのjobを実行するか
timeZone: "Asia/Tokyo" # タイムゾーン指定
onTick: -> # 時間が来た時に実行する処理
robot.send {room: "#ROOM_NAME"}, "おはようございます!"
)
結果
(毎朝9:00に)
Hubot> おはようございます!
ちなみに、定期的に発言させたいだけなら、hubot-cronを使うという手もある。
データの永続化を行う(brain)
hubotにデータを記憶させる機能。
redisが必要になる。(redis参考)
redisサーバの準備
homebrewでインストール
$ brew install redis
一応バージョン確認してみる。
% redis-server --version
Redis server v=2.8.13 sha=00000000:0 malloc=libc bits=64 build=96319fcc2102d7fa
Redisサーバ起動。(Ctrl-C
で終了)
% redis-server /usr/local/etc/redis.conf
Redisサーバが自動実行されるよう登録しておくのもいいかも。
% ln -sfv /usr/local/opt/redis/*.plist ~/Library/LaunchAgents
% launchctl load -w ~/Library/LaunchAgents/homebrew.mxcl.redis.plist
念のためプロセスを確認してみる。
% ps ax | grep redis-server
13443 ?? S 0:00.44 /usr/local/opt/redis/bin/redis-server 127.0.0.1:6379
13836 s005 S+ 0:00.00 grep redis-server
これでMacを再起動しても自動でRedisサーバが起動してくれる。
自動起動をやめたいときはコチラ。
% launchctl unload -w ~/Library/LaunchAgents/homebrew.mxcl.redis.plist
実装
module.exports = (robot) ->
# redisに保存するためのキー
KEY_DERBY_POINTS = 'derby_points'
# 対象と点数を指定
robot.hear /^(.*)さんに([0-9]+)点/, (msg) ->
name = msg.match[1]
points = (robot.brain.get KEY_DERBY_POINTS) or {}
points[name] = (points[name] or 0) + Number(msg.match[2])
robot.brain.set KEY_DERBY_POINTS, points
msg.send "#{name}さん: #{points[name]}点"
# 点数の合計を表示
robot.respond /derby list/i, (msg) ->
points = (robot.brain.get KEY_DERBY_POINTS) or {}
for name, point of points
msg.send "#{name}さん: #{point}点"
# 点数をリセット
robot.respond /derby reset/i, (msg) ->
robot.brain.set KEY_DERBY_POINTS, {}
msg.send "reset: done"
結果
Hubot> はらたいらさんに3000点
Hubot> はらたいらさん: 3000点
Hubot> 竹下景子さんに2000点
Hubot> 竹下景子さん: 2000点
Hubot> はらたいらさんに5000点
Hubot> はらたいらさん: 8000点
Hubot> hubot derby list
Hubot> はらたいらさん: 8000点
Hubot> 竹下景子さん: 2000点
WebAPIを叩く(http)
WebAPIでデータをjsonなり何なりで取ってくる。
例として、Google Geocoding APIを使って住所から緯度経度を取得してみる。
実装
module.exports = (robot) ->
robot.hear /location (.*)/, (msg) ->
request = robot.http("https://maps.googleapis.com/maps/api/geocode/json")
.query(address: msg.match[1])
.get()
request (err, res, body) ->
json = JSON.parse body
location = json['results'][0]['geometry']['location']
msg.send "#{location['lat']}, #{location['lng']}"
結果
Hubot> location 福岡市中央区大名
Hubot> 33.5869827, 130.3949105
環境変数
これだけrobotのメソッドじゃないけど、環境変数を使う方法を知っておくと設定が楽。
ローカルで環境変数を設定するには、hubot関係なくターミナルでこんな感じに。
$ export HUBOT_ENV_TEST_VAR=hogehoge
スクリプトで読み込むときはこんな感じ。
TEST_VAR = process.env.HUBOT_ENV_TEST_VAR
まとめ
これだけできれば大抵のことはできると思う。
あとはアイデアと実装する根気があれば…!