SlackのReactionのEventに応じて〇〇する

この記事を読んだらできるようになること

  • SlackのReactionが押されたときに、何かの処理をしたりできるようになります

この記事で伝えたいこと

  • Real Time Messaging APIとEvents APIの存在
  • Slackをハックする楽しさ

今回作ったもの

f:id:serinuntius:20171211203819p:plain f:id:serinuntius:20171211205232p:plain

  • リアクションがたくさんある投稿(質問内容)が上にくる質問システム

(弊社では年に2回ぜんいん社長合宿というものがあるのですが、質問タイムのときに特定のチャンネルで呟いてもらい、共感した人はReactionを押すと注目度の高い質問が上の方へ来るという仕組みです。司会がWeb上のコンテンツをプロジェクターで表示しながら、質問に答えていくというスタイルでやりたいと、今年の司会であるはまーさんから製作依頼を受けました。)

このエントリーのターゲット

  • SlackのReactionを使って何かしてみたい人

* すでにSlackのReal Time Messaging APIやEvents APIを扱ったことがある人は退屈だと思います。

はじめに

こんにちは。 Tech KAYAC Advent Calendar 2017の16日目の記事をお届けします。受託チーム(CL)の新卒サーバサイドエンジニア、 serinuntiusこと芹川です。

皆様、Slackをハックしてますか?

趣味でSlackのBotを製作して社内で公開してから、Slack関係のソフトウェアの製作依頼や質問が来るようになりました。 嬉しいことですね!

早速ですが、Botを作成していきましょう!

SlackのBotsを作成する

1. Apps を押す

f:id:serinuntius:20171214181745p:plain

2. View App Directory を押す

f:id:serinuntius:20171214181840p:plain

3. Bots と打って選択

f:id:serinuntius:20171214182127p:plain

4. Botを追加する権限があれば

Add configuration が選べる。

f:id:serinuntius:20171214182441p:plain

もし、権限がないとこんな表示になると思います。 Request to install を押すとAdminの人とかに(たぶん)通知がいくので承認してもらいましょう!

f:id:serinuntius:20171214182745p:plain

5. Bot名を適当に入れて鍵を入手しましょう

f:id:serinuntius:20171215173355p:plain

後で使うので、コピーしておいてください。

言うまでもないがこの鍵は慎重に扱いましょう! (漏れるとBotがいるチャンネルでの会話が筒抜けになる可能性がある)

その他、アイコン等も設定しておくと良いと思います。 まったくつぶやかないBotなら設定しなくてもいいと思います。

6. Botを部屋に招待しておく

これが終わればSlack側の設定は終わったはず!!!

BotがいるチャンネルのReactionだけが取れます。

f:id:serinuntius:20171214185429p:plain

コードの部分

今回はサクッと作りたかったので、Node.jsで実装しました。 本当はServerless & Lambdaで作ろうと思ってたから・・・

1. 依存ライブラリのインストールとか

mkdir slack-reaction
cd slack-reaction
yarn add @slack/client dotenv
echo SLACK_BOT_TOKEN=xoxb-xxxxxxx > .env

2. ミニマムな構成のコード

// main.js
const RtmClient = require('@slack/client').RtmClient;
const RTM_EVENTS = require('@slack/client').RTM_EVENTS;
const dotenv = require('dotenv');

dotenv.load();


const botToken = process.env.SLACK_BOT_TOKEN || '';

const rtm = new RtmClient(botToken);

rtm.on(RTM_EVENTS.REACTION_ADDED, event =>{
  console.log('Reaction added:', event);
  // ここに好きな処理を書きましょう!!!
});


rtm.start();

特に説明するほどのこともないかと思いますが、こんな感じに書いて

node main.js

で起動させて、

f:id:serinuntius:20171215122845p:plain

SlackでBotを招待したチャンネルで呟いてReactionを付けると次のようなログが吐き出されると思います。

Reaction added: { type: 'reaction_added',
  user: 'U6HJ69ECD',
  item: 
   { type: 'message',
     channel: 'C7JHG5GQG',
     ts: '1513301808.000130' },
  reaction: '100',
  item_user: 'U6HJ69ECD',
  event_ts: '1513301811.000176',
  ts: '1513301811.000176' }

各項目について一応説明しておくとこんな感じ。 event_tsとtsの違うがよくわからん・・・。

key 説明
user Slack内部で使われているuserのID
item.type messageとかfileとかfile_commentとか
item.channel Slack内部で使われているチャンネルのID
item.ts messageのuniqなtimestamp(これがメッセージのIDみたいなもの)
reaction reactionの名前
item_user そのmessageを書いたuser
event_ts eventが発生したtimestamp(↓違いがわからん)
ts timestamp(↑と違いがわからん)

