mecamonagi取扱説明書
先日のTokyo.Rで、teramonagiさんが機械の体・mecamonagiを手に入れる話をされていましたが、
www.slideshare.net
機械の体はなにもteramonagiさんだけのものではありません。 Tokyo.Rにあるmecamonagiレポジトリを使えば、あなたも機械の仲間入りです。
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
これでもう機械の体はあなたのものです。以下のように話しかけてみましょう。
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_)
この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```" })
この例だと、data
パラメータに指定したparams
がsimple_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の改造をすると気分が乗ってきます。ぜひお試しください!