5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

LeveragesAdvent Calendar 2017

Day 3

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

Last updated at Posted at 2017-12-11

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

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メソッドでニョキニョキ生や
します。以下はそのまま使えないですが例として


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

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

a.one 
b.two

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

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

(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などを使って割と大掛かりになるので、一旦保留中。このノウハウを確立してちゃんとバージョン管理したい。

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?