Slackでリアクションをした際にツイートできるようにしてみた

# はじめに
プライベートでチーム開発をしているのですが、モチベーションを保つことは難しいです。

モチベーションを上げるための施策として前回は[GitHubのWebhookでプルリクエストをマージした際にツイートできるようしてみた](https://qiita.com/turmericN/items/f6267a75be45c51eaed7)でGitHubからツイートをする仕組みを作成しました。
そして、今回はSlackのリアクションからツイートできる仕組みを解説となります。

**※注意点**
現時点のソースコードはかなり雑になっています。
適宜修正する予定のため、最新のコードは[GitHub](https://github.com/nsuzuki7713/post-tweet-from-slack)より確認をお願いします。

# 動作イメージ
SlackでコメントにTwitterのリアクションをすることで、ツイートを行います。
<img width="760" alt="スクリーンショット 2019-05-14 21.55.15.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/226008/cd76504c-d440-3556-a854-249f60a64a23.png">

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">今からQiitaに投稿する記事を書くぞー！！<br>がんばるぞい💪<br>slackからの投稿です。</p>&mdash; a6s-cloud (@CloudA6s) <a href="https://twitter.com/CloudA6s/status/1128282555784908806?ref_src=twsrc%5Etfw">2019年5月14日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# 構成
構成は前回書いた「[GitHubのWebhookでプルリクエストをマージした際にツイートできるようしてみた](https://qiita.com/turmericN/items/f6267a75be45c51eaed7)」と非常に似たような構成となります。GitHubの箇所がそのままSlackに変更になっただけです。

前回はPythonを使用したので今回は何となくNode.jsにしてみました。

<img width="1027" alt="スクリーンショット 2019-05-20 0.29.26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/226008/31d99754-28aa-272c-ea26-c1f15b1bb651.png">



# 環境構築
「Twitter API」と「lambdaとAPI Gatewayの設定」は前回書いて[GitHubのWebhookでプルリクエストをマージした際にツイートできるようしてみた](https://qiita.com/turmericN/items/f6267a75be45c51eaed7)を参考にしていただければです。

## Slack APIの設定
[Slack API](https://api.slack.com/)からSlack Appを作成します。
Request URLですが初回認証する際にはchallengeの項目の内容（トークン）をレスポンスで返す必要があります。

イベントはreaction_addedを登録しておきます。（何かしらのリアクションをした際に発火されます）

設定に関してはこちらの記事が分かりやすかったです。
[AWS初心者でもわかる！ ブラウザ上で完結！ AWS+Slack Event APIを使ったSlackボット超入門](https://qiita.com/mido_app/items/fcbbdb2bcce3edf0d3f5#slack-api%E3%81%AE%E3%82%A2%E3%83%97%E3%83%AA%E4%B8%8A%E3%81%AB%E3%83%9C%E3%83%83%E3%83%88%E3%83%A6%E3%83%BC%E3%82%B6%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B)

<img width="943" alt="スクリーンショット 2019-05-14 22.43.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/226008/847f0478-ea71-1e0f-b3c4-ff267244a4ab.png">

# lambda実装
slack apiの仕様で3秒以内にレスポンスを返さなければ、再度リクエストが行われてしまいます。

今回は外部APIを呼んでいるためレスポンス時間が5秒ぐらい掛かり、リトライ実行されてしまいました。
レスポンスを返すlambdaと非同期実行するlambdaを用意することで回避可能であるとのことですが、今回は失敗を許容する実装にしています。。
※適宜修正する予定のため、最新のコードは[GitHub](https://github.com/nsuzuki7713/post-tweet-from-slack)より確認をお願いします。

slackのtokenは設定画面から確認できます。
<img width="934" alt="スクリーンショット 2019-05-20 1.08.57.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/226008/e5736437-6ea9-3cd4-f3bd-15271ec4fd4f.png">


```javascript

exports.handler = (event, context, callback) => {
  // slack apiの仕様で3秒以内にレスポンスを返さなければ、再度リクエストを行う
  // 2回目のリクエストの場合は終了する
  if(event.headers["X-Slack-Retry-Num"]){
    console.log("リトライのため終了");
    console.log(event);
    callback(null, {statusCode: 200,body: JSON.stringify({ok:"ok"})});
    return;
  }

  // レスポンスからデータを取得
  const body = JSON.parse(event.body);
  console.log(event);
  console.log("channel:" + body.event.item.channel);
  console.log("latest:" + body.event.item.ts);
  console.log("reaction:" + body.event.reaction);
  
  // リアクションが「tweet」リアクション以外なら終了
  if(body.event.reaction !== 'tweet'){
    console.log("tweetリアクションではないので終了")
    callback(null, {statusCode: 200,body: JSON.stringify({ok:"ok"})});
    return;
  }
  
  const twitter = require('twitter');
  const twitter_client = new twitter({
      consumer_key: 'xxxxxxxxxxxxx',
      consumer_secret: 'xxxxxxxxxxxxx',
      access_token_key: 'xxxxxxxxxxxxx',
      access_token_secret: 'xxxxxxxxxxxxx'
  });
  
  // axios を require してインスタンスを生成する
  const axiosBase = require('axios');
  const axios = axiosBase.create({
    headers: {
      'Content-Type': 'application/json',
      'X-Requested-With': 'XMLHttpRequest'
    },
    responseType: 'json'  
  });
  
  // Slack APIからコメントを取得するためにパラメータを作成
  const queries = {
    token: "xxxxxxxxxxxxx",
    latest:body.event.item.ts,
    limit:1,
    inclusive:true,
    channel: body.event.item.channel
  };

  // Slack APIからコメントを取得
  axios.get('https://slack.com/api/conversations.history/',{params: queries})
    .then(res => {
      // 絵文字文字列を絵文字に変換
      // 使い方は「Node.jsで絵文字を扱うための、node-emojiライブラリの使い方」に書いています
      const emoji = require('node-emoji')
      const text = emoji.emojify(res.data.messages[0].text);
      console.log("ツイート文章")
      console.log(text);
      
      // twitter apiでtweetを行う
      twitter_client.post('statuses/update', {status: text + '\nslackからの投稿です。'},  function(error, tweet, response){
        if(error) {
            console.log('Twitter APIエラー.');
            console.log(error);
        }
        console.log('Twitter完了');
      });
    }).catch(err => 
      console.log(err);
  });
  
  callback(null, {statusCode: 200,body: JSON.stringify({ok:"ok"})});
  return;
};

```

# 参考URL
[Node.jsで絵文字を扱うための、node-emojiライブラリの使い方](https://qiita.com/turmericN/items/33ba9bcd8bed2a63f119)
[GitHubのWebhookでプルリクエストをマージした際にツイートできるようしてみた](https://qiita.com/turmericN/items/f6267a75be45c51eaed7)
[AWS Lambda + Node.jsでTwitter Botを開発する](http://ykubot.com/2017/08/28/lambda-nodejs-twitter/)
[AWS初心者でもわかる！ ブラウザ上で完結！ AWS+Slack Event APIを使ったSlackボット超入門](https://qiita.com/mido_app/items/fcbbdb2bcce3edf0d3f5)
[SlackのReactionのEventに応じて〇〇する](https://techblog.kayac.com/slack-reaction-event)
[Slack投稿を元にしてTwitter運用するために、GASで一定のリアクション数に達したSlackメッセージを収集した話](https://qiita.com/coboco/items/56493853e3f2e5628d29)
[Slack Bot をサーバーレスで運用する時の、タイムアウト対策【小技】](https://qiita.com/saken649/items/b70e462ae41614b72f77#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB)
[[AWS lambda] Slack Botがタイムアウトで何度もレスポンスするのを防ぐ](https://qiita.com/kamata1729/items/fc6c31a3039599b48305)