~ ToDo 活用 ~ Slack メッセージに絵文字リアクションをつけると Google スプレッドシートにアーカイブする

#:rocket: はじめに
この記事では、ToDo 活用の一環としまして、Slack メッセージに特定の絵文字リアクションをつけたタイミングで、対象メッセージを Google スプレッドシートにアーカイブし、Slack から削除する Google Apps Script (以下 GAS) レシピをご紹介します。

#:back: 背景
[前回の記事](https://qiita.com/knorr03/items/f33faac52bdc5d03ddd9) (削除操作でメッセージをアーカイブ) を読んでいただいた上司から、こんな素敵アイデアが。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430271/d43601ab-1f57-3d8b-ddb5-414e6e01dacd.png)

たしかに「削除」操作ではなく「特定の絵文字リアクション」操作で対象のメッセージをアーカイブできれば

- 意図して削除したメッセージはアーカイブされない
- 削除の場合 3 クリックを要するところ、絵文字リアクションならわずか 2 クリックで済む
- 削除よりも絵文字リアクションのほうがアーカイブ操作感ある (気持ちいい ← 重要

と、どう考えても削除操作よりイケてますので、早速 ~~パクらせてもらお~~ アイデア活用させていただきました。




#:book: レシピ

## ステップ
[前回記事](https://qiita.com/knorr03/items/f33faac52bdc5d03ddd9) の「GAS ウェブ アプリケーション URL の発行」と「Slack App の作成」からの続きとなります。以下のかんたんな 3 ステップで、絵文字リアクションによるアーカイブを実現する、GAS Web アプリケーションを作っていこうと思います。

1. Slack Events API を使い絵文字リアクション イベントを取得・判定
2. 対象メッセージを Google スプレッドシートにアーカイブする
3. Slack Web API を使って対象メッセージを削除する


## 下準備 
上述の機能を実装するため前回作成した Slack App「ToDoArchive」に対して、あらためて必要な Event と Scope の設定をおこなっていきます。まずは [Your Apps](https://api.slack.com/apps) にアクセスして前回作成した Slack App「ToDoArchive」の設定画面にはいります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430271/dbed1f37-408a-ed55-1424-33ac18190c6c.png)


### Event Subscriptions
公式リファレンス [Events API](https://api.slack.com/events-api) を見ながら、取得したいイベントを設定します。今回は「絵文字リアクションが使用された」というイベントのみが取れればよいので、イベント名「reaction_added」を登録します。これにより、Slack 内で絵文字リアクション イベントが発生するたびに GAS へ POST リクエストが届くようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430271/0377de13-eb13-83bd-4fec-9e9f114b7050.png)

### Scopes
次に GAS から「①絵文字リアクションを取得する」「②IM チャンネル内のメッセージを取得する」「③メッセージを削除する」の 3 つがおこなえるよう、スコープ設定 (権限の範囲設定) をおこないます。それぞれ必要なスコープ名は以下のとおりです。

① Access the workspace’s emoji reaction history (reactions:read)
② Access content in user’s direct messages (im:history)
③ Send messages as user (chat:write:user)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430271/1a369aa4-fee3-fda7-ece3-00a972c21a55.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430271/4d150982-75ea-a7f4-99e1-a8b1ad21e0b0.png)

:bulb: 「①絵文字リアクションを取得する」 reactions:read は前項のイベント「reaction_added」登録時点で自動的にスコープ設定をしてくれています。よって、実際に追加設定するのは im:history と chat:write:user の 2 点で OK.

:bulb: 今回は IM チャンネル (自分宛ての DM) のみをターゲットとしているため im:history を設定しています。パブリック チャンネルで実装したい場合は channels:history, プライベート チャンネルの場合は groups:history でいけると思います。

### 取得しておく情報
最後に「OAuth Access Token」と「Channel ID」を取得しておきます。あとで使うので、どっかにコピーしといてください。

< OAuth Access Token >
同「OAuth & Permissions」ページ内で取得します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430271/7170502e-b256-ad65-dd17-f8f476c05b43.png)
< Channel ID >
Slack を開き、チャンネル名を右クリックして [コピー] します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430271/f9829277-e92f-3e7d-165d-ff1b701fe432.png)
そしてどっかに貼り付けて確認できる以下 URL の

https://[ワークスペース名].slack.com/messages/[ここの部分]

が ID に該当します。  

以上で準備完了です。
次は GAS で Web アプリケーションを作っていきましょう。

## 1. 絵文字リアクションの判定
はじめに絵文字リアクション イベントが届いた際、特定の絵文字以外はスルーするコードを書きます。

1. 特定の絵文字を何にするか決めましょう。絵文字名称は Slack 内で確認ができます。
僕は ToDo を **爆発させたい** ので、迷わず boom を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430271/6a92adc1-0d1e-0e4e-0934-fb38d547d3bc.png)

2. 以下のコードで絵文字の種類を判定します。

    ```javascript:hasSpecificEmojiReaction
      /* 絵文字リアクション "boom" が押された場合にのみ実行 */
      var json  = JSON.parse(e.postData.getDataAsString());
      var event = json.event;
      if (!event.reaction) return;
      if (event.reaction !== 'boom') return;
```

## 2. Google スプレッドシートへアーカイブ
続いて絵文字リアクションが付いたメッセージ (とその日時情報) を、GAS にコンテナ バインドされている Google スプレッドシートにアーカイブするコードを書いていきます。

1. GAS の [プロジェクトのプロパティ] を開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430271/4ef4a094-f819-84e2-badf-95e5fdaaa8df.png)

