BotkitでSlack Botを作りつつ自己アップデートできるようにした
Slack皆さん使ってますか?
Slackはただチャットが出来るだけではなく豊富なAPIによってもたらされる便利Botたちの存在が非常に大きいです.
Botを開発する際には,Githubが開発しているHubotが有名ですが,ここ最近ではSlack botに特化した(別に特化しているわけでもなさそう)BotkitというBotフレームワークが出ました.
Botの設置自体はリポジトリのREADME.mdを見ればわかるので,見てください. で,自己アップデートというのは何かって言うと,Botのコードを書き換えてpushした後に設置しているサーバにログインして,pullしてrestartするっていう作業がめっちゃだるかったので,自分で自分を更新して再起動させる仕組みを作りました.
基本的なコンセプトは HubotでHubotの更新をforeverを利用してHubotにさせてみる - MANA-DOT ここから得ました.ありがとうございます.
foreverでの起動
まず,参考ページの通りforeverでの起動をします. 元リポジトリの起動方法だと,tokenを環境変数で与えるようになっていますが,foreverでの環境変数の与え方がわからなかったので,Botのソース内に直書きするかファイルを読む仕組みにしましょう.
forever start bot.js
これで起動はできます.
参考ページにもある通り,foreverは死んでもうまいこと生き返らせてくれる機能を持っているので,アップデート処理が終わったら自殺するというコードをかけば良いでしょう.
アップデート処理
まずはコードを出します.
var child_process = require('child_process'); function updateSelf(bot, message){ child_process.exec('git reset --hard origin/master', function(error, stdout, stderr){ bot.reply(message, 'Botが更新されました!'); bot.reply(message, 'Botを再起動します'); setTimeout(function(){ process.exit(); }, 2000); }); } controller.hears(['update'], 'direct_mention', function(bot, message) { bot.reply(message, 'Botのアップデートを開始します'); child_process.exec('git fetch', function(error, stdout, stderr){ child_process.exec('git log master..origin/master', function(error, stdout, stderr){ if(stdout == ""){ bot.reply(message, 'Botは最新です'); }else{ bot.startConversation(message, function(error, convo){ bot.reply(message, '更新内容は以下のとおりです'); bot.reply(message, '```\n' + stdout + '\n```'); convo.ask('アップデートを行いますか?(y/n)', [ { pattern: bot.utterances.yes, callback: function(response, convo){ updateSelf(bot, message); convo.next(); } }, { pattern: bot.utterances.no, callback: function(response, convo){ bot.reply(message, 'アップデートを中止します'); convo.next(); } }, { pattern: 'じゃあそれで', callback: function(response, convo){ updateSelf(bot, message); convo.next(); } }, { default: true, callback: function(response, convo){ convo.repeat(); convo.next(); } } ]); }); } }); }); });
まずgitを操作したりするために,コマンド実行可能な child_process
を利用します.
update
コマンドが発行された時にまず,git fetchを行い,アップデートの確認を行います.
アップデートがあるのであれば,なんらかの標準出力がなされるので,それでアップデートがあるかどうかを判定します.
アップデートがある場合は,変更点を git log
で出力するようにしました.
また,BotkitのConversation機能を用いて,アップデートを行うかどうかの確認を行う仕組みを導入しました.
アップデートが許可された場合には, git reset --hard origin/master
とすることで,origin/master
への追従を行い,最新版へのコード更新が行われます.
その後, process.exit()
を発行し,自殺した後foreverによって再び生かされることになります.
process.exit()
を発行するときの setTimeout
が無いと,bot.reply()
が間に合わず何も出力されないケースがあるので,適当に時間とって setTimeout
した方がいいです.
実行時の様子
終わり
これでBot開発が捗る.
記事を自分で読み返して, git fetch
しておいて,アップデートを了承しなかった場合,もう一度updateした時には,fetchが働かず永久にupdateできないというバグを見つけてしまった. アップデートがあるかどうかの判定を git log master..origin/master
でやれば良さそう.