JavaScript
GoogleAppsScript
moment.js
Slack

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

はじめに

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

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

やりかた

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

Moment.js を ライブラリとして追加する

日付&時刻の便利ライブラリ「Moment.js」をGoogle Apps Scriptで使う方法 に従って追加します。

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

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

POST ハンドラを書く

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

仕様

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

スクリーンショット 2018-02-03 18.18.51.png

コード

  • どのコマンドで発火したかは e.parameter.command
  • コマンドと一緒に入力されたパラメータは e.parameter.text
  • レスポンスは ContentService を使ってJSONで返す
  • どんなJSON返したらどう表示されるかは Attaching content and links to messages | Slack を参照
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にコマンドとして登録する

スクリーンショット 2018-02-03 17.10.21.png

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

スクリーンショット 2018-02-03 17.10.58.png

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

スクリーンショット 2018-02-03 17.11.30.png

スクリーンショット 2018-02-03 18.20.08.png

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

スクリーンショット 2018-02-03 18.20.37.png

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

スクリーンショット 2018-02-03 17.16.22.png

今後

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

おまけ

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

スクリーンショット 2018-02-04 1.53.26.png
`

  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);
    }
  }