GASを使うときにAPIのWrapperを作ってる人なんているだろうか。

Google App Scriptの APIって微妙に使いづらいこと多いので、
自分で、ラッパー作ることが多いです。
以下は個人のアカウントごとにオブジェクトを作成する
Google Calendarのプロトタイプデザインパターンっぽい実装。

```js
function createDefaultEvents_(){
  var now = Now();
  now.month--;
  return [
   {
     title: EVENT_LABEL.READY,
     start: new Date(now.year,now.month,now.date,10,30),
     end:new Date(now.year,now.month,now.date,11,00)
   },
   {
     title: EVENT_LABEL.DONE,
     start: new Date(now.year,now.month,now.date,13,00),
     end:new Date(now.year,now.month,now.date,14,00)
   }
  ];
}
   
function Event_(title,start,end){
  return {
     title: title,
     start: start,
     end: end
   }
}

function getCalendarManger(id){
  var _id = id || "hogecalender"
  return  CalendarManager_(_id)
}

function registerEvent_(calendar){
  return function(address,event){
    Logger.log(event);
    return calendar.createEvent(event.title,event.start,event.end,{guests:address,sendInvites:false});
  };
}

function getEvent_(calendar){
  return function(title,start,end){
    var calEvents = calendar.getEvents(start,end);
    var intMaxIndex = calEvents.length;
    for (var i = 0;i<intMaxIndex;i++){
        if(calEvents[i].getTitle()===title){
          return calEvents[i];
        }
    }
  };
}

function deleteGuest_(address,event){  
   if(existGuest_(address,event)) event.removeGuest(address); 
}

function existGuest_(address,event){
    var eventGuests = event.getGuestList();
    eventGuest.forEach(function(v,i){
      if(address == v.getEmail()){
        return true;
      }
    });
}

function addGuest_(address,event) {
  event.addGuest(address);
}


function CalendarManager_(id){
  var _calendar = CalendarApp.getCalendarById(id);
  var registerEvent = registerEvent_(_calendar);
  var getEvent = getEvent_(_calendar);
  return {
    "calendar":_calendar,
    "getEvents":function(startDate,endDate){
      return _calendar.getEvents(startDate, endDate);
    },
    "Event":Event_,
    "registerEventsAssignees":function(addresses,events) {
      var _events = events || DEFAULT_GC_EVENT_;     
      _events.forEach(function(event){
        registerEvent(addresses.join(","),event);
      });
    },
    "registerEvents":function(address,events) {
      var _events = events || DEFAULT_GC_EVENT_;
      _events.forEach(function(event){
        registerEvent(address,event)
      });
    },
    "registerEvent":registerEvent,
    "deleteAllEventsForDay":function(date){
      var calEvents = _calendar.getEventsForDay(date);
      var intMaxIndex = calEvents.length;
      for (var i = 0;i<intMaxIndex;i++){
        calEvents[i].deleteEvent();
      }
    },
    "getEvent":getEvent,
    "deleteEvents":function(events){
      var _events = events || DEFAULT_GC_EVENT_;
      _events.forEach(function(_event){
        getEvent(_event.title,_event.start,_event.end).deleteEvent();      
      });
    },
    "deleteEvent":function(event){
      var _event = getEvent(event.title,event.start,event.end);
      _event.deleteEvent();
    },
    "changeGuests":function(origin,newer,event){
      var _event = getEvent(event.title,event.start,event.end);
      origin.forEach(function(v){deleteGuest(v,_event)});
      newer.forEach(function(v){addGuest(v,_event)});
    },
    "addGuest":function(address,event){
      var _event = getEvent(event.title,event.start,event.end);
      addGuest_(address,_event);
    },
    "deleteGuest":function(address,event){
      var _event = getEvent(event.title,event.start,event.end);
      deleteGuest_(address,_event);
    },
  };
}

function Now(){
  var _date = new Date();
  return {
    instance:_date,
    year: _date.getYear(),
    month: _date.getMonth()+1,
    date: _date.getDate(),
    hour: _date.getHours(),
    day: _date.getDay(),
  }
}
```
Google Spread Sheetをurlで指定するときもwrapper作ると便利です。
url指定で欲しいスプレッドシートのプロトタイプオブジェクトを手に入れます。
スプレッドシート内の参照したいsheetをaddSheetメソッドでニョキニョキ生や
します。以下はそのまま使えないですが例として

```js

function getSheet1_(){
  var now = Now();
  var _sheet = getSheetByUrlOrDefault_();
  _sheet.addSheet("sheet1","one");
  _sheet.currentSheet = _sheet.addSheet(String(now.year)+'/'+String(now.month));
  return _sheet;
}  

function getSheet2_(){
  var _sheet = getSheetByUrlOrDefault_("https://docs.google.com/spreadsheets/d/hogehogeurl");
  _sheet.addSheet("sheet2","two");
  var now = Now();
  _sheet.targetRow = 8;  
  _sheet.targetColumn = 2 + 4*(now.day - 1);  
  _sheet.getProtectionRange = (function(now){
    return function (sheet){
      return sheet.getRange( _sheet.targetRow, _sheet.targetColumn , 100, 4);
    };
  })(Now());
  return _sheet;
}

function getSheetByUrlOrDefault_(url){
  var _target = url ? SpreadsheetApp.openByUrl(url):
  SpreadsheetApp.getActiveSpreadsheet();
  var _sheets={};
  return{
    main: _target,
    sheet: _sheets,
    addSheet:function(dateStr,alias){
      if(!alias) alias=dateStr;
      _sheets[alias] = _target.getSheetByName(dateStr);
      this[alias] = _sheets[alias];
      return _sheets[alias];
    }
  };
}
```

