動かざることバグの如し

近づきたいよ 君の理想に

【Slack】Message Buttonを使ったクイズを作ってみる

Message Buttonとは

最近になってSlack APIに追加された新機能 普段使ってるタイムライン上でボタン出せるよ!って話

ニュースになって(一部の界隈では)結構話題になった

jp.techcrunch.com

japan.cnet.com

その割に「実際に試してみた」的な記事は殆ど見かけない。あのQiitaですら音沙汰ない(平成28年7月2日現在)

何故か、理由は簡単で APIはシンプルなくせに実装がくっそ煩わしいから である

けどどうしてもボタンを作ってみたくなったので試しにやってみた

用意するもの

  • Slack
  • 1 Integration消費するっぽい かなC
  • SSL通信できるサーバー
  • 証明書はLet's Encryptでも可能
  • 上記サーバーに接続するためのドメイン1つ

構成

数値はポート番号を表す。

[ いんたーねっと ]-----[ ルーター ]--443--[ nginx ]--3000--[botkit]

証明書の準備

繰り返すがLet's Encryptでもできた 要はHTTPSなら何でもいいっぽい(オレオレ証明書は未確認)

thr3a.hatenablog.com

Slackの設定

これが結構ややこしい

https://api.slack.com/apps/newにアクセスしてアプリを作る 作成時は適当でもおk

次にココからさっき作ったアプリの設定画面に行き各種設定を行う

  • App Credentialsより
    • Redirect URIhttps://YOUR_HOST/oauthにする
    • Client IDとClient Secretは後で使うので控えておく
  • Bot Userより
    • 適当な名前を設定しておく
  • Interactive Messagesより
    • Request URLを https://YOUR_HOST/slack/receive にする

nginxのインストール

省略 リバースプロキシ役なので適当に443でlistenしてhttp://localhost:3000に投げればおk

Botの作成

npm initして npm install -S botkit は予めやっておく bot.jsとして以下を作成

var Botkit = require('botkit');

/***********************************
 * Setup
 ***********************************/

if (!process.env.clientId || !process.env.clientSecret || !process.env.port) {
  console.log('Error: Specify clientId clientSecret and port in environment');
  process.exit(1);
}

var controller = Botkit.slackbot({
  json_file_store: './bot_db/'
}).configureSlackApp(
  {
    clientId: process.env.clientId,
    clientSecret: process.env.clientSecret,
    scopes: ['bot']
  }
);

controller.on('create_bot',function(bot,config) {

  if (_bots[bot.config.token]) {
    // already online! do nothing.
  } else {
    bot.startRTM(function(err) {

      if (!err) {
        trackBot(bot);
      }

      bot.startPrivateConversation({user: config.createdBy},function(err,convo) {
        if (err) {
          console.log(err);
        } else {
          convo.say('I am a bot that has just joined your team');
          convo.say('You must now /invite me to a channel so that I can be of use!');
        }
      });

    });
  }

});


// Handle events related to the websocket connection to Slack
controller.on('rtm_open',function(bot) {
  console.log('** The RTM api just connected!');
});

controller.on('rtm_close',function(bot) {
  console.log('** The RTM api just closed');
  // you may want to attempt to re-open
});

controller.setupWebserver(process.env.port,function(err,webserver) {
  controller.createWebhookEndpoints(controller.webserver);

  controller.createOauthEndpoints(controller.webserver,function(err,req,res) {
    if (err) {
      res.status(500).send('ERROR: ' + err);
    } else {
      res.send('Success!');
    }
  });
});

var _bots = {};
function trackBot(bot) {
  _bots[bot.config.token] = bot;
}

controller.storage.teams.all(function(err,teams) {

  if (err) {
    throw new Error(err);
  }

  // connect all teams with bots up to slack!
  for (var t  in teams) {
    if (teams[t].bot) {
      controller.spawn(teams[t]).startRTM(function(err, bot) {
        if (err) {
          console.log('Error connecting bot to Slack:',err);
        } else {
          trackBot(bot);
        }
      });
    }
  }

});

/**************************
 * Reply
 **************************/

controller.hears('hi', ['direct_message','direct_mention','mention'],function(bot,message) {
  var reply = {
    "text": "朝にする挨拶は?",
    "attachments": [{
      "text": "どれか1つ選んでね",
      "fallback": "failed...",
      "callback_id": "my_quiz",
      "color": "#3AA3E3",
      "actions": [
        {
          "type": "button",
          "name": "atari",
          "text": "おはよう"
        },
        {
          "type": "button",
          "name": "hazure",
          "text": "こんにちは"
        },
        {
          "type": "button",
          "name": "hazure",
          "text": "こんばんわ"
        }
      ]
    }]
  };
  bot.reply(message, reply);
});

controller.on('interactive_message_callback', function(bot, message) {
  var users_answer = message.actions[0].name;
  if (users_answer == "atari") {
    bot.replyInteractive(message, "正解!");
  }else {
    bot.replyInteractive(message, "はずれ。。。。");
  }
});

コードは以下を参考にした

実行!

clientId=CLIENT_ID clientSecret=CLIENT_SECRET port=3000 node bot.js