Botkit + ImgurでプルリクにLGTMする

  • 37
    Like
  • 0
    Comment
More than 1 year has passed since last update.

こんばんは,うどん大 情報系 院2年の@hico_horiuchiです.
この記事はChatOps Advent Calendar 2015の17日目です.

本当は「Hubot + ImgurでプルリクにLGTMする」だったのですが,@jacopen大先輩に
「セカホリ、botkit試してー」
と言われたので,急遽Botkitで頑張ることにしました.

Botkit?

昨日のSlack Platformの発表の中で,新しいBot Frameworkの 「Botkit」 が紹介されました.

Hubotと同じく,Node.js上で動作するようです.
READMEを読んで感じた特徴は,以下のような感じです.

メッセージの受信方法の多様性

Hubotは robot.hearrobot.respond の2種類だけでした.
BotkitではSlackに特化して,5種類準備されているようです.

  • message_received : 全てのメッセージ( robot.hear 相当)
  • ambient : メンション以外のメッセージ
  • direct_mention : @bot hello のように名前で始まるメンション
  • mention : hello @bot のように名前を含むメッセージ
  • direct_message : 1対1のDirect Messages

更に controller.hears('hello', ['direct_message', 'mention',]) のように複数指定できる.
(ただ,Botとのインタフェースを増やし過ぎるのは良くなさそう.)

対話機能

bot.startConversation を使うと,Yes/Noなどの対話を簡単に実装できるみたい.
Yes/Noの判定は標準で用意されてるけど,自分でも 'done' などを指定できる.

  • bot.utterances.yes : yes, yeah, yup, ok, sure
  • bot.utterances.no : no, nah, nope

「プルリクマージする?」「デプロイする?」みたいな場面で使えそうですね.

Imgurから画像を取得してLGTM

ここからが本題です.
突然ですが,みなさんLGTMしてますか?

LGTMとは、Looks good to meの略です。
GitHubのPull Requestなど、コードやデザインのレビューをした際などに「いいね!」の意味合いでつかわれます。

今やってる開発系のバイトではプルリク駆動開発をしていて,LGTMし合っています.
ただ,LGTM用の画像を探してきて,コメントして…って面倒ですよね.
そこで,Botを使ってSlackからLGTMできるようにしてみました.

今回は,お気に入りのLGTM画像をImgurのアルバムにしてみました.
ここからランダムに画像を取得して,LGTMコメントに使います.
(一緒にバイトしてる同級生の影響で,俺妹のGIFが多いですね.)

最初に,SlackとImgurでAPIトークンを取得しておきます.
こちらは,Qiitaに投稿されている記事を参考にします.

次に,適当な作業ディレクトリを作って,必要なライブラリをインストールします.

$ mkdir ~/botkit
$ cd ~/botkit
$ npm install --save botkit request node-github 

さて,少し長いですが,Botkitのコードはこのようになります.
Imgur APIの呼出にはrequestを,GitHub APIの呼出にはnode-githubを利用しました.
関数の階層が深くなるのが嫌だったので,以前覚えたコールバック関数を積んでいく形にしました.

~/botkit/bot.json
var SLACK_TOKEN = 'xoxb-0123456789abcdefghijklmnopqrstuvwxyz';
var IMGUR_ALBUM_ID = 'ABCDE';
var IMGUR_CLIENT_ID = '0123456789abcde';
var GITHUB_ACCESS_TOKEN = '0123456789abcdefghijklmnopqrstuvwxyz0123';

var botkit = require('botkit');
var request = require('request');
var githubAPI = require('node-github');

var controller = botkit.slackbot({
  debug: false
});

controller.spawn({
  token: SLACK_TOKEN
}).startRTM();

var getAccountImages = function(bot, message, args) {
  request.get({
    url: 'https://api.imgur.com/3/album/' + IMGUR_ALBUM_ID + '/images',
    headers: {
      'Authorization': 'Client-ID ' + IMGUR_CLIENT_ID
    }
  }, function(error, response, body) {
    if (error || response.statusCode !== 200) {
      return bot.reply(message, 'Faild to request of Imgur API.');
    }

    var images = JSON.parse(body).data;

    if (images.length === 0) {
      return bot.reply(message, 'No images are uploaded to Imgur album.');
    }

    args.image = images[Math.random() * images.length | 0];
    args.callbacks.shift()(bot, message, args);
  });
};

var issuesCreateComment = function(bot, message, args) {
  github = new githubAPI({
    version: '3.0.0'
  });

  github.authenticate({
    type: 'oauth',
    token: GITHUB_ACCESS_TOKEN
  });

  github.issues.createComment({
    user: args.user,
    repo: args.repo,
    number: Number(args.id),
    body: '![' + args.image.id + '](' + args.image.link + ')'
  }, function(error, _) {
    if (error) {
      bot.reply(message, 'Failed to request of GitHub API.');
    }
  });
};

controller.hears('^lgtm +(.+)\/(.+) +([0-9]+)$', 'direct_mention', function(bot, message) {
  var matches = message.text.match(/^lgtm +(.+)\/(.+) +([0-9]+)$/i);

  getAccountImages(bot, message, {
    callbacks: [issuesCreateComment],
    user: matches[1],
    repo: matches[2],
    id: matches[3]
  });
});

ちなみに,元になったHubotでの実装もあります.

Botkitでは正規表現に ¥s とか ¥S が使えないっぽい?
(Hubotでは /lgtm\s+(\S+)\/(\S+)\s+([0-9]+)$/i にしてました.)
あと,正規表現のマッチ部分を message.match[1] とかで取れないのも不便です.

実際に使ってみる

では,実際にBotkitを起動してみましょう.

$ node bot.js 
** No persistent storage method specified! Data may be lost when process shuts down.
** Setting up custom handlers for processing Slack messages
** API CALL: https://slack.com/api/rtm.start
** BOT ID:  bot  ...attempting to connect to RTM!

コマンドのフォーマットは @bot: lgtm <user>/<repo> <id> です.
(イカに深い意味はありません.)

botkit_ika_1.gif

APIトークンをタイポしてたりすると,ちゃんとエラーを返してくれます.
(これは bot.log にも出した方が良さそう.)

botkit_ika_2.png

まとめ

今回は,Botkitの紹介と,ImgurとGitHubを組み合わせたサンプルを書いてみました.
Hubotと比較して,Slackに特化して,多機能になっている感じです.
移植も,インタフェース部分を変えるだけなので,比較的に出来そう…?

ただ,やっぱりHubotの方が知見や資産も多くて良いなと思います.
(あと,CoffeeScriptで書けないし…ファイル分割もどうやるんだろう.)

Slack + Botkitでどんなことが出来るようになるのか,今後が楽しみですね!

Hubot×ChatOps勉強会

僕は生粋のうどん県民ですが,実は大阪や神戸で「Hubot×ChatOps勉強会」をやってます.

来年の4月から就職して上京するので,その前にもう1度開催したいと思っています.
そこで,大阪で「会場提供しても良いよ」という方居ましたら,連絡頂けると嬉しいです!
また,Botkitも含め,HubotやChatOpsについて発表して頂ける方も募集中です!