```js

var a = getSheet1_();
var b = getSheet2_();

a.one 
b.two
```
とかやると、スプレッドシートaのsheet1,スプレッドシートbのsheet2,など
にアクセスできて、使いたいシートだけobjectに生やしとけば便利に使えます。


＊＊　ただ後に普通にprototypeでclass宣言できることを知りました。＊＊
以下はGmailのパラメータを監視して、正規表現マッチする
メールがあれば、slackに通知するようなスクリプトsendSlackChannel
の実装はincoming webhookでメッセージ投げる簡単な関数なので割愛。
色々なことを想定して設計したけど、結局本文中のパラメータマッチしか
使わなかったので、雑な実装になっているのは多めに見てください。

```js
(function(global){
  function GmailBoxWatcher(watch_box_label,params){
    this._watchLabel = this.setWatchLabel(watch_box_label);
    this._keyWords = { 
      or:params.or,
      and:params.and
    };
    this._textType = params.textType;
    this._recentlyThreads = this._watchLabel.getThreads(0, GmailBoxWatcher.LIMIT_THREADS);
    this._slackChannel = params.slackChannel;
    this.newMessagesCache = [];
  }
  GmailBoxWatcher.prototype.setWatchLabel = function(watch_box_label){
    var count = GmailBoxWatcher.USER_LABELS.length;
    for(var i = 0; i < count; i++){
      var _label = GmailBoxWatcher.USER_LABELS[i];
      if(_label.getName() === watch_box_label)
        return _label;
    }
    return false;
  }
  GmailBoxWatcher.prototype.getNewMessages = function(){
    if(this.newMessagesCache.length>0) return this.newMessagesCache;
    return GmailBoxWatcher.prototype._setNewMessages();
  }
  GmailBoxWatcher.prototype.notifyMessageToSlack = function(message){
    var sendText = this._getNotifyMessage(message);
    if(sendText){
      sendText = "hogehoge";
      Logger.log(sendText);
      Logger.log(this._slackChannel);
      sendSlackChannel(this._slackChannel,sendText)  
    }
  }
  GmailBoxWatcher.prototype._getNotifyMessage = function(message){
    if(!message.isUnread()) return;
    this.newMessagesCache.push(message);
    var _text = this._textForCondition(message);
    if(!this._satisfyAllMatchConditions(_text)) return;
    if(this._satisfyArbitaryMatchConditions(_text)){
      return this._makeNotifyMessage(message);
    } 
    return;
  }
  GmailBoxWatcher.prototype._makeNotifyMessage = function(message){
    //ToDo:送るテキストのフォーマットを設定する。
    return  message.getPlainBody();
  }
  GmailBoxWatcher.prototype._textForCondition = function(message){
    switch(this._textType){
      case "title":
      case "body":
      case "to":
      case "from":
      case "cc":
      case "attachment":
      case "id":
      default:
      return message.getPlainBody();
    }
  }
  GmailBoxWatcher.prototype._setNewMessages = function(){
    var count_i = this._recentlyThreads.length;
    for(var i = 0; i < count_i; i++){
      var _messages = this._recentlyThreads[i].getMessages();
      var count_j = _messages.length;
      for(var j = 0; j < count_j; j++){
        this.notifyMessageToSlack(_messages[j]);
      }
    }
    return this.newMessagesCache;
  }
  GmailBoxWatcher.prototype._satisfyArbitaryMatchConditions = function (message){
    var keyWords = this._keyWords.or;
    var re = new RegExp(keyWords.join("|"),"g");
    return re.test(message);
  }
  GmailBoxWatcher.prototype._satisfyAllMatchConditions = function (message){
    var keyWords = this._keyWords.and;
    var count = keyWords.length;
    for(var i = 0;i<count;i++){
      var re = new RegExp(keyWords[i],"g");
      if(!re.test(message)) return false;
    };
    return true;
  }
  GmailBoxWatcher.prototype.notifyMessagesToSlack = function(messages){
    if(messages) this.newMessagesCache = messages
    if(this.newMessagesCache.length>0){
      var _messages = this.newMessagesCache;
      var count_j = _messages.length;
      for(var j = 0; j < count_j; j++){  
        this.notifyMessageToSlack(_messages[j]);
      }
    }else{
      return this._setNewMessages();
    }
  }
  GmailBoxWatcher.load = function (options){
    GmailBoxWatcher.USER_LABELS = GmailApp.getUserLabels();
    GmailBoxWatcher.LIMIT_THREADS = options.limitThreads;
  }
  global.GmailBoxWatcher = GmailBoxWatcher;
})(this);

```

1回目の実装は面倒だけど2回目以降は大変便利に使えます。
巷では、es6やtypescriptをBUILDして、gasをかけるnode modulesがあるときいて、やりたい気持ちが高いけど、uploadにservice accountなどを使って割と大掛かりになるので、一旦保留中。このノウハウを確立してちゃんとバージョン管理したい。