2. [スクリプトのプロパティ] タブより「SLACK_CHANNEL」と「SLACK_ACCESS_TOKEN」というプロパティを追加し、[取得しておく情報](#取得しておく情報) でとったそれぞれの値を貼り付けます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430271/24f1ce58-b1dc-8e91-c7ae-aaeda296b511.png)

3. 以下のコードを書きます。

    ```javascript:archiveMessageToSs
      /* プロパティ ストアよりトークン・チャンネル情報を読み込む */
      var token   = PropertiesService.getScriptProperties().getProperty('SLACK_ACCESS_TOKEN');
      var channel = PropertiesService.getScriptProperties().getProperty('SLACK_CHANNEL');
     
     
       /* 絵文字リアクションのついたメッセージをスプレッドシートにアーカイブする */
       var imHistoryUrl   = 'https://slack.com/api/im.history';
       var messageTs      = event.item.ts;  // リアクションが押されたメッセージのタイムスタンプ
       var messageTsDate  = new Date(messageTs * 1000);
       var reactionTs     = event.event_ts;  // リアクション押下時のタイムスタンプ
       var reactionTsDate = new Date(reactionTs * 1000);
       
       var payload = {
         token    : token,
         channel  : channel, 
         oldest   : messageTs,
         latest   : messageTs,
         inclusive: true,
       };
       
       var options = {
         method     :'post',
         contentType:'application/x-www-form-urlencoded',
         payload    : payload
       };
       
       var response    = UrlFetchApp.fetch(imHistoryUrl, options);
       var contentText = JSON.parse(response.getContentText());
       
       var messageText = contentText.messages[0].text;
       var array       = [reactionTsDate, messageTsDate, messageText];
       
       var ss          = SpreadsheetApp.openById('hogehoge'); // Google スプレッドシートの ID (URL の末尾部分)
       var sh          = ss.getSheetByName('fugafuga');  // 入力先シートの名前
       sh.appendRow(array);
```    
:bulb: Slack 上のすべてのメッセージにはユニーク ID としてタイムスタンプが付いています。Events API でポストされた JSON データより、絵文字リアクションがついたメッセージを確認すると対象のタイムスタンプ情報を取得することができます。そこで Web API メソッド im.history を使って絵文字リアクションがついたメッセージのテキスト データをタイムスタンプで検索・取得しています。  
:bulb: 変数 imHistoryUrl の URL は IM チャンネル用です。別の種類のチャンネルで実装したい場合は種類に応じて、[Scopes](#scopes) と URL 変更が必要です。  
パブリック チャンネル: https://slack.com/api/channels.history
プライベート チャンネル: https://slack.com/api/groups.history

## 3. メッセージを削除
1. 対象メッセージを削除するコードを書いて完成です。こちらも、前回取得したメッセージのタイムスタンプ情報を使い、削除対象となるメッセージを特定しています。

    ```javascript:deleteMessage
      /* 対象メッセージを削除する */
      var chatDeleteUrl = 'https://slack.com/api/chat.delete';
      
      var payload = {
        token  : token,
        channel: channel, 
        ts     : messageTs,
      };
      
      var options = {
        method     :'post',
        contentType:'application/x-www-form-urlencoded',
        payload    : payload
      };
      
      UrlFetchApp.fetch(chatDeleteUrl, options);
```

2. 最後にスクリプト エディター画面の上部にある [公開] から [ウェブ アプリケーションとして導入] を開き、プロジェクト バージョンの更新作業をおこないます (よく忘れて動かないやつ)。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430271/6727b7fe-acf8-eace-1328-a7e5eb79509b.png)

完成！！！ さっそくテストしてみましょう。

## テスト
![2019-06-25_16h03_30.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430271/5c5d52d0-b0bc-cffc-ac9e-a16424ddb131.gif)

できました！ :tada::tada::tada:

#:question: ちょっと気になったこと
[Scopes](#scopes) の設定ですが、試しに im:history, chat:write:user の両スコープを削除 ( reactions:read は Events で使用しているため削除不可) してから Slack App を再インストールして実行しても、なぜか動作する。

別の環境で上記スコープを追加せずに同アプリケーションを新規作成してみたところ動かないので、両スコープは必須な模様。こういうものなんですかね :confused:

#:checkered_flag: まとめ
もともと Slack 上で、完了した ToDo を Gmail のアーカイブみたいな機能で消したいと思って作り始めたので、前回の「削除」よりも、「絵文字リアクション」のほうが遥かにアーカイブらしい操作感があって、とっても良い感じです。よかったら Slack で ToDo 管理、試してみてください。



#:link: 参考
[Slack Events API - Slack](https://api.slack.com/events-api)
[Slack Web API - Slack](https://api.slack.com/web)