(-> % read write unlearn)

My writings on this area are my own delusion

hubotの環境をセットアップして、chatworkからshellファイルを実行できるようにして、デーモン化する

この記事の概要:

  1. hubotでボットをコーディングする環境を作って
  2. chatworkから操作できるようにして
  3. shellのファイルを実行できるようにして
  4. それをデーモンにする。

install hubot with chatwork adapter

install node*1

brew install node

install yo generator-hubot*2

npm install -g yo generator-hubot

install coffee. hubotのスクリプトの多くはcoffeeで書かれているみたい。

npm install -g coffee-script

他にredisも必要って出てくる記事が多いけど、自分がやった時はなくても大丈夫だった。

作業ディレクトリ作成 。cwhubotというディレクトリにしてます。以降、このディレクトリを起点に作業します。

mkdir -p ./cwhubot
cd       ./cwhubot

create hubot project

yo hubot

途中でアダプタを聞かれるので、デフォルト値のcampfire を上書きするために chatwork と書いてEnter。他は書き換えずにEnterでOK。

以下のような感じ

[cwhubot] yo hubot
                     _____________________________
                    /                             \
   //\              |      Extracting input for    |
  ////\    _____    |   self-replication process   |
 //////\  /_____\   \                             /
 ======= |[^_/\_]|   /----------------------------
  |   | _|___@@__|__
  +===+/  ///     \_\
   | |_\ /// HUBOT/\\
   |___/\//      /  \\
         \      /   +---+
          \____/    |   |
           | //|    +===+
            \//      |xx|

? Owner: xxxxx <xxxxx@example.jp>
? Bot name: cwhubot
? Description: A simple helpful robot for your Company
? Bot adapter: (campfire) chatwork
? Bot adapter: chatwork
   create bin/hubot
   create bin/hubot.cmd
・・・(省略)・・・

起動時にherokuのエラーが出るのでpackage.jsonから削除。*3

vi ./external-scripts.json
    # heroku 関連の行は削除。

起動

試しに hubot のコマンドラインを起動。bin/hubotでOK。 cwhubot pingって打ってPONGって返ってきたら疎通確認OK。いくつかコマンド例も記述しておきます。

bin/hubot

    help                           # コマンドリストの表示
    cwhubot help                   # cwhubotのコマンドリストを表示
    cwhubot ping                   # PONGって返ってくるだけ
    cwhubot echo hoge              # 引数がそのまま返ってくるだけ
    cwhubot translate me こんにちは  # 翻訳してくれる
    exit                           # 終了します。CTRL+c でも終了できる。

chatwork adapter

チャットワークのアダプタの設定を環境変数にセットする。*4

export HUBOT_CHATWORK_TOKEN="*****"     # APIのアクセストークンを入力します。これは申請して取得しておいて。
export HUBOT_CHATWORK_ROOMS="****"      # ルームIDを指定。カンマ区切りで複数指定可能。テストの時はマイチャットとか指定しとくのが無難。
export HUBOT_CHATWORK_API_RATE="360"    # ルーム上の発言の取得回数/時

HUBOT_CHATWORK_API_RATEをちょっと補足。例えば360なら、3600秒(つまり1時間)あたり360回ルーム上の発言を取得するので、平均的に5秒(=3600秒/360/2)+αの時間でhubotが反応してくれます。*5

ちなみに、書いてる時点ではchatworkのAPIは100回/分の制限なので、RATEは大き過ぎたらだめ。小さ過ぎてもhubotの反応が遅くていらいらする。

チャットワークアダプタで起動

さっきはデフォルトのshellアダプタで起動したのだえ、次はアダプタにチャットワークを指定して起動する。yo hubotした時にchatworkアダプタ自体は設定しているので、やるべきことは起動時にアダプタを明示するだけ。

bin/hubot -a chatwork -n sample-bot
    CTRL+Cで終了します。
  • -a: アダプタ指定。defaultはshellアダプタというコマンドラインインターフェイス
  • -n: hubot が反応する名前。defaultは今回の場合cwhubot。このデフォルト値はbin/hubot内に起動オプションで指定されている。yoコマンドでプロジェクトが生成される時に勝手にそういうふうにしてくれている。

チャットワークの指定したルームでsample-bot pingとしてPONGが返ってくれば成功。

scriptを書いて動作を拡張する。

hubotはjavascriptかcoffeeで動作を反応する動作を拡張することができる。coffeeで書くほうがメジャーっぽい。なので、書いたことのないcoffeeを書いてみる。

coffeescriptシンタックスハイライト

coffeeは初めてなので、sublimetextに以下のpluginを入れてみる。 https://github.com/aponxi/sublime-better-coffeescript#installation

shellファイルを実行するhubotの拡張script

scriptsにcoffeeかjsのファイルを置けば自動で認識してくれるみたい。./scripts/script.coffeeファイルを作成。

