簡単な Hubot スクリプトをもっとつくろう (おみくじ系)

これは [Hubot Advent Calendar 2014][hubot-adventar-2014] の 7 日目の記事です。

また、今回は @bouzuya の Hubot 連載の第 6 回です。[目次は、第 1 回の記事にある][hubot-adventar-2014-bouzuya-index]ので、そちらをどうぞ。

前回は [簡単な Hubot スクリプトをゼロからつくろう][hubot-adventar-2014-6] ということで、generator-hubot という Yeoman ジェネレーターで Hubot スクリプトのひながたを用意し、そこから Hubot スクリプトをつくりました。

今回は Hubot が提供している機能をもうすこし知るために、Hubot スクリプト界隈では定番の「おみくじ」系のスクリプトをつくりたいと思います。

また、今回から Hubot 2.10.0 にバージョンアップされています。

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

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

今回は「おみくじ」系スクリプトということで、選択肢を挙げて「選んで！」とお願いすると、ランダムにひとつを選んで返してくれる Hubot スクリプト hubot-choice をつくりましょう。

実行例はこんな感じです。

```
bouzuya> だいこん、たまご、こんにゃく、はんぺん、ちくわから選んで！
hubot> bouzuya: だいこんで！
```

# さっそくつくる

とりあえず Hubot スクリプトのひながたを生成します。このへん分からないのであれば、過去の連載記事を参照してください。

```bash
$ mkdir hubot-choice
$ cd hubot-choice

$ yo hubot:script
```

次に `src/choice.coffee` を編集します。

```coffeescript:src/choice.coffee
# Description
#   A Hubot script for choosing at random
#
# Configuration:
#   None
#
# Commands:
#   <item1>、<item2>、<item3>から選んで - choose at random
#
# Author:
#   bouzuya <m@bouzuya.net>

module.exports = (robot) ->
  robot.hear /(.+)から選んで/, (msg) ->
    items = msg.match[1].split(/[　・、\s]+/)
    item = msg.random items
    msg.reply "#{item}で！"
```

こうだ！

実行してみましょう。

```bash
$ HUBOT_SHELL_USER_NAME='bouzuya' PATH="./node_modules/hubot/node_modules/.bin:$PATH" $(npm bin)/hubot -a shell -n hubot -r src
hubot> だいこん、たまご、こんにゃく、はんぺん、ちくわから選んで！
bouzuya: だいこんで！
```

長いですね。あと Hubot 2.10.0 からの機能をしれっと使っています。こちらの名前が 'bouzuya' になるようにしています。

# 今回の新しい要素

上記のスクリプトの新しい要素は、次の 4 つくらいでしょうか。

- `robot.hear`
- `msg.match`
- `msg.random`
- `msg.reply`

CoffeeScript 的に新しい `"#{...}"` なんてのもありますけど、そのあたりは http://coffeescript.org/ に任せるということで割愛します。 (Hubot さわりはじめたひとで CoffeeScript つらいという意見はよく聞きますが…… JavaScript でも書けるのでお好きにどうぞ)

適当に解説つけます。

## `robot.hear`

https://github.com/github/hubot/blob/v2.10.0/src/robot.coffee#L77-L78

これは listener を登録するメソッドです。

`robot.respond(pattern, cb)` は指定した pattern が `@hubot pattern` などで動くように書き換えられるのに対して、`robot.hear(pattern, cb)` は指定した pattern がそのまま使われます。

当然ですが、誤爆(関係ないメッセージに誤ってマッチ)しやすいので注意が必要です。

## `msg.match`

https://github.com/github/hubot/blob/v2.10.0/src/response.coffee#L8-L9

これは正規表現のマッチした結果を取り出しています。

今回の `msg.match[1]` は最初の例で言うと、正規表現が `/(.+)から選んで/` で、入力が `'だいこん、たまご、こんにゃく、はんぺん、ちくわから選んで！'` なんで、`'だいこん、たまご、こんにゃく、はんぺん、ちくわ'` になります。

## `msg.random`

https://github.com/github/hubot/blob/v2.10.0/src/response.coffee#L69-L75

これは与えられた Array からランダムにひとつを返します。

今回は最初の例で言うと `[だいこん', 'たまご', 'こんにゃく', 'はんぺん', 'ちくわ']` からひとつ (`'だいこん'`) を選んでくれています。

## `msg.reply`

https://github.com/github/hubot/blob/v2.10.0/src/response.coffee#L33-L40

これはアダプター依存ですが、メッセージを送信した個人に対して返信するメッセージをチャットに送信します。

## 補足

紛らわしいのですが、`msg` という変数は `robot.Response` を `new` したものに相当します。ソースコード追うとわかります。ちなみに、ぼくの個人的な Hubot スクリプトでは `res` という変数名にしています。変数名なので、好きなようにすればいいと思います。

# まとめ

今回は Hubot が提供している機能をもうすこし知ろうということで、いくつか標準の機能を紹介しました。

今回はソースコードを見てもらえるように GitHub のリンクを貼りました。Hubot のソースコードは大した量ではありません。ぼくのゴミみたいな解説を読む時間で、本体のソースコードを読むほうが速いし確実です。ぜひ、ソースコードを読んでください。

`Response#http` などで HTTP リクエストなどを紹介したいところですが、正直、使い勝手がたいへん悪いので、大多数の人が `request` などの他のメジャーな npm パッケージを使っているのが現実だと思います。外部の npm パッケージの利用については後日紹介します。

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

# 最後に

Hubot のソースコードを読んだら、いかに Hubot がひどいかが分かり、クローンをつくりたい衝動にかられると思います。

<ins>最後に、のネタを考えてたら投稿が遅れて 2014-12-08 になってしまった。</ins>

[getting-started-with-hubot]: https://github.com/github/hubot/tree/v2.9.3/docs#getting-started-with-hubot
[bouzuya/hubot-hello-world]: https://github.com/bouzuya/hubot-hello-world/
[bouzuya/hubot-choice]: https://github.com/bouzuya/hubot-choice/
[github/hubot]: https://github.com/github/hubot/
[hubot.github.com]: https://hubot.github.com/
[hubot-adventar-2014]: http://www.adventar.org/calendars/384
[hubot-adventar-2014-1]: http://qiita.com/bouzuya/items/c7d0ad80c357aab6b696
[hubot-adventar-2014-2]: http://qiita.com/bouzuya/items/11c0c6da2b3ad54b827f
[hubot-adventar-2014-3]: http://qiita.com/bouzuya/items/2a200c9e8a45e2478bc2
[hubot-adventar-2014-4]: http://qiita.com/bouzuya/items/4c0206d72ff22ade9339
[hubot-adventar-2014-5]: http://pasero.net/~mako/blog/s/685
[hubot-adventar-2014-6]: http://qiita.com/bouzuya/items/4e051f68f8d68a2944b0
[ops-for-everyone]: https://speakerdeck.com/johndbritton/ops-for-everyone
[hubot-adventar-2014-bouzuya-index]: http://qiita.com/bouzuya/items/c7d0ad80c357aab6b696