Hubot
Express

Hubot でホームページをつくろう

More than 3 years have passed since last update.

これは Hubot Advent Calendar 2014 の 10 日目の記事です。

また、今回は @bouzuya の Hubot 連載の第 8 回です。目次は、第 1 回の記事にあるので、そちらをどうぞ。


前回まで、そして今回は

前回は スクレイピングする Hubot スクリプトをつくろう ということで、npm パッケージ requestcheerio とを使って、Adventar をスクレイピングして Advent Calendar 一覧を返す Hubot スクリプトをつくりました。

前回は HTTP リクエストしたので、今回は HTTP レスポンスしましょう (?)。 Hubot で HTTP アクセスを待ち受けて、HTML を返しましょう。要するに Hubot でホームページ (Web サーバー) をつくりましょう


スクリプトの完成イメージ

まず、今回つくろうとしている Hubot スクリプトのイメージを共有しましょう。

今回は簡単なリンク集をつくります。チャットでの実行イメージはこんな感じです。

bouzuya> hubot homepage add bouzuya http://bouzuya.net/

hubot> added bouzuya http://bouzuya.net/

bouzuya> hubot homepage list
hubot> [0] bouzuya http://bouzuya.net/

bouzuya> hubot homepage remove 0
hubot> removed bouzuya http://bouzuya.net/

また Web ブラウザで http://<hubot url>/hubot-homepage/ を見るとリンク集が表示されます。


さっそくつくる

まずは Hubot スクリプトのテンプレートを generator-hubot で生成します。

$ mkdir hubot-homepage

$ cd hubot-homepage

$ yo hubot:script
...

説明不要ですね。

今回は追加パッケージは不要です。 Hubot は元々 express 3.x に依存しており、HTTP の待ち受けも標準でできるようになっているからです。

さっそく書いていきましょう。


src/homepage.coffee

# Description

# A Hubot script for hosting your web page.
#
# Configuration:
# None
#
# Commands:
# hubot homepage list - list links in homepage
# hubot homepage add <text> <url> - add link to homepage
# hubot homepage remove <index> - remove link from homepage
#
# Author:
# bouzuya <m@bouzuya.net>

module.exports = (robot) ->
links = []

robot.router.get '/hubot-homepage/', (req, res) ->
li = links.map (i) ->
"""
<li><a href="
#{i.url}" target="_blank">#{i.text}</a></li>
"""

.join '\n'
html = """
<html>
<head><title>Links</title></head>
<body>
<h1>Links</h1>
<ul>
#{li}
</ul>
</body>
</html>
"""

res.type 'html'
res.send html

robot.respond /homepage li?st?/, (msg) ->
msg.send links.map((i, index) -> "[#{index}] #{i.text} #{i.url}").join('\n')

robot.respond /homepage add (.+) (https?:\/\/.+)/, (msg) ->
text = msg.match[1]
url = msg.match[2]
item = { text, url }
links.push item
msg.send "added #{item.text} #{item.url}"

robot.respond /homepage re?m(?:ove)? (\d+)/, (msg) ->
index = msg.match[1]
item = links.splice(index, 1)[0]
msg.send "removed #{item.text} #{item.url}" if item?



解説

ええと、解説します。


links = []

リンクをここに保持します。永続化はしていないため、Hubot を再起動すると消えてしまいます。

本来は Hubot の brain で永続化すべきなのですが、それはまた別の機会に紹介します。


robot.router

https://github.com/github/hubot/blob/v2.10.0/src/robot.coffee#L265-L300

Node.js express に慣れたユーザーに説明するなら、これは express() です。express は Node.js のデファクトスタンダードな Web アプリケーションフレームワークです。Ruby の sinatra のような軽量のフレームワークです。

想像どおり express の解説をするときりがないので、簡単に。

robot.router.get(path, callback)GET メソッドを待ち受けて callback で処理できます。robot.router.post(path, callback) などもあります。詳しくは express の api reference を参照してください。

注意事項としては express のバージョンは 3.x なので、それに対応したドキュメントを参照し、コードを記述してください。


/homepage li?st?/ /homepage re?m(?:ove)? (\d+)/

細かいことですが、コマンドを省略できるようにしています。

listls でも認識されますし、removerm でも認識されます。

Hubot のコマンドはなるべく入力しやすく工夫すると良いと思います。


その他

あとは特にはないですね。list / add / remove をそれぞれ定義し、links を操作できるようにしています。


では、動かしてみましょう。

$ HUBOT_SHELL_USER_NAME='bouzuya' PATH="./node_modules/hubot/node_modules/.bin:$PATH" $(npm bin)/hubot -a shell -n hubot -r src

hubot> hubot homepage list

hubot> hubot homepage add bouzuya http://bouzuya.net/
added bouzuya http://bouzuya.net/
hubot> hubot homepage add blog http://blog.bouzuya.net/
added blog http://blog.bouzuya.net/
hubot> hubot homepage add google http://www.google.com/
added google http://www.google.com/
hubot> hubot homepage list
[0] bouzuya http://bouzuya.net/
[1] blog http://blog.bouzuya.net/
[2] google http://www.google.com/
hubot> hubot homepage rm 1
removed blog http://blog.bouzuya.net/
hubot> hubot homepage list
[0] bouzuya http://bouzuya.net/
[1] google http://www.google.com/
hubot>

動きそうですね。

Web ブラウザで http://localhost:8080/hubot-homepage/ にアクセスしてみてください。以下のような Web ページが表示されます。

hubot-homepage.png

登録した links が表示されます。画像は 3 件登録した直後のものです。もちろん削除も反映されます。


まとめ

今回は robot.router を使って HTTP リクエストを待ち受けてみました。

Hubot はホームページもつくれるんですね!すごい!(のか?)

ちなみに、この機能を使って、外部サービスの Web hook へ対応したり、adapter の実装に使ったり、生成した画像を一時的にホストして返す、などができます。BOT は必ずしも HTTP の待ち受けの必要はないので、削除すべき、といった意見もありますが、互換性の問題もありますし、おそらく簡単には削除されないと思います。

ちなみに今回のサンプルは bouzuya/hubot-homepage にあります。うまく動かない、などがあれば参考にどうぞ。


最後に

サンプルをつくるのが大変になってきました。

あとは brain / adapter くらいですかね。起動スクリプトの動きとか、スクリプト読み込みとか、Hubot 本体の動きを追っていく感じになりますかね。

次回は brain かな。正直、クソ仕様が山ほどありますし、説明する上では準備が面倒なので、個人的には嫌いなんですが。