内容は以下のように。

module.exports = (robot) ->

    # 単純な応答: respond
    robot.respond /I am (.*)/i, (msg) ->
        msg.send "Hi, #{msg.match[1]}"

    # 呼ばれなくても発言に反応: hear
    robot.hear /今何時/, (msg) ->
        msg.send new Date()

    # scriptの実行
    robot.respond /sh (.+)/, (msg) ->
        file = msg.match[1].trim()
        if /(^\.|\/)/.exec(file)
            msg.send 'Error: File name must not have "/" and must not start with "."'
            return

        @exec = require('child_process').exec
        command = "sh ./scripts/sh/#{file}"

        @exec command, (err, stdout, stderr) ->
            resp = ""
            resp +=    err if    err?
            resp += stdout if stdout?
            resp += stderr if stderr?
            msg.send "[info][title]execute: #{command}[/title]#{resp}[/info]"

./script/sh/ディレクトリのスクリプトを指定して実行可能なようにしている。*6

実際に実行するshellファイル。

./scripts/sh/test.shを作る。

mkdir -p ./scripts/sh
touch    ./scripts/sh/test.sh
open     ./scripts/sh/test.sh

サンプルなので、内容はシンプルに以下のように。

echo "Hello, chatwork->hubot->shellscript sample!"
date
pwd
whoami

chatworkから実行してみる

sample-bot sh test.shとチャットワークの対象ルームで発言して、shellの実行結果が返ってくれば成功。

daemonize

foreverというnode_moduleを使う。

npm install -g forever

bin/hubotをこんな感じに書き換えて仕上げる。

#!/bin/sh

set -e

npm install
export PATH="node_modules/.bin:node_modules/hubot/node_modules/.bin:$PATH"
export HUBOT_CHATWORK_TOKEN="*****"
export HUBOT_CHATWORK_ROOMS="****" 
export HUBOT_CHATWORK_API_RATE="1200"

forever --minUptime 3000 --spinSleepTime 3000 start -c coffee node_modules/.bin/hubot -a chatwork -n sample-bot "$@"

後は起動するだけ。

bin/hubot

forever listでプロセスがちゃんと出てくればOK。こんな感じ。

[cwhubot] forever list
info:    Forever processes running
data:        uid  command script                                       forever pid   id logfile                         uptime
data:    [0] yoPz coffee  node_modules/.bin/hubot -a chatwork -n sample-bot 54899   54939    /Users/xxxxx/.forever/yoPz.log 0:0:1:1.956
  • forever list: foreverから起動しているデーモンの一覧表示。
  • forever stopall: foreverから起動しているデーモンを全て停止する。
  • forever stop [forever process id]: ID指定で停止する。引数に指定するidはforever listで確認(上の表示だと54939がそれ)。*7
  • forever help: Usageを表示。

起動する際のオプションなどはこちらの方の記事を参考にしました。

感想

簡単にできた。はまったのは、

  • coffeeの文法
  • homebrewから入れたnodebrewのnpmがうまく動かなかったこと。というかnodebrewの使い方はちゃんと見なかったかも。
  • 既存のbin/hubotをラップするようにshellを書いて、その中でforever使ったんだけど、それだとうまく動かなかった。forever的には起動自体はしてるっぽいんだけど。

便利だけどchatops目指すとコマンドもscript(というかrespond)がたくさん増えて管理はそれなりに大変そう。hubotのアカウント自体複数作って、重要度やオペレーション内容で分けたりするのかな。

この記事には書いてないけど、cronも簡単に動かせた。自分にはこっちのほうが(ローカルだと)使い道がありそう。

coffeeは便利だし直感的に分かるから不満はないけど、replでif文がなんかうまく書けない。。。

hubotについて書かれてる書籍は、まだ WEB+DB PRESS しかないみたい。手許にあるんだけど、結構突っ込んだ例まで書かれている。ただ、普通に動かしたりするくらいならネットで調べたほうが早い。

*1:最初nodebrewからやったんだけどなぜか上手くいかなかった。ほんとはnodebrewのほうがいいと思う。

*2:古い記事だと hubot --create とかになっててyoを使ってないので注意。この時点ではyoを使うのが標準。

*3:この記事ではherokuにデプロイしたりしないので。

*4:環境変数以外で設定を渡す方法がよくわかりません。誰か教えてください。

*5:ChatWorkAPIの反応時間、NWを通る時間、hubotの処理時間もろもろがあるので、hubotの応答が返ってくるまでには、平均5秒ではなく。平均5秒+αです。

*6:一応、ディレクトリトラバーサルされないようにファイル名のチェックを入れたけどちゃんとできてるかは自信ないです。

*7:forever list で表示されるIDっぽい数字は2つあるんだけど、後のほう。