「予約した後、Googleカレンダーにも入れられると便利」というフィードバックをオーナーからもらった。確かにそうだ。LINEで通知が来ても、それだけではカレンダーに予定が入らない。
実装を始めた。CalendarAppというGASのクラスを見つけた。数行でGoogleカレンダーにイベントを作れる。これだと思って書いた。
テストした。動いた。どこに入ったか確認した。
私のGoogleカレンダーに入っていた。
CalendarAppは誰のカレンダーを操作するか
CalendarAppが操作するのは「GASを実行するGoogleアカウント」のカレンダーだ。
予約システムのGASはシンプルシステム株式会社の開発者アカウントでデプロイされている。CalendarApp.getDefaultCalendar()が返すのは、私のカレンダーだ。オーナーのカレンダーでも、お客さんのカレンダーでもない。
考えてみれば当然だ。GASは別のGoogleアカウントのカレンダーを操作する権限を持っていない。権限を得るためにはOAuth認証フローを通す必要がある。それは数十倍複雑な実装だ。
CalendarAppを使う方針は間違っていた。最初から捨てることにした。
「URLで開く」という発想
Googleカレンダーには、URLパラメータで新規予定の作成画面を開く仕組みがある。
https://calendar.google.com/calendar/render?action=TEMPLATE&text=タイトル&dates=開始/終了
このURLをブラウザで開くと、Googleカレンダーの新規予定画面が、タイトルと日時が入力済みの状態で開く。ユーザーが「保存」ボタンを押すことで、「そのユーザー自身のカレンダー」に予定が入る。
これが正しいアプローチだ。開発者はカレンダーを操作しない。URLを渡すだけだ。保存するかどうかはユーザーが決める。
URLを生成するコード
日時のフォーマットはISO 8601のコンパクト形式(yyyyMMddTHHmmss)だ。Googleカレンダーはこの形式を要求する。
function buildGCalUrl(serviceId, serviceName, slotDate, slotTime) {
const dur = { '1':60, '2':300, '3':90, '4':120, '5':60, '6':60 };
const [y, mo, d] = slotDate.split('/');
const [hh, mm] = slotTime.split(':');
const start = new Date(+y, +mo-1, +d, +hh, +mm);
const end = new Date(start.getTime() + (dur[serviceId] || 60) * 60000);
const fmt = dt =>
dt.getFullYear().toString() +
String(dt.getMonth()+1).padStart(2,'0') +
String(dt.getDate()).padStart(2,'0') + 'T' +
String(dt.getHours()).padStart(2,'0') +
String(dt.getMinutes()).padStart(2,'0') + '00';
return 'https://calendar.google.com/calendar/render?' + new URLSearchParams({
action: 'TEMPLATE',
text: '予約システム ' + serviceName,
dates: fmt(start) + '/' + fmt(end),
});
}
5時間のサービス(300分)でも、durationsオブジェクトから所要時間を引いてミリ秒計算するだけで終了時刻が求まる。
使い道は2つ
このURLを2か所に置いた。
予約完了画面に「Googleカレンダーに追加」ボタンとして置く。お客さんが予約直後にタップして、自分のカレンダーに入れる。
オーナーへのLINE通知にURLを含める。オーナーが通知を見てタップして、自分のカレンダーに入れる。
どちらも「渡す側はURLを生成するだけ、操作するのは受け取った側」という設計だ。
CalendarAppとURLリンクの使い分け
CalendarAppはGASで「自分のカレンダーを自動操作する」用途に使う。たとえば「毎週月曜に定例ミーティングを自動登録する」ような、開発者自身のカレンダーを機械的に管理するスクリプトには向いている。
他人のカレンダーに予定を入れるよう促したい場合は、URLリンク方式が正しい。カレンダーのAPIではなく、Googleカレンダーの「ウェブUIのURL」を使う。
用途を混同すると私のように「動いたが、誰のカレンダーに入っているかが違う」という結果になる。
*次回は「カレンダーURLが長すぎる問題——EC2リダイレクト方式で解決した」*
*シンプルシステム株式会社 代表 伊藤勝彦*