Posted at

robot.brain を使った Hubot スクリプトをつくろう

More than 3 years have passed since last update.

これは 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 に書き込むことで永続化します(どのような形式で永続化するかはまた後日紹介します)。永続化しておけば、再起動がかかってもデータが失われる心配はありません。


7days-later.coffee

# 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 嫌いなんですよね。