GoogleAppScript で UnixTime と 時刻文字列を相互変換してくれる Slack コマンドをつくる （ついでに golang の時刻フォーマットも）

はじめに
--------------------
所属企業のプロダクトで、時間をUnixTimeとして扱うことが多く、変換がめんどくさいなーと思っていました。（自分は「UnixTime 変換」で検索してトップに出てくる [Unixtime相互変換ツール](http://tool.konisimple.net/date/unixtime) あたりを毎回検索して使用していましたが、同僚のなかには「いや、 `date` 叩けよ」 という男前がいたり）

そこで、 Slack ハックの練習に、スラッシュコマンドに仕立ててみたいと思います。

やりかた
--------------------
今回も、私の Qiita で何度か登場している Google App Script を使います。

### Moment.js を ライブラリとして追加する
[日付＆時刻の便利ライブラリ「Moment.js」をGoogle Apps Scriptで使う方法](https://tonari-it.com/gas-moment-js-moment/) に従って追加します。

詳細は上の記事を参照してください（手抜き）

- リソース > ライブラリ... を選択
- 「ライブラリを追加」 で下記のプロジェクトキーを入力
- バージョンを選択

```
プロジェクトキー : MHMchiX6c1bwSqGM1PZiW_PxhMjh3Sh48
```


### POST ハンドラを書く
Slack がコマンドを認識した際にアクセスする先の API を GAS で作成します。

#### 仕様

- `/now` : `現在時刻の UnixTime（ミリ秒まで）` と `現在時刻の文字列表現（UTC）` を返却
- `/u2d <unix time>`  : `指定時刻の UnixTime（ミリ秒まで）` と `指定時刻の文字列表現（UTC）` を返却
- `/d2u <date str>`  : `指定時刻の UnixTime（ミリ秒まで）` と `指定時刻の文字列表現（UTC）` を返却

<img width="519" alt="スクリーンショット 2018-02-03 18.18.51.png" src="https://qiita-image-store.s3.amazonaws.com/0/111801/57f27e05-de71-2b31-0c45-678369164d72.png">

#### コード

- どのコマンドで発火したかは `e.parameter.command`
- コマンドと一緒に入力されたパラメータは `e.parameter.text`
- レスポンスは `ContentService` を使ってJSONで返す
- どんなJSON返したらどう表示されるかは [Attaching content and links to messages | Slack](https://api.slack.com/docs/message-attachments) を参照

```js
var FORMAT = "YYYY-MM-DDTHH:mm:ss.SSSZ";
var COLOR = "#84dccf";

function doPost(e) {
  
  var cmd = e.parameter.command;
  
  if (cmd == "/u2d") {
    var m = Moment.moment(e.parameter.text, "X").utc();
    var u = m.valueOf() * 0.001;
    var d = m.format(FORMAT);
    
    var res = {
      attachments: [{
        color: COLOR,
        title: "unixtime -> date : " + e.parameter.command + " " + e.parameter.text,
        text: u + "\n" + d,
      }],
    };
    return createContent(res);
  }
  
  if (cmd == "/d2u") {
    var m = Moment.moment(e.parameter.text).utc();
    var u = m.valueOf() * 0.001;
    var d = m.format(FORMAT);
    
    var res = {
      attachments: [{
        color: COLOR,
        title: "date -> unixtime : " + e.parameter.command + " " + e.parameter.text,
        text: u + "\n" + d,
      }],
    };
    return createContent(res);
  }
  
  if (cmd == "/now") {
    var m = Moment.moment().utc();
    var u = m.valueOf() * 0.001;
    var d = m.format(FORMAT);

    var res = {
      attachments: [{
        color: COLOR,
        title: "now : " + e.parameter.command + " " + e.parameter.text,
        text: u + "\n" + d,
      }],
    };
    return createContent(res);
  }

  return null;
}

function createContent(res) {
  return ContentService.createTextOutput(JSON.stringify(res)).setMimeType(ContentService.MimeType.JSON);
}
```

### ウェブアプリケーションとして公開
- 公開 > ウェブアプリケーションとして導入... を選択
- 「アプリケーションにアクセスできるユーザー」を「全員（匿名ユーザーを含む）」にする
- 「現在のウェブ アプリケーションの URL」をメモしておく

### Slackにコマンドとして登録する
- https://api.slack.com/apps にアクセス
- 「新しいアプリを作成する」 ボタンをクリック

<img width="1372" alt="スクリーンショット 2018-02-03 17.10.21.png" src="https://qiita-image-store.s3.amazonaws.com/0/111801/4b4d088e-3f0a-bd3a-7a86-3e6dc53f3635.png">

- アプリ名を入力
- 導入するワークスペースを選択

<img width="570" alt="スクリーンショット 2018-02-03 17.10.58.png" src="https://qiita-image-store.s3.amazonaws.com/0/111801/e00340ab-bd67-6348-415b-5fb808762ac9.png">

- 「スラッシュコマンド」に移動
- 「新しいコマンドを作成する」 ボタンをクリック

<img width="1375" alt="スクリーンショット 2018-02-03 17.11.30.png" src="https://qiita-image-store.s3.amazonaws.com/0/111801/5f6df150-064f-8486-f266-df265ac27ea6.png">

- コマンド名や使い方の説明を入力
- リクエストURLには [ウェブアプリケーションとして公開](#ウェブアプリケーションとして公開) でメモっておいたURLを入力

<img width="664" alt="スクリーンショット 2018-02-03 18.20.08.png" src="https://qiita-image-store.s3.amazonaws.com/0/111801/3aef8b54-3268-862d-4ee4-a8a514df3d87.png">

- こんな感じでコマンドができる

<img width="1376" alt="スクリーンショット 2018-02-03 18.20.37.png" src="https://qiita-image-store.s3.amazonaws.com/0/111801/1ab10543-0d61-ecd9-e6d4-6445c49a4818.png">

- ワークスペースへインストール

<img width="1374" alt="スクリーンショット 2018-02-03 17.16.22.png" src="https://qiita-image-store.s3.amazonaws.com/0/111801/3233921f-af37-606c-ded8-3e6f7a022c03.png">


今後
--------------------
SlackAppにまとめて公開したい。時間があれば。

おまけ
--------------------
普段 golang を使うことが多いのですが、 時間のフォーマット方法にかなり癖があり（特に日本人には）、こちらも毎回調べることになるので、 linux の date コマンドのフォーマットを打つと golang のフォーマットに変換してくれるものも作りました。 

<img width="501" alt="スクリーンショット 2018-02-04 1.53.26.png" src="https://qiita-image-store.s3.amazonaws.com/0/111801/c74ca287-94e8-c945-89ba-fd23746d0599.png">
`



```js
  if (cmd == "/format") {
    var format = e.parameter.text;
    format = format.replace(/%H/g, "15");
    format = format.replace(/%I/g, "03");
    format = format.replace(/%l/g, "3");
    format = format.replace(/%M/g, "04");
    format = format.replace(/%p/g, "PM");
    format = format.replace(/%S/g, "05");
    format = format.replace(/%a/g, "Mon");
    format = format.replace(/%A/g, "Monday");
    format = format.replace(/%b/g, "Jan");
    format = format.replace(/%B/g, "January");
    format = format.replace(/%d/g, "02");
    format = format.replace(/%m/g, "01");
    format = format.replace(/%y/g, "06");
    format = format.replace(/%Y/g, "2006");
    format = format.replace(/%N/g, "000000000");
    
    var n = /%\dN/.exec(format);
    if (n != null) {
      var s = "";
      for (var i = 0; i<Number(n[0].substring(1,n[0].length-1)); i++) {
        s += "0";
      }
      format = format.replace(/%\dN/g, s);
    }
    
    if ( format.match(/%/g) ) {
      var res = {
        attachments: [{
          color: COLOR,
          title: "format : " + e.parameter.command + " " + e.parameter.text,
          text: "invalid format",
        }],
      };
      return createContent(res);

    } else {
      var res = {
        attachments: [{
          color: COLOR,
          title: "format : " + e.parameter.command + " " + e.parameter.text,
          text: format,
        }],
      };
      return createContent(res);
    }
  }
```