Action on Google 用リマインダーアプリケーション

#概要
Action on Google で日時・件名・所要時間を話しかけるとgoogleカレンダーに登録してくれるアプリケーションを作成しました。

#実行の様子
アプリを呼び出すと、何を言って欲しいか誘導してくれます。
会話は友達とするようにフランクな感じを目指しました。
誘導通りに日付・時間・予定の名前・かかる時間を言うとgoogleカレンダーにその通りに記入を行ってくれます。
<img width="276" alt="aaa.JPG" src="https://qiita-image-store.s3.amazonaws.com/0/254656/d19f222d-d77f-45ea-7650-7db1551f4ae3.jpeg">
<img width="277" alt="iii.JPG" src="https://qiita-image-store.s3.amazonaws.com/0/254656/bf5355f1-7ca1-1cca-ade6-d4b49d4cc988.jpeg">

<img width="334" alt="a.JPG" src="https://qiita-image-store.s3.amazonaws.com/0/254656/9126a5cb-c465-3b89-dce6-22a22ae96f7d.jpeg">


もしユーザーが予想外の発言をした場合は、言い直すように誘導します。例えば日付を言って欲しいのに全く別の発言がなされた場合は、画像のように分からなかった事をユーザーに伝えて言い直してもらいます。
<img width="273" alt="uuu.JPG" src="https://qiita-image-store.s3.amazonaws.com/0/254656/d974c039-9a12-2533-fc73-46a8d5df5ccf.jpeg">


#DialogFlowのソースコード
googleカレンダーへの認証と登録、その他ユーザーへの返信などの処理は以下のコードで行っています。

ユーザーからカレンダー予約名、開始時刻、経過時間を取得しているので、開始時間から経過時間を足したものを終了時間としてgoogleカレンダーに登録しています。

~~~javascript
'use strict';
require('date-utils');
const functions = require('firebase-functions');
const { dialogflow } = require('actions-on-google');

const app = dialogflow({debug: true});

const HEARING_INTENT = 'Hearing Intent';
const ARGUMENT_DATE      = 'date';
//const ARGUMENT_STARTTIME = 'start-time';
const ARGUMENT_USETIME   = 'use-time';
const ARGUMENT_STARTTIME= 'start-time';
const ARGUMENT_STARTTIME_PARM= {
  AMOUNT: 'amount',
  UNIT: 'unit',
}
const ARGUMENT_USETIME_PARM = { //{"usage-time":{"amount":2,"unit":"時"}}
  AMOUNT: 'amount',
  UNIT: 'unit',
};
const ARGUMENT_EVENTNAME='event-title';
const calendar = 'tktkmy@gmail.com';
let eventTitle; //カレンダー予約タイトル
let eventStartTime; //カレンダー予約開始時間
let eventEndTime; //カレンダー予約終了時間

const {google} = require('googleapis');
const SCOPES = ['https://www.googleapis.com/auth/calendar'];
//credentials.jsonの文字列データ
const CRIENT_SECRET = ['ここにはcredentials.jsonのデータを記入しています'];
//secret.jsonの文字列データ
const CREDENTIAL ='ここにはsecret.jsonの文字列データを記入しています'; 


//認証
function authorize(credentials, callback) {
  const {client_secret, client_id, redirect_uris} = credentials.installed;
  let token = {};
  const oAuth2Client = new google.auth.OAuth2(
      client_id, client_secret, redirect_uris[0]);

  try {
    token = CREDENTIAL;
  } catch (err) {
    console.log('oAuth error occurred');
    return;
  }
  oAuth2Client.setCredentials(JSON.parse(token));
  callback(oAuth2Client);
}

//カレンダーイベント登録

function CreateEvents(auth) {
  const calendar = google.calendar({version: 'v3', auth});
  var event = {
      'summary': eventTitle,
      'location': '',
      'description': '音声アシスタントからの予約投稿です',
      'start': {
        'dateTime': eventStartTime,
        'timeZone': 'Asia/Tokyo',
      },
      'end': {
        'dateTime': eventEndTime,
        'timeZone': 'Asia/Tokyo',
      },
      'attendees': [
        {'email': 'tktkmy@gmail.com'},
      ],
      'reminders': {
        'useDefault': false,
        'overrides': [
          {'method': 'popup', 'minutes': 10},
        ],
      },
    };
     console.log(event);
  calendar.events.insert({
    auth: auth,
    calendarId: 'primary',
    resource: event,
  }, function(err, event) {
    if (err) {
      console.log('There was an error contacting the Calendar service: ' + err);
      return;
    }
    console.log('Event created: %s', event.htmlLink);
  });
}


app.intent('Hearing Intent', (conv, params)  => {
  console.log('Hearing Intent');

  //予約日
  let dDate = new Date(params[ARGUMENT_DATE]);
  let reserveDate = dDate.toFormat('YYYY年MM月DD日');
  let startTimeAmount = params[ARGUMENT_STARTTIME][ARGUMENT_STARTTIME_PARM.AMOUNT];
  let startTime = new Date(dDate.setHours(startTimeAmount));
  eventStartTime = new Date(dDate.setHours(startTime.getHours()));
  eventStartTime.setTime(eventStartTime.getTime() - 1000*60*60*9);
  dDate.setHours(eventStartTime.getHours()+9);
  let reserveBeginTime = dDate.toFormat('HH24時MI分');

  //利用時間
  let useTimeAmount = params[ARGUMENT_USETIME][ARGUMENT_USETIME_PARM.AMOUNT];
  let useTimeUnit   = params[ARGUMENT_USETIME][ARGUMENT_USETIME_PARM.UNIT];
  let useTime = useTimeAmount + useTimeUnit;

  //予約終了時間
  let endTime = dDate;

  switch (useTimeUnit) {//@sys.durationのunitに応じて処理をわける。日は無視して時と分のみだけの対応にしておく
    case '時':
      endTime.setHours(dDate.getHours() + Number(useTimeAmount));
      break;
    case '分':
      endTime.setMinutes(dDate.getMinutes() + Number(useTimeAmount));
      break;
    default:  
      endTime.setHours(dDate.getHours() + Number(useTimeAmount));
      break;
  }

  let reserveEndTime = endTime.toFormat('HH24時MI分');
  eventEndTime = new Date(endTime.setHours(endTime.getHours())); //カレンダー登録用
  eventEndTime.setTime(eventEndTime.getTime() - 1000*60*60*9);
  //予約タイトル
  eventTitle = params[ARGUMENT_EVENTNAME];
   //認証とカレンダーイベント登録
  try {
    const content = CRIENT_SECRET;
    authorize(JSON.parse(CRIENT_SECRET), CreateEvents);
  } catch (err) {
    return console.log('Authorize error occurred');
  }
  conv.close(`${reserveDate} の ${reserveBeginTime} から ${reserveEndTime} までの ${useTime}間、カレンダーに追加したッす！`);    
});


exports.dialogflowFirebaseFulfillment = functions.https.onRequest(app);
~~~