Google Apps Scriptでタスクを忘れない仕組みを作ってみよう

こんにちはtsuttieです(*'▽')
今回はGASをつかってchatworkにタスク忘れを通知をしてみたいと思います。

#1. なにをしたのか

今回は何をしたかというとkintoneのレコード情報を参照して、レコードがなければchatworkに通知する仕組みを色々組み合わせて構築してみました。
定期的に実行できるようにスクリプトの実行にはGAS(Google Apps Script)を使用しています。丁度社内の勉強会(参加者2人・・・)でGASの勉強をしていたのでせっかくなので使ってみた。といった感じです。


#2. GASを使ってみよう

GASの使い方については以下のサイトを参考に実施しました。
[【保存版】初心者向け実務で使えるGoogle Apps Script完全マニュアル](https://tonari-it.com/google-apps-script-manual/)

本当に詳しく記載してくださっているのでこれからGASを使ってみるという方は是非参考にされてください。
基本的な実行ができれば問題ないかと思います。もちろんすべて把握できればいいのですが、あとはやり方を調べ調べという感じで私は使っています・・・


#3. 実際に書いてみる

そんなこんなで以下の流れでスクリプトを組んでみました。

GASの自動実行(時間で設定)
↓
平日かどうか(休日の場合は終了)
↓
kintoneの情報を取得
↓
当日のレコードが存在するか(ある場合には終了)
↓
chatworkにレコードを追加するように通知

こんな感じです。
誰かがやらないといけない。
けど実施者が日によって変わるせいでちょっと忘れがち。
そういったタスクがあったのでこういった方法をとりました。

```
function myFunction() {

　// 平日かどうかを判定する
　// 日曜と土曜を省く
  var currentDate = new Date();
  var weekday = currentDate.getDay();
  if (weekday == 0 || weekday == 6) {
    return;
  }
　// 祝日も省く
  var calendar = CalendarApp.getCalendarById('ja.japanese#holiday@group.v.calendar.google.com');
  if (calendar.getEventsForDay(currentDate, {max: 1}).length > 0) {
    return;
  }
  
　// kintoneAPIを使用するために初期化
  var subdomain = "{サイボウズのサブドメイン}";
  var apps = {
    YOUR_APPLICATION1: { appid: {アプリケーションのID}, name: "{アプリケーション名}", token: "{APIトークンキー}" },
  };
  var kintone_manager = new KintoneManager(subdomain, apps);
  
　// 登録日のフィールドコードが当日のものを取得
  var date = Utilities.formatDate(new Date(), 'Asia/Tokyo', "yyyy-MM-dd");
  var query = '{登録日のフィールドコード} = "' + date + '"';
  var response = kintone_manager.search("YOUR_APPLICATION1", query);
  // ステータスコード
  // 成功すれば200になる
  var code = response.getResponseCode();
  var content = JSON.parse(response.getContentText());
  // レコードの配列が取得できる。
  var records = content.records;

　// 当日のものがあるかどうかを調べる
  if (records[0] == null){
　　// ない場合にはchatworkに通知を実施
    var client = ChatWorkClient.factory({token: '{chatworkトークンキー}'}); //チャットワークAPI
    client.sendMessage({
      room_id:{ルームID}, 
      body: '{メッセージ内容}'});
  }
}


// (1)　ライブラリ直接追加部分(ハマりポイントに記載)
var KintoneManager = (function() {
    "use strict";
    // user, passが指定されれば、パスワード認証
    // 指定されなければ、APIトークン認証
    // appsは以下の形式
    // {
    //    // アプリケーション名はkintoneのデータに依存せず、GAS内のコードで取り扱う専用
    //    YOUR_APP_NAME1: {
    //       appid: 1,
    //       guestid: 2,
    //       name: "日報",
    //       token: "XXXXXXXXXXXXXX_YOUR_TOKEN_XXXXXXXXXXXXXX" // パスワード認証する場合は省略化
    //    },
    //    YOUR_APP_NAME2: {
    //       ...
    //    }
    // }
    function KintoneManager(subdomain, apps, user, pass){
        this.subdomain = subdomain;
        this.authorization = null;
        this.apps = apps;

        if (arguments.length > 3) {
            this.authorization = Utilities.base64Encode(user + ":" + pass);
        } else if (arguments.length > 2) {
            // 引数が3つの場合はエンコード済みの認証情報として処理
            this.authorization = user;
        }
    }
    // レコードの作成
    KintoneManager.prototype.create = function(app_name, records) {
        var app = this.apps[app_name];
        var payload = {
          app: app.appid,
          records: records
        };
        var response = UrlFetchApp.fetch(
            "@1/records.json".replace(/@1/g,this._getEndpoint(app.guestid)),
           this._postOption(app, payload)
        );
        return response;
    };
    // レコードの検索
    KintoneManager.prototype.search = function(app_name, query){
       var q = encodeURIComponent(query);
       var app = this.apps[app_name];
       var response = UrlFetchApp.fetch(
         "@1/records.json?app=@2&query=@3"
            .replace(/@1/g, this._getEndpoint(app.guestid))
            .replace(/@2/g, app.appid)
            .replace(/@3/g, q),
         this._getOption(app)
       );
       return response;
    };
    // レコードの更新
    KintoneManager.prototype.update = function(app_name, records) {
        var app = this.apps[app_name];
        var payload = {
          app: app.appid,
          records: records
        };
        var response = UrlFetchApp.fetch(
            "@1/records.json".replace(/@1/g, this._getEndpoint(app.guestid)),
           this._putOption(app, payload)
        );
        return response;
    };
    // レコードの削除
    KintoneManager.prototype.destroy = function(app_name, record_ids){
       var app = this.apps[app_name];
       var query = "app=" + app.appid;
       for(var i=0; i<record_ids.length;i++){
           query += "&ids[@1]=@2".replace(/@1/g,i).replace(/@2/g,record_ids[i]);
       }
       var response = UrlFetchApp.fetch(
         "@1/records.json?@2"
            .replace(/@1/g, this._getEndpoint(app.guestid))
            .replace(/@2/g, query),
         this._deleteOption(app)
       );
       return response;
    };
    // GETメソッドの時のオプション情報
    KintoneManager.prototype._getOption = function(app) {
       var option = {
          method: "get",
          headers: this._authorizationHeader(app),
          muteHttpExceptions: true
       };
       return option;
    };
    // POSTメソッドの時のオプション情報
    KintoneManager.prototype._postOption = function(app,payload) {
       var option = {
               method: "post",
               contentType: "application/json",
               headers: this._authorizationHeader(app),
               muteHttpExceptions: true,
               payload: JSON.stringify(payload)
       };
       return option;
    };
    // PUTメソッドの時のオプション情報
    KintoneManager.prototype._putOption = function(app,payload) {
       var option = {
               method: "put",
               contentType: "application/json",
               headers: this._authorizationHeader(app),
               muteHttpExceptions: true,
               payload: JSON.stringify(payload)
       };
       return option;
    };
    // DELETEメソッドの時のオプション情報
    KintoneManager.prototype._deleteOption = function(app) {
       var option = {
          method: "delete",
          headers: this._authorizationHeader(app),
          muteHttpExceptions: true
       };
       return option;
    };
    // エンドポイントの取得
    KintoneManager.prototype._getEndpoint = function(guest_id) {
      var endpoint = "https://@1.cybozu.com".replace(/@1/g,this.subdomain);
      if (guest_id == null) {
        return endpoint + "/k/v1";
      } else {
        return endpoint + "/k/guest/@1/v1".replace(/@1/g, guest_id);
      }
    };
    // ヘッダーの認証情報
    KintoneManager.prototype._authorizationHeader = function(app) {
      if (this.authorization) {
         // パスワード認証
         return { "X-Cybozu-Authorization": this.authorization };
      } else if (app.token) {
         // APIトークン認証
         return { "X-Cybozu-API-Token": app.token };
      } else {
        throw new Error("kintone APIを呼ぶための認証情報がありません。");
      }
    };
    return KintoneManager;
})();
```

あとは上記のスクリプトを定期実行に設定してあげれば完成です。(ハマりポイントに記載)
感想は・・・

簡単にできる(戦慄)

でした。

#4. ハマったポイント

・ライブラリの追加
chatwork、kintoneのAPIをたたく部分はライブラリを使用させていただきました。

■ChatWorkClientライブラリの追加
追加の方法は↓を参考にさせていただきました。
参考：[Google Apps ScriptからChatWork APIを使って投稿する](https://qiita.com/y_minowa/items/39db8ca5bffc9c440caf)

■kintoneのAPIの実行
kintoneのAPIを使用するために以下を使用させていただきましたが、「ライブラリの追加」からではなぜかうまくいかず・・・
参考：[kintone とGoogle Apps Script連携](https://qiita.com/Arahabica/items/063877b0da439020d2c2)
上記コード内(1)に直接追加したところ、うまくいってしまったのでそちらで対応しました。

・スクリプトの定期実行
スクリプトの定期実行については初めに紹介させていただきましたサイトより↓部分を参考に設定を行いました。
[Google Apps Script で毎日決まった時刻にスクリプトを実行するトリガー設定](https://tonari-it.com/gas-trigger-set/)
私は午前中の終わりに確認がしたかったので以下のように設定しています。
負荷分散のためか１時間単位での設定になるので細かい通知には不向きかもしれません・・・
![トリガー.PNG](https://qiita-image-store.s3.amazonaws.com/0/198205/125c22f5-670f-a26b-e4bd-1467001b888d.png)

#5. まとめ

今回はGASをつかってchatworkにタスク忘れを通知をしてしてみました。
いろいろ組み合わせて仕組みを作ってみましたが、意外と色んな所に使えそうだなぁ。という印象でした。
これからもなにか場面があれば使ってみたいと思います。

何かご指摘等ありましたら、コメントいただければ幸いです。
よろしくお願いします。('◇')ゞ