
「今日いつ空いてる?」をSlackで一発!予定ではなく“すき間”を返すcalendar機能
こんにちは!Accel-Hack (アクセルハック)開発チームです。
社内AIアシスタント「Airi」の開発プロセスをお届けする開発記ブログ。
第20回目の今回は、前回整えた Google 連携の土台の上に建てる、最初の実用機能――カレンダー機能(calendar機能)をお届けします。
前回の Vol.19 では、サービスアカウントで Google API を叩く通行証(アクセストークン)を手に入れました。
いよいよこれを活かすときです。
今回作るのは /airi calendar open というコマンド。
「今日いつ空いてる?」という日常の問いに、Slack から一発で答えます。
ポイントは、予定の一覧ではなく“空き時間”を返すという発想の転換です。
1. 欲しいのは「予定一覧」ではなく「空き時間」
打ち合わせを入れたいとき、本当に知りたいのは「どんな予定が入っているか」ではなく、「いつなら空いているか」です。
予定一覧を眺めて、頭の中で“すき間”を逆算する――あの地味な手間を、Airi に肩代わりさせたい。
そこで作ったのが /airi calendar open です。
叩けば、その人の今日の空き時間だけがパッと返ってくる。
カレンダーアプリを開く必要すらありません。
小さな機能ですが、「探す」を「尋ねる」に変える、気持ちのいい一歩です。
2. 実行者が誰かは、Slackが知っている
まず必要なのは「誰のカレンダーを見るか」です。
これは Slack が教えてくれます。
コマンドを叩いた人のプロフィールから、メールアドレスを取得します。
const userInfo = await context.client.users.info({ user: payload.user_id });
const email = userInfo.user?.profile?.email;
if (!email) {
await context.respond({
text: "Slackプロフィールにメールアドレスが設定されていません。",
response_type: "ephemeral",
});
return;
}Slack のメールアドレスと Google カレンダーのアカウントが揃っていれば、これだけで「あなたの」カレンダーを対象にできます。
メールが未設定なら、無理に進めず丁寧に案内して終了。
入り口でのこうした気配りが、地味に効きます。
3. 「今日の枠」を決める
次に、空きを探す範囲を決めます。
Airi では業務時間を 10:00〜19:00 JST とし、その枠の中だけを見ます。
const businessEnd = new Date(now);
businessEnd.setUTCHours(19 - 9, 0, 0, 0); // 19:00 JST
if (now >= businessEnd) {
await context.respond({
text: "本日の業務時間は終了しています。ゆっくり休んでくださいね!",
response_type: "ephemeral",
});
return;
}すでに業務時間を過ぎていたら、空きを計算せず「ゆっくり休んでくださいね」と返します。
また、検索の起点は「業務開始前なら 10:00 から、途中ならいまから」。
過ぎた時間の空きを出しても仕方ないので、常に“これから”だけを見るようにしています。
4. FreeBusy で予定を取り、busy を反転して open を出す
ここが機能の心臓部です。
Google には FreeBusy API という、「指定した期間に予定が入っている時間帯(busy)」だけを返してくれる便利なAPIがあります。
予定の件名や参加者といった中身は返さず、「埋まっている/空いている」だけを教えてくれるのです。
これを Vol.19 のサービスアカウントで認証して呼び出します。
得られるのは「埋まっている時間(busy)」の一覧。
でも私たちが欲しいのはその逆、「空いている時間(open)」です。
そこで、busy を反転させる小さなアルゴリズムを書きました。
let currentStart = businessStart;
for (const busy of sortedBusy) {
if (busy.start > currentStart) {
openSlots.push({ start: currentStart, end: busy.start }); // すき間 = 空き
}
if (busy.end > currentStart) currentStart = busy.end;
}
if (currentStart < businessEnd) {
openSlots.push({ start: currentStart, end: businessEnd });
}busy を時系列に並べ、先頭から「予定と予定のすき間」を順に拾っていくだけ。
やっていることは単純ですが、これで「予定表」が「空き時間表」に早変わりします。
この反転のイメージを図にすると、次のとおりです。

そして嬉しいのは、この方式がプライバシーに優しいことです。
Airi は予定の中身を一切見ません。
「埋まっているか・空いているか」だけを扱うので、他人の予定の詳細が漏れる心配がないのです。
5. 本人にだけ、そっと返す
最後に、計算した空き時間を Slack に返します。
このとき使うのが ephemeral メッセージ――実行した本人にだけ見える投稿です。
自分の空き時間が、チャンネル全員に流れてしまっては気まずいですよね。
ephemeral なら、コマンドを叩いた人だけに、Block Kit で整形された空き時間リストがそっと表示されます。
チャンネルを汚さず、必要な人に必要な情報だけを届ける。
Slack ならではの気の利いた返し方です。
6. おわりに
今回は、/airi calendar open を題材に、「予定ではなく“すき間”を返す」カレンダー機能をお届けしました。
FreeBusy API で埋まっている時間を取り、それを反転して空きを出し、本人にだけそっと返す。
小さな機能ですが、「カレンダーを開いて逆算する」というありふれた手間を、見事に一発で消してくれます。
次回は、同じ Google 連携でも、今度は向こうから飛んでくる情報を扱います。
「Vol.21: 大事なメールだけ、Slackに届く。Gmail Push × ルール振り分けの作り方」 として、Gmail の Push 通知(Webhook)を受け取り、特定のメールを Slack へ自動で振り分ける仕組みをお届けします。
お楽しみに!
この記事は以下の体制で作成されました。
ドラフト作成・執筆: Airi (社内AIアシスタント)
アイデア出し・レビュー: CTO
デザイン: 事務スタッフ
Accel-Hackでは、テクノロジーを駆使して世の中の課題を解決していく仲間を募集しています。
私たちの取り組みに少しでも興味を持っていただけたら、ぜひ採用ページや会社サイトも覗いてみてくださいね。