📅

📅「LINEに話すだけ」で予定管理!ChatGPT × GAS × Googleカレンダーで作る秘書ボット

に公開

📖 1. はじめに

この記事では、ChatGPT API と Google Apps Script(以下 GAS)、LINE Messaging API を組み合わせて、「会話だけで予定管理ができる秘書ボット」を作成します。

こんなことができます

  • LINEで「明日の10時に美容院」と送るだけで、Googleカレンダーに予定登録
  • 明日の予定教えて」で予定を確認
  • 明日の予定キャンセルして」で削除

📃 2. 使用技術と準備

使った技術

  • Google Apps Script (GAS)
  • OpenAI ChatGPT API (gpt-4o-mini)
  • Googleカレンダー (CalendarApp)
  • LINE Messaging API (Webhook と reply)

必要なもの

  • OpenAI APIキー
  • LINEのアカウント
  • Googleアカウント

⚖️ 3. 構成図と構成概要

  • LINE が入力受付、返信を担当
  • GAS がロジックコントローラー
  • ChatGPT が自然言語を構造化
  • Googleカレンダー が予定の登録先

⚙️ 4. 実装手順

この秘書ボットは、LINE・ChatGPT・Googleカレンダー・GAS の4つを連携させて構築します。

4-1. LINE Botを作成しWebhook URLを設定

  1. LINE Developers Console にアクセス
  2. プロバイダーを作成(なければ新規作成)
  3. 「Messaging API」を選択してチャネルを作成
  4. チャネル作成後、以下を設定:
    • Webhook送信 → オン
    • 応答メッセージ → オフ
    • Webhook URL → 後ほど作成する GAS の WebアプリURLを貼る
  5. チャネルアクセストークンを控える(GAS内で使う)

4-2. Google Apps Script(GAS)でWebアプリを作成

  1. Google Apps Script にアクセス
  2. 新規プロジェクトを作成し、以下のような構成で記述:
function doPost(e) {
  const json = JSON.parse(e.postData.contents);
  const replyToken = json.events[0].replyToken;
  const userMessage = json.events[0].message.text;

  const replyText = runSchedulerBot(userMessage); // 本体ロジック

  UrlFetchApp.fetch("https://api.line.me/v2/bot/message/reply", {
    method: "post",
    headers: {
      "Content-Type": "application/json",
      "Authorization": "Bearer " + LINE_ACCESS_TOKEN
    },
    payload: JSON.stringify({
      replyToken: replyToken,
      messages: [{ type: "text", text: replyText }]
    })
  });

  return ContentService.createTextOutput("OK");
}
  1. runSchedulerBot() 関数では、ChatGPT API とのやりとり・Googleカレンダー操作などの処理を実装(後述)

  2. 「デプロイ」→「新しいデプロイ」→「Webアプリ」

  • アクセス権は「全員(匿名ユーザー含む)」に設定
  • 発行されたURLを LINE の Webhook に登録

4-3. 現在の日時を取得して基準にする

ここからはrunSchedulerBot関数の解説を行います。
この関数は、LINEやスプレッドシートからの入力メッセージ(userMessage)をもとに、ChatGPTに問い合わせ、予定の登録・確認・削除を行う本体ロジックです。

まず、GPT に「今が何年何月何日か」を伝えるため、現在時刻を取得してプロンプト内に組み込みます。

function runSchedulerBot(userMessage) {
    const now = new Date();
    const nowStr = Utilities.formatDate(now, Session.getScriptTimeZone(), "yyyy/MM/dd HH:mm");

    <--- 省略 --->

}

function formatDate(date) {
  return Utilities.formatDate(date, Session.getScriptTimeZone(), "yyyy/MM/dd HH:mm");
}

これにより、GPTが「明日」「今週末」などの相対表現を正しく解釈できるようになります。

4-4. ChatGPT へのプロンプト設計

次に、GPT に対して期待する出力形式を指示する プロンプト を組み立てます。
以下は簡略化したプロンプトです。

const systemPrompt = `
現在の日時は ${nowStr} です。
これを基準に、「明日」「来週」「◯月◯日」などを正確に解釈してください。

【出力フォーマット】
#操作
登録 / 削除 / 確認 のいずれか

#返答
秘書らしい丁寧な文章

#期間(確認時は必須)
開始: YYYY/MM/DD HH:mm
終了: YYYY/MM/DD HH:mm

#予定(登録・削除時は必須)
タイトル: ○○
開始: YYYY/MM/DD HH:mm
終了: YYYY/MM/DD HH:mm
`;
  • #操作 によって処理の種別を判断
  • #予定タイトル, 開始, 終了 を明示
  • #期間(確認時)も ChatGPT に指示して含めさせます

✅ 登録・削除の操作では #予定 ブロックが 必須 です。プロンプト内に明記。

4-5. Webhook処理

systemPromptuserMessagegpt-4o-mini に送信し、応答を取得します。

