これは Hubot Advent Calendar 2014 の 18 日目の記事です。
また、今回は @bouzuya の Hubot 連載の第 11 回です。目次は、第 1 回の記事にあるので、そちらをどうぞ。
ちなみに昨日は @hiconyan さんの Hubotで「どのコマンドにも一致しない」ときの処理 でした。Robot#helpCommands()
については説明する予定だったんだけど、もういいかなー。
前回まで、そして今回は
さて、前回は 設定を読み込む Hubot スクリプトをつくろう ということで、外部 (環境変数) から設定を読み込む Hubot スクリプトをつくりました。
今回は、robot.brain
を使って Hubot スクリプトのデータを永続化してみましょう。
brain
はシンプルな KVS であり、Hubot の標準の永続化の仕組みです。実際にどうやって永続化するのかは次回説明します。今回は使うだけです。
サンプルコード
以下のサンプルコードは、ちょっと長いのですが、メッセージを 7 日後に予約できる Hubot スクリプトです。
Heroku にデプロイした場合、単純にメモリーに保持すると 7 日後までに何度も再起動がかかり、予約情報を失ってしまいます。そこで、予約情報を brain に書き込むことで永続化します(どのような形式で永続化するかはまた後日紹介します)。永続化しておけば、再起動がかかってもデータが失われる心配はありません。
# Description
# A Hubot script for sending a message to yourself seven days later.
#
# Configuration:
# None
#
# Commands:
# hubot 7d add <message> - reserve the sending message
# hubot 7d list - list reserved messages
#
# Author:
# bouzuya <m@bouzuya.net>
moment = require 'moment'
module.exports = (robot) ->
key = '7days-later'
robot.respond /7d add (\S+)$/, (msg) ->
message = msg.match[1]
messages = robot.brain.get(key) ? []
i = { room: msg.envelope.room, at: moment().add(7, 'days'), message }
messages.push i
robot.brain.set(key, messages)
msg.send "#{i.at.format('YYYY-MM-DD HH:mm')} #{i.message}"
robot.respond /7d list$/, (msg) ->
messages = robot.brain.get(key) ? []
message = messages.sort (a, b) ->
if a.at.isSame(b.at)
0
else if a.at.isBefore(b.at)
-1
else
1
.map (i) ->
"#{i.at.format('YYYY-MM-DD HH:mm')} #{i.message}"
.join '\n'
msg.send message
watch = ->
setTimeout ->
messages = robot.brain.get(key) ? []
now = moment()
targets = messages.filter (i) -> i.at.isBefore(now)
newMessages = messages.filter (i) -> !i.at.isBefore(now)
if messages.length isnt newMessages.length
robot.brain.set(key, newMessages)
targets.forEach (i) ->
robot.messageRoom i.room, i.message
watch()
, 60000
watch()
解説
大きく分けて、3 つになります。
- 予約を追加する
robot.respond /7d add (\S+)$/, (msg) ->
- 予約を一覧表示する
robot.respond /7d list$/, (msg) ->
- メッセージを送信する
watch = ->
予約の追加
robot.respond /7d add (\S+)$/, (msg) ->
message = msg.match[1]
messages = robot.brain.get(key) ? []
i = { room: msg.envelope.room, at: moment().add(7, 'days'), message }
messages.push i
robot.brain.set(key, messages)
msg.send "#{i.at.format('YYYY-MM-DD HH:mm')} #{i.message}"
ポイントは brain です。
brain からの値取得
messages = robot.brain.get(key) ? []
brain から値を取得しています。もし取得できなかった場合には、nullが返されるので、代わりに空の配列を設定しています。
brain への値設定
robot.brain.set(key, messages)
brain へ値を設定しています。
予約の一覧
特に言うことはないですね。 robot.brain.get(key)
で取得して表示するだけですね。
メッセージの送信
watch = ->
setTimeout ->
messages = robot.brain.get(key) ? []
now = moment()
targets = messages.filter (i) -> i.at.isBefore(now)
newMessages = messages.filter (i) -> !i.at.isBefore(now)
if messages.length isnt newMessages.length
robot.brain.set(key, newMessages)
targets.forEach (i) ->
robot.messageRoom i.room, i.message
watch()
, 60000
watch()
setTimeout()
を使って定期的に メッセージを確認しています。
setInterval()
を使って定期的に確認する方法もありますが、一回ずつ実行したいので、今回は setTimeout()
を使っています。
動かしてみましょう
念のため動かしましょう。
$ HUBOT_SHELL_USER_NAME='bouzuya' PATH="./node_modules/hubot/node_modules/.bin:$PATH" $(npm bin)/hubot -a shell -n hubot -r src
hubot> hubot 7d add hoge
2014-12-25 23:33 hoge
hubot> hubot 7d add 忘れたころにやってくる
2014-12-25 23:34 忘れたころにやってくる
hubot> hubot 7d list
2014-12-25 23:33 hoge
2014-12-25 23:34 忘れたころにやってくる
7 日後なので、確認できないのですが、たぶん来ます。
まとめ
robot.brain
の値の取得・設定を説明しました。
実際にどうやって永続化するのかや、問題点などは次回説明します。
今回の Hubot スクリプトは bouzuya/hubot-7days-later に置いています。動かない場合には参考にしてみてください。
最後に
実は今回は bouzuya/hubot-brainfxxk というボツになったネタがあって、brain といえば brainf*ck だろうと思い、 をつくったのだけど、余計なコードが多すぎるので却下されました。
というか、brain 嫌いなんですよね。