チャットでGoogle Calendarを使ってみるの巻

記念すべき初投稿（備忘録）なのにこんな内容でいいのだろうか…

#1. 何故作った…
仕事の合間に、なんとなく「チャットアプリでGoogle Calendarを操作できないかね」と言われる。

###1-1. 何故REST API
GCMは苦手で、ちょうどAzure Functionを触っていたのでその延長で。

###1-2. どんなものを作ろうとした？
テキストを自然言語分析 → Functionで分けてGoogle Calendar APIのメソッドを実行
→ チャットに結果を表示

#2. 大まかな構成
### 2-1. Google APIs
まずAPIを有効化し、サービスアカウントを作ることでここで使うprivatekeyを作ります。

![スクリーンショット 2017-11-06 18.31.41.png](https://qiita-image-store.s3.amazonaws.com/0/192249/3ce566ff-cd1c-8f2f-ccf1-10b4cf94db05.png)

ここでGoogle Calendar APIを選択し、有効化することで、既存のプロジェクトや新しいプロジェクトでAPIが使用可能になります。


![スクリーンショット 2017-11-06 18.38.57.png](https://qiita-image-store.s3.amazonaws.com/0/192249/037086a1-474c-2453-80c8-f76fde5ea968.png)

コンソールで認証可能な場合、OAuthキーの発行 → その他を選ベば、google-auth-libraryで使う client_secret.jsonを発行できます。今回はコンソールで打ち込むのは無理なのでサービスアカウント作成へ。
作成が済めば、自動的にトークンなどが記入されているjsonファイルがダウンロードされるのでprivatekey.jsonに命名。
アップロードしておきましょう。


###2-2. Azure Function
Web App自体はなんでもいいです。筆者、プログラミング初年度でNode.jsの基礎くらいしかわかっておらぬ状態。
Google Apisは Firebaseを推奨していますが、今回Azureのサンプルを流用するため、こちらに。
今後サンプル作成するならデプロイまで簡単なGCMがおすすめ。

ともかく、Example通り作ろうとしたら、google-auth-libraryとやらで認証しなければならない
が、これはコンソールでトークンを打ち返す必要があり、相互作用できない（筆者の能力不足かもですが）Function APPや組み込み済みのアプリケーションのために用意されたJWTを使用。

NPMに用意されているgoogleapisモジュールをインポートし、最初の認証を。

``` Functionのindex.js
let jwtClient = new google.auth.JWT(
  privatekey.client_email,
  null,
  SCOPES: ['https://www.googleapis.com/auth/calendar.readonly'],
  null
); 

jwtClient.authorize(function (err, tokens) {
  if(err) { 
    console.log("auth err: " + err);
    return;
  } else { 
    console.log("auth done"); 
    {APIsに用意されているメソッド)
  }
});    
```

JWT(Json Web Token)は、OAuth2などで用意したプライベートキーを持って認証元とやりとりを行う役割。
詳しくは、こちらを参照
・ [JWT(JSON Web Token)を使った認証を試みる] (https://blog.kazu69.net/2016/07/30/authenticate_with_json_web_token/ )
・ [Using OAuth 2.0 for Server to Server Applications] (https://developers.google.com/identity/protocols/OAuth2ServiceAccount)

そして、メソッドは [APIs Explorer](https://developers.google.com/apis-explorer) で指定している値をJSONで渡せます。

``` Sample_Method.js
function listEvents(auth) {
  console.log("listing Events");
  var calendar = google.calendar('v3');

  calendar.events.list({
    auth: auth,
    calendarId: {カレンダーのID},
    timeMin: (new Date()).toISOString(),
    maxResults: 10,
    singleEvents: true,
    orderBy: 'startTime'
  }, function(err, response) {
    if (err) {
      console.log(err);
      return;
    } else {
      var events = response.items;
      if (events.length == 0) {
        console.log('No upcoming events found.');
      } else {
        async.each(events, (i, next) => {
          var event = i;
          var startTime = event.start.dateTime.slice(11,-6);
          var startMsec = new Date(event.start.dateTime).getTime();
          var endTime = event.end.dateTime.slice(11,-6);
          var endMsec = new Date(event.end.dateTime).getTime();
          var during = ((endMsec - startMsec)/60000) + " MIN";
          var start = event.start.dateTime || event.start.date;
          var eventId = event.id;
          console.log('%s (%s) - %s (%s)', startTime, during, event.summary, eventId);
        }, (err) => {
           if(err) throw err;
        });
      }
    }
  });
}
```

### 2-3. Google Calendar

・古いUI

<img width="264" alt="スクリーンショット 2017-11-06 18.55.47.png" src="https://qiita-image-store.s3.amazonaws.com/0/192249/742aa19a-8f2e-d527-ff5d-a60305a5cd2a.png">

・新しいUI
<img width="234" alt="スクリーンショット 2017-11-06 18.56.05.png" src="https://qiita-image-store.s3.amazonaws.com/0/192249/e59a0594-3b4f-256c-6210-d704bda8d906.png">

カレンダーIDは、カレンダーの設定で確認してください。

権限は、calendar.events.listなどは閲覧までで、insertなどを行うためにはwriter以上の権限が必要です。
<img width="667" alt="スクリーンショット 2017-11-06 18.57.42.png" src="https://qiita-image-store.s3.amazonaws.com/0/192249/d7f6351c-a78e-6f08-5156-49e03f200534.png">

SCOPEもreadonlyを外す必要があります。何度か変更し忘れたので注意。

# 3. 感想
わざわざチャットでカレンダーを使うなんて、なかなかないなーと思いつつ、Google APIsの勉強になったとは思いました。以上。