Help us understand the problem. What is going on with this article?

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

More than 3 years have 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について発表して頂ける方も募集中です!

hiconyan
大手町でクラウドエンジニアしてます。うどん大学院 情報系出身。趣味は料理と映画、カメラ。
https://hiconyan.page/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away