Slackのログをスプレッドシートと連携して技術メモの整理に使っている話

この記事は [Google Apps Script Advent Calendar](https://qiita.com/advent-calendar/2018/gas) の1日目です。カレンダー立ち上げたので1日目書きます！

最近、自分の技術メモをSlackとスプレッドシートを使って整理するという試みを始めたので紹介します！

やっていることは、Slackで自分のDMに作業ログを呟いて、1日1回スプレッドシートに保存しているだけです。

## やろうと思った動機

作業中に詰まったところや手順をメモしたりするのですが、これをうまいことまとめたいと思ったのがきっかけです。

僕の場合、ちゃんと整理したわかりやすいメモを残そうとすると、そっちに気を取られて作業の進みが遅くなってしまう。
とはいえ、適当に書いてしまうと後から見てもよくわからない！というのが問題としてありました。

良い方法はないかと考えた結果、

- 作業したときに雑でも良いのでメモをする
- その場でメモをわかりやすく整理することは諦める
- 後で時間がある時にwikiなどにまとめる

というやり方が合っていそうな感じがしたので、Slackとスプレッドシートを連携させることを思いつきました。

仕事中は基本的にSlackを立ち上げているし、自分宛のDMを使って簡単なメモを残すとかはたまにやっていたので、気軽にメモを残しやすい。
スプレッドシートは一覧性が高いので後からまとめやすいし、GASを使って定期的にSlackのAPIを叩けます。

で、実際に試してみました。実装自体はすんなりできました。

## Slack側の設定

まずはSlackのAPIを叩いてメッセージを取得できるようにします。
トークンの取得が必要です。以下の記事などを参考に。

https://qiita.com/ykhirao/items/0d6b9f4a0cc626884dbb

APIを叩いてメッセージを取得します。DMのメッセージを取得するには、`im.history` というAPIを使います。

https://api.slack.com/methods/im.history

僕は自分宛のDMを使いましたが、自分専用のチャンネルを作るとかも良いと思います。
その場合は、`channels.history` というAPIを使います。

https://api.slack.com/methods/channels.history

取得したトークンとチャンネルIDをパラメータに設定してAPIを叩くとこんな感じで返ってきます。

```json
{
    "ok": true,
    "messages": [
        {
            "type": "message",
            "user": "xxxxxxxxxx",
            "text": "xxxxxxxxxxxxxxxxxxxx",
            "client_msg_id": "xxxxxxxxxx",
            "ts": "xxxxxxxxxx"
        },
        {
            "type": "message",
            "user": "xxxxxxxxxx",
            "text": "xxxxxxxxxxxxxxxxxxxx",
            "client_msg_id": "xxxxxxxxxx",
            "ts": "xxxxxxxxxx"
        },
        …
    ]
}
```

チャンネルのIDが全然わからなくて困ったのですが、Webでみた時のURL `https://hoge.slack.com/messages/{ID}/` の最後の文字列部分がIDでした。


## GASのスクリプト

SlackのメッセージがAPIで取得できたら、GASで取得するだけです。

僕の場合、スプレッドシートには `日付` `内容` `URL` `状態`　ってのをあらかじめ作ってます。

日付：Slackに投稿した日付
内容：Slackに書いた本文
URL：Slackの本文中にURLがあった時にタイトルとURL
状態：あとで整理したら処理済にする。

コードはこんな感じ。

```js
// Slackからメッセージ取得
function getSlackLog() {
    var requestUrl = 'https://slack.com/api/im.history?';
    var payload = {
        // Slack Token
        'token': 'xxxxxxxxxx',
        // Channel ID
        'channel': 'xxxxxxxxxx',
        // 25時間分のメッセージ取得
        'oldest': parseInt( new Date() / 1000 ) - (60 * 60 * 25)
    }

    // パラメータの設定
    var param = [];
    for (var key in payload) {
        param.push(key + '=' + payload[key]);
    }
    requestUrl += param.join('&');

    return UrlFetchApp.fetch(requestUrl);
}

// 取得したSlackのメッセージをスプレッドシートに保存
function setSlackLog() {
    // Slackのメッセージを取得して逆順に
    var response = JSON.parse(getSlackLog());
    var messages = response.messages.reverse();

    // スプレッドシートの情報取得
    var ss = SpreadsheetApp.getActiveSpreadsheet();
    var sheet = ss.getSheets()[0];
    var lastrow = sheet.getLastRow();

    // メッセージをシートに書き込む
    for ( var i = 0; i < messages.length; i++ ){
        // 日付
        sheet.getRange(lastrow + i + 1, 1).setValue(convertTimestamp(messages[i].ts));
        // 内容
        sheet.getRange(lastrow + i + 1, 2).setValue(messages[i].text);
        // リンクがあればリンクURL
        if (messages[i].attachments) {
            sheet.getRange(lastrow + i + 1, 3).setValue(messages[i].attachments[0].title + String.fromCharCode(10) + messages[i].attachments[0].title_link);
        }
    }
}

// UNIXタイムスタンプを変換
function convertTimestamp(timestamp) {
    var date_format = function(num) {
        return ( num < 10 ) ? '0' + num  : num;
    };
    var d = new Date(timestamp * 1000);
    var date = d.getFullYear() + '/';
    date += date_format( d.getMonth() + 1 ) + '/';
    date += date_format( d.getDate() ) + ' ';
    date += date_format( d.getHours() ) + ':' + date_format( d.getMinutes() );

    return date;
}
```

コードは[Github](https://github.com/KanDai/gas/blob/master/slack_to_sheet/script.gas)にもアップしてます。

`setSlackLog` という関数を、1日1回に定期実行しています。
URLとかは1つしか反映されません。自分用なので手抜きです。

ぼかしますが、実行されるとこんな感じになります。

![slacklog.jpg](https://qiita-image-store.s3.amazonaws.com/0/3531/075086b9-cb00-b508-3bab-081fb7587da3.jpeg)

Slackのメモを25時間分にしてるのは、GASの定期実行の時間が○時〜○時の間のようにアバウトだから念のためです。

今の所はこんな感じで運用してますが、それなりに満足しています。

GASはアイディア次第で色々できるので良いですね！
明日以降の記事も楽しみです！