mecamonagi取扱説明書

先日のTokyo.Rで、teramonagiさんが機械の体・mecamonagiを手に入れる話をされていましたが、

www.slideshare.net

機械の体はなにもteramonagiさんだけのものではありません。 Tokyo.Rにあるmecamonagiレポジトリを使えば、あなたも機械の仲間入りです。

github.com

mecamonagiを動かす

mecamonagiとは

mecamonagiは、Rを実行できるhubotのDockerイメージです。主にSlackで使うことを想定しています。

現在のところ、以下の機能が実装されています。

  • r! Rのコマンドとつぶやくと、Rのコマンドを実行してくれます。
  • wheather! 場所とつぶやくと、OpenWheatherMapのAPIから天気を取得してくれます。
  • plot! Rのコマンドとつぶやくと、出力されたグラフをSlackに出力してくれます。

mecamonagiのイメージをdocker pullする

mecamonagiのDockerイメージは、Dockerhubに置かれています。

https://hub.docker.com/r/tokyor/mecamonagi/

なので、docker pullすればさくっと手元で走らせることができます。

docker pull tokyor/mecamonagi

mecamonagiのイメージをdocker runする

mecamonagiを動かすには、hubot用のSlackトークンが必要です。まだ自分のhubotを登録していなければ、https://YOURTEAM.slack.com/services/new/hubotから設定できます。

docker run -d \
  -e HUBOT_SLACK_TOKEN=XXXXXXXXXX \
  tokyor/mecamonagi

ただし、hubotはファイルのアップロード権限がありません。このため、plot!の結果を出力するにはユーザー権限のAPIキーも必要です。 その場合のコマンドラインは以下のようになります。

docker run -d \
  -e HUBOT_SLACK_TOKEN=XXXXXXXXXX \
  -e SLACK_WEB_API_TOKEN=YYYYYYYYY \
  tokyor/mecamonagi

これでもう機械の体はあなたのものです。以下のように話しかけてみましょう。

f:id:yutannihilation:20150911233614p:plain:w300

mecamonagiを支える技術

hubot

ググってください

Rserve

Rserveは、Rのセッションを提供するサーバです。別言語や別のアプリケーションからRを利用するときによく使われます。mecamonagiの内部ではRserveのプロセスが走っていて、r!plot!コマンドは、このRserveに渡されて実行されます。

Rserve - Binary R server - RForge.net

コマンドラインでRserveを立ち上げるには、いかのようなコマンドを打ちます。

R CMD Rserve --vanilla

rio

hubotはNode.jsで、Rserveとやりとりをするのはrioというnpmパッケージを使っています。ちなみに、やりとりするデータは、文字列のエスケープの仕方がRとNode.jsで違ったりするので、いちどBASE64に変換してから渡すようにしています(もっと頭いいやり方がある気はしつつ...)。

albertosantini/node-rio · GitHub

rioは、パラメータを受け取って処理をするRの関数を用意しておかないといけないのがややめんどくさいですが、パラメータの指定が細かくできるので便利です。

例えば、以下のようなRスクリプトを使っています。(wrap_func()についてはあとで説明します)

simple_exec_ <- function(params) {
  MAX_LINES <- 30
  script <- RCurl::base64Decode(params$script)
  output <- capture.output({
    eval(parse(text = script))
  })
  if (length(output) > MAX_LINES) {
    output <- c(head(output, MAX_LINES), "...")
  }
  as.character(RCurl::base64Encode(paste0(output, collapse = "\n")))
}

simple_exec <- wrap_func(simple_exec_)

https://github.com/TokyoR/mecamonagi/blob/a930a218461a7a98d62e8ead8855560a61f92557/R/simple_exec.R#L28-L40

このsimple_execをパラメータのentryPointに指定して渡せば動きます。具体的にはこんな感じです:

    rio.sourceAndEval(path.join(__dirname, "R", "simple_exec.R"), {
      entryPoint: "simple_exec",
      data: params,
      callback: (err, ans_raw) ->
        ans = JSON.parse(ans_raw)

        ...snip...

        if ans.result
          msg.emote "```\n" + Buffer(ans.result, 'base64').toString() + "\n```"
    })

https://github.com/TokyoR/mecamonagi/blob/a930a218461a7a98d62e8ead8855560a61f92557/mecamonagi.coffee#L27-L44

この例だと、dataパラメータに指定したparamssimple_execの引数になります。

コンソール出力をキャプチャする

以下の関数でやってます。ほぼ、StackOverflowにあった回答そのままです。

wrap_func <- function(fun) {
  function(params) {
    warn <- err <- NULL
    params <- fromJSON(params)

    res <- withCallingHandlers(
             tryCatch(fun(params),
               error = function(e) {
                 err <<- conditionMessage(e)
                 NULL
               }),
               warning = function(w) {
                 warn <<- append(warn, conditionMessage(w))
                 invokeRestart("muffleWarning")
               })

    result_json <- toJSON(list(result = res, warning = warn, error = err),
                          auto_unbox = TRUE, null = "null")
  
    as.character(result_json)
  }
}

mecamonagiを改造する。

mecamonagiにこんな機能がほしい!/足りない!と思ったら、積極的に改造してください。プルリクお待ちしています。

パッケージをインストールする

足りないパッケージがあったときは、都度都度インストールするよりDockerfileに書いてしまった方が早いです。littlerがインストールされているので、install.rあたりをDockerfileに追記すれば大丈夫です。

RUN install2.r \
    pkg1 \
        pkg2

mecamonagi/Dockerfile at master · TokyoR/mecamonagi · GitHub

新しいスクリプトを書く

書くこと思いつかないので決断的に省略!

まとめ

Coffee ScriptでもRでも、プルリクお待ちしています。

個人的には、この曲を聴きながらmecamonagiの改造をすると気分が乗ってきます。ぜひお試しください!

www.youtube.com