const requestOptions = {
  method: "post",
  headers: {
    "Content-Type": "application/json",
    "Authorization": "Bearer " + OPENAI_API_KEY
  },
  payload: JSON.stringify({
    model: "gpt-4o-mini",
    messages: [
      { role: "system", content: systemPrompt },
      { role: "user", content: userMessage }
    ]
  })
};

const gptResponse = UrlFetchApp.fetch("https://api.openai.com/v1/chat/completions", requestOptions);
const json = JSON.parse(gptResponse.getContentText());
const replyText = json.choices[0].message.content.trim();

4.6 ChatGPT の出力を解析して処理を分岐

ChatGPT の出力から、以下のブロックを正規表現で抽出します。

const operationMatch = replyText.match(/#操作\s*([\s\S]*?)#返答/);
const responseMatch = replyText.match(/#返答\s*([\s\S]*?)(#期間|#予定)/);
const periodMatch = replyText.match(/#期間\s*([\s\S]*?)($|#予定)/);
const scheduleMatch = replyText.match(/#予定\s*([\s\S]*)/);

抽出した内容をもとに、操作種別と返答文を決定します。

let operation = "登録";
let userMessageFormatted = "";

if (operationMatch) {
  operation = operationMatch[1].trim();
}

if (responseMatch) {
  userMessageFormatted = responseMatch[1].trim();
}

4.7 操作が「確認」の場合(予定の確認)

操作が「確認」のときは#期間から開始・終了日時を取り出し、その期間に登録されている予定を取得します。

const events = CalendarApp.getDefaultCalendar().getEvents(start, end);
if (events.length > 0) {
  userMessageFormatted += "\n\n📅 登録されている予定:\n";
  userMessageFormatted += events.map(e => {
    const time = formatDate(e.getStartTime());
    return `${time}${e.getTitle()}`;
  }).join("\n");
} else {
  userMessageFormatted += "\n(その期間に予定はありません)";
}

操作が「登録」または「削除」の場合

登録

calendar.createEvent(title, start, end);
return `📅 ${title} の予定を登録しました。`;

🗑️ 削除

const events = calendar.getEvents(start, end);
const targetEvents = events.filter(event => event.getTitle().includes(title));

if (targetEvents.length > 0) {
  targetEvents.forEach(event => event.deleteEvent());
  return `🗑️ ${targetEvents.length} 件の「${title}」の予定を削除しました。`;
} else {
  return `❗「${title}」の予定が見つかりませんでした。`;
}

✅ 5. 動作例

入力例 結果
明日の10時に〇〇入れて 📅 予定がGoogleカレンダーに登録される
明日の予定教えて ✅ 登録済みの予定一覧が表示される
明日の予定キャンセル 🗑️ 指定の予定が削除される

以下は実際に LINE に入力して、Googleカレンダーと連動した動作結果です。


📝 予定を登録する

入力:

明日の10時から1時間、美容院の予定を入れて

結果:

📅 美容院の予定を登録しました。

LINE
Google カレンダー


🔍 予定を確認する

入力:

明日の予定を教えて

結果:

明日の予定を確認いたします。
📅登録されている予定:

  • 2025/04/23 10:00~ 美容院


🗑️ 予定を削除する

入力:

明日の美容院の予定を削除して

結果:

🗑️ 1件の「美容院」の予定を削除しました。


すべての操作が自然言語で完結します。しかもLINEで完結するので、予定管理がぐっと身近になります!

💬 感想・振り返り

今回の開発を通して、自然言語で予定を登録・確認できる仕組みがあると、日常の予定管理がとてもスムーズになると実感しました。

特に 予定の登録と確認は安定して動作できました。LINE に「明日10時に美容院」と話しかけるだけで予定が入るのは、ちょっと未来感があります。

ただし、削除機能についてはまだ不安定な部分も残っており、入力内容によって削除対象が見つからないケースもありました。この点は今後の精度向上の課題です。


🤔 実際に使うかどうか?

正直なところ、「ぱっとカレンダーを見たいとき」はやっぱり Google カレンダーそのものを開いた方が早いこともあります。そのため、LINEボットはあくまで補助的・並列的に使うのが現実的かもしれません。

とはいえ、「手がふさがっていてスマホしか触れない」「会話感覚で予定を入れたい」といったシーンではかなり便利に感じました。


🔮 今後のアイデア・改良したいこと

  • 毎晩21:00に「明日の予定」をLINEでリマインドしてくれる機能を追加したい
  • 📝 やるべきタスクや締切も管理できるように拡張していきたい
  • 📆 カレンダーを見て「空いている時間」に自動で予定やタスクを配置してくれるような、提案型の秘書AI にしていきたい

今回の取り組みは、「未来の予定管理」に一歩近づいた感覚がありました。✨

📝 7. おわりに

この記事を読んで「やってみよう!」と思ってもらえたら嬉しいです。
試してみたら、Zenn や X で感想を聞かせてください!

Discussion