Rubotyに定期的に何かしてもらう

ruboty-cronを利用して、Rubotyに定期的に何かしてもらう方法について説明します。例えば、毎朝ミーティングの時間に通知してもらったり、今日の予定をGoogle Calendarから教えてもらったり、twitterでエゴサーチした結果を発言してもらったり、定期的にサーバの疎通確認をしたり、という用途が考えられます。

ruboty-cron

ruboty-cronは、予め登録しておいたメッセージを定期的に受け取っているように認識してもらうためのプラグインです。日時はCrontab形式で指定できます。対応しているCrontab形式は具体的には以下のような書式で、* , - / などの拡張形式にも対応しています。^1

* * * * *
T T T T T
| | | | `- wday --- 0 ..  6 (0 = Sunday)
| | | `--- month -- 1 .. 12
| | `----- day ---- 1 .. 31
| `------- hour --- 0 .. 23
`--------- minute - 0 .. 59

試しに、毎分ごとに @ruboty ping というメッセージを受け取ったものとして認識してもらいましょう。

$ ruboty
Type `exit` or `quit` to end the session.
> @ruboty add job "* * * * *" @ruboty ping
Job 3114 created
pong
pong
pong
pong
pong

ruboty-echoとの組み合わせ

ruboty-echo と組み合わせると、定期的に任意のメッセージを発言してもらえます。

image

image

おわり

簡単なリマインド機能として使ったり、定期的に何かしてもらったり、他のプラグインとの相乗効果をねらってうまく活用してみてください。Botにtwitterを監視してもらう - Qiita という記事にもruboty-cronを利用する内容を書いてみたので、よければ参考にしてください。

おまけ: ruboty-cronの内部実装

ruboty-cronの内部実装について説明します。@ruboty add job "* * * * *" @ruboty echo foo というメッセージを受け取ったとき、ruboty-cronは Chrono::Trigger.new("* * * * *") { ... }.run というコードを実行します。lib/ruboty/cron/job.rb の部分がそれです。Chrono::TriggerというのはchronoというGemで実装されているクラスです。

Rubyでは、特定の日時を表すのであればTimeやDateTimeが利用できます。しかし、これらのクラスでは「定期的な実行のためのスケジュール」を表すことができません。書式として単純な日時ではなくCrontabの書式を選んだのは、まさにそれが理由です。他の候補としては、ISO 8601 で規定された繰り返しの表現を利用することも考えましたが、あまり馴染みが無いだろうと思い採用しませんでした。ISO 8601を採用しているシステムとしては、分散システムとして有名なApache Mesos があります。ジョブスケジューリング機能にAirbnbの Chronos というライブラリが採用されていますが、ここではISO 8601の書式で定期的に処理を実行させられます。

さて、話を戻すと、Chronoではイテレータとしてスケジュールを表現することにしました。具体的には「未来の全ての瞬間の中で、指定したパターンに一致する最も近い時を返す」というオブジェクトでスケジュールを表現しています。以下がその例です。nextを呼ぶと次のTimeオブジェクトを返すという使い方になっています。

iterator = Chrono::Iterator.new("* * * * *")
iterator.next #=> 2015-04-17 14:40:00 +0900
iterator.next #=> 2015-04-17 14:41:00 +0900
iterator.next #=> 2015-04-17 14:42:00 +0900

Chrono::Trigger#run では、上記のイテレータを利用して、指定した時刻に与えたブロックを実行しています。具体的には、まずnextで次の時刻を算出して、そこまでsleepで眠りにつき、それから目覚めて実行する、という流れになります。それを更にラップしたのがruboty-cronで、Rubotyに眠ってもらってはこまるので、別のThreadでその処理を担当してもらったり、またスケジュールを脳に記憶しておいて、再起動時にスケジュールを読み込んで正しくセットする、などの処理を担当しています。