これを見ていてびっくりするのが、messageの内容が書かれていないよう(激寒)ってこと。

なんとかして、reactionが付いたmessageが何か知りたい。

そう思ってググってたら、下記のStackoverflowが見つかりました。

slack api - How to search for a message by a specific ts - Stack Overflow

どうやら、conversation.history APIとmessageのtsを使えば取れそうだ。

3. 改良したメッセージの内容がわかるやつ

const RtmClient = require('@slack/client').RtmClient;
const RTM_EVENTS = require('@slack/client').RTM_EVENTS;
const WebClient = require('@slack/client').WebClient;
const dotenv = require('dotenv');
dotenv.load();

const botToken = process.env.SLACK_BOT_TOKEN || '';

const rtm = new RtmClient(botToken);
const web = new WebClient(botToken);


rtm.on(RTM_EVENTS.REACTION_ADDED, reaction =>{
  console.log('Reaction added:', reaction);
  web.conversations.history(reaction.item.channel, {
    latest: reaction.item.ts,
    limit: 1,
    inclusive: true,
  }, (err, res) => {
    if (err) {
      console.log(err);
      return
    }
    const messageText = res.messages[0].text;
    console.log(messageText)
  });
});


rtm.start();

よしよし!イイ感じに取れた。

けど、普通に考えてユーザ名も知りたいですよね・・・。

4. ユーザの名前もわかる版(完成版)

そろそろ、コールバックが辛くなるのでPromise化もしましょう!

users.info APIでuserIDからuserの名前を調べる。

const RtmClient = require('@slack/client').RtmClient;
const RTM_EVENTS = require('@slack/client').RTM_EVENTS;
const WebClient = require('@slack/client').WebClient;
const dotenv = require('dotenv');
dotenv.load();

const botToken = process.env.SLACK_BOT_TOKEN || '';

const rtm = new RtmClient(botToken);
const web = new WebClient(botToken);

const getMessageText = (event) => {
  return new Promise((resolve, reject) => {
    web.conversations.history(event.item.channel, {
      latest: event.item.ts,
      limit: 1,
      inclusive: true,
    }, (err, res) => {
      if (err) {
        reject(err);
      }
      const _messageText = res.messages[0].text;
      const _userID = res.messages[0].user;
      const _reaction = event.reaction;
      resolve({_messageText, _reaction, _userID})
    });
  })
};

const getName = (userID) => {
  return new Promise((resolve, reject) => {
    web.users.info(userID, (err, res) => {
      if (err) {
        reject(err)
      }
      resolve(res.user.name)
    })
  })
};


rtm.on(RTM_EVENTS.REACTION_ADDED, event => {
  let messageText, reaction, userName;

  if (event.item.type !== 'message') {
    console.log('this is not message');
  }
  getMessageText(event)
    .then(({_messageText, _reaction, _userID}) => {
      messageText = _messageText;
      reaction = _reaction;
      return getName(_userID)
    })
    .then(_userName => {
      userName = _userName;
      console.log(messageText, reaction, userName);
    })
    .catch((err) => {
      console.error(err);
    })

});


rtm.start();

これで、何を作るにしてもだいたい必要になる3種の神器がそろいました。

動作イメージ

f:id:serinuntius:20171215165458g:plain

実際のアプリケーションでは、DyanmoDBに保存したりフロントから参照するためにExpressでAPI作ったりしたのですが、今回の大筋とはかけ離れるのでやめておきます。

一応サンプルをGitHubにおいておきます。

github.com

おもったこと

今回あまり下調べをせずに勢いに任せてReal Time Messaging APIを使って実装してしまったのですが、Events APIの方を使えばいつも使っているお気に入り構成のServerless Framework & AWS Lambda & DynamoDB が出来たのにな〜って強く思いました。

Lambdaでも出来なくはないと思うのですが、タイムアウトが300秒なので5分毎に実行させて・・・

みたいなことになるので、あまりキレイな設計ではないですね。

Events APIの方を使って、API GatewayとLambdaでAPIを作ってReactionごとにAPIコールしてもらうのが一番いい構成です。(たぶん) 今回のアプリケーションはec2のt2.nanoとかで動かしました。

おわりに

いかがだったでしょうか。 今回はSlackのReactionごとに何かするアプリケーションを作りました。

Slackをハックする楽しみが少しでも伝われば幸いです。

カヤックではSlackをハックするのがエンジニアも、そうでないエンジニアも募集しております!

明日は tamiflu さんの GoogleTestかなあ です。乞うご期待!