予定とタスクは、気づけば頭の中だけでは持ちきれない量になっています。会議や外回りで席を離れることが多い人ほど、共有された連絡や予定を見落としやすい。「あれ、あの予定っていつだっけ」と確認しに戻る数分が、地味に積み重なる。大事な用事をうっかり忘れて、あとでヒヤッとする。働いていれば、たぶん誰にでも覚えがあります。
とくに地味にこたえるのが、前の晩です。明日の段取りが前の夜に分かっていれば、朝はずいぶんラクになる。でも予定表は職場のパソコンや手帳の中にあって、家ではパッと確認できない。だから夜のうちに、安心しきれない。——正直に言うと、これはぜんぶ、わたし自身のことでした。「秘書がひとりいてくれたら」と、ずっと思っていました。
そこで頼ったのが、AIです。このブログでこれまで一つずつ作ってきた小さな自動化——フォームの通知、LINEへの通知、毎朝のレポート——が、実は「秘書の部品」でした。それを一つにつなげて、ひと工夫だけ足したら、LINEに話しかけるだけで予定もタスクも整う「自分専用のAI秘書」になりました。名前は「ぽけっと手帳」。アイコンは、白くて笑った可愛いロボットです。しかも、コードは一行も書いていません。ぜんぶAIにお願いして、わたしは貼って、許可しただけ。
できあがったもの
完成したのは、こういう秘書です。
- LINEに話しかけると、予定もタスクも勝手に登録してくれる
- 毎晩、明日の予定を向こうから送ってきてくれる
- いつものスプレッドシートが「今日ダッシュボード」に進化した
- 登録した予定は、Googleカレンダーにも自動で入っている
予定とタスクの本体は、ずっとスプレッドシートです。シートが常にマスターで、AIはそこへ書き込む係。だから「AIが暴走して訳が分からなくなる」ことがありません。元はいつものシートのままです。

わたしの一日で見せます
朝。 スマホを見ると、LINEにもう今日の予定が届いています。早番や朝の早い会議の日は、いちばん上に⚠️マークつきで強調されている。だから朝礼に出られなくても、今日が「いつもと違う日」かどうかが一目で分かります。
前の晩。 毎晩、明日の予定が向こうから届きます。ここで「明日は早い日だ」と分かるのが、わたしにとっては最大のポイントでした。前夜に分かれば、保育園送りの段取りに間に合う。この一通のために作ったと言ってもいいくらいです。
思いついた時。「来週水 ○○面談 15時」「金曜までに報告書」——こんなふうに、思いついた言葉のままLINEに送るだけ。あとはAIが、それが予定なのかタスクなのか問い合わせなのかを判断して、ちゃんと該当するシートに登録し、その場で「登録しました」と復唱して返してくれます。同じ予定が既にあれば、登録前に「はい/いいえ」で確認してくれる。間違えても対話で消せるし、変えられる。だから雑に放り込めます。

パソコンで作業中。 ボタンを押すのも面倒な時は、シートに直接、内容と締切を打ってチェックを1つ入れるだけでタスクが追加できます。LINEを開かずに、作業の手を止めずに足せる。リッチメニューのボタンを押せば、今日の予定・今週の予定・残タスクが吹き出しカードでワンタップです。

週末。 週に一度、今週の予定がまとめて届きます。日曜の夜に「今週はこういう一週間だな」と俯瞰できる。これで、月の予定表をいちいち見にいく習慣そのものが消えました。
これは、新しい魔法ではありません(これまでの“組み合わせ”です)
正直に打ち明けると、何かすごい新技術を覚えたわけではないんです。このブログで一つずつ書いてきた部品を、組み合わせただけ。並べてみると、こうなります。
- 03「毎朝レポート」でやった「決まった時間に、自動でLINEへ送る」——これがそのまま、毎朝の「今日の予定」、毎晩の「明日の予定」、週1の「今週まとめ」になりました。
- 02「LINE通知」で作った“LINEに送る”土台を、もう一歩進めて、LINEの言葉を受け取る双方向にしました。フォームの回答を読む代わりに、LINEに送った自然文をAI(Gemini)に読ませて、予定かタスクかに仕分けています。
- 01「フォーム回答をメールで通知」は、捨てずに“保険”へ回しました。LINEへの通知が失敗した時、自動でメールに切り替わって届きます。
- 04「差し込みメール」でやった「名簿を1件ずつ処理して、済んだら✅で記録する」考え方は、タスクを1件ずつ管理して状態を残す仕組みに生きています。
新しく足したのは、ほんのひと工夫だけ——LINEの言葉をAIに読ませること、予定をGoogleカレンダーへ同期すること、シートを“今日ダッシュボード”に整えること。土台はぜんぶ、これまでの記事の中にありました。だから、01から順番にやってきた人なら、ここまで地続きでたどり着けます。
どうやって作ったか(おおまかに)
仕組みの土台は、拍子抜けするほど素朴です。紙の月の予定表をスマホで撮影して、AIに読ませて表のデータに変換し、シートに貼る。それだけ。あとは、これまでの記事で覚えた通知や受け取りの部品を、上に重ねていきました。最後の仕上げに、シートを今日ダッシュボードにリッチ化し、予定をGoogleカレンダーへ約15分ごとに一方向で同期するようにしています。
言葉の解析も、写真の読み取りも、ぜんぶAI(Gemini)の担当です。そして大事な告白を一つ。コードはぜんぶAIが書きました。わたしは、貼って、許可しただけです。「自分で開発した」なんて、とても言えません。詳しい組み合わせ方は、次回からのシリーズで一つずつ分解していきます。
お金とAIの、正直な話
気になるお金の話を正直に書きます。AIエンジンはGeminiの無料枠で統一していて、わたしの使い方では総額0円です。たくさんの人が一斉に使う前提だと話は変わるはずなので、あくまで「個人がひとりで使う、わたしの使い方では」という範囲の話として読んでください。
無料枠なので、混んでいる時間帯はAIの返事が一瞬詰まることがあります。それでも、よく使うボタン操作はAIを呼ばずローカルだけで処理する作りにしてあるので、肝心なところは止まりません。
そしてもう一つ正直に言うと、これで稼いでいるわけではありません。収益はまだゼロです。いまは、作って、こうして発信して、知ってもらう段階。それでも、自分の一日がラクになった実感は本物です。
つまずきと、その工夫
作ってみて一番こだわったのは、「壊れても気づける」ことでした。便利な道具が黙って壊れているのが、いちばん怖い。だから「沈黙の故障を作らない」を設計の柱にしました。
- 写真の中に読めない行があったら、行番号つきで「ここが読めません」と警告する
- LINEへの通知が失敗したら、自動でメールに切り替えて届ける
- 同じ予定は登録前に確認する
- 間違えても「取消」で元に戻せる
- エラーは英語の暗号ではなく、日本語で知らせる
完璧ではありません。読み取りをミスることもあります。でも、壊れた時に黙り込まず、ちゃんと声を上げてくれる。安心して任せられる線は、たぶんここでした。賢いより、裏切らない。秘書に求めるのは、結局それなんだと思います。
まとめと、次回予告
朝礼に出られなくても、前の夜に翌日の段取りが決まる。思いついた用事はLINEに一言投げておけば、忘れない。それだけで、毎日の気の重さがずいぶん軽くなりました。
創刊号 → 01(フォーム通知)→ 02(LINE通知)→ 03(毎朝レポート)→ 04(差し込みメール)と積み上げてきた部品が、この05でようやく一つにつながって、「自分専用のAI秘書」になりました。これは、ゼロから作った特別なものではなく、これまでの応用編です。
次回からは、この「ぽけっと手帳」を、どの記事のどの部品を組み合わせたのか、作り方シリーズとして一つずつ分解していきます。写真をどう読ませるか、LINEとどうつなぐか、許可画面で気後れしないために——コードが書けなくても、AIに任せて同じものにたどり着けるところまで、ご一緒できればうれしいです。
🧩 設計図:ぜんぶの“組み立て方”(上級者向け)
ここまでの「部品」が、1つのボットの中でどう組み合わさっているか——全体の組み立て図です。コードを書かなくても、この“設計図”をAIに渡せば、AIが組み立ててくれます。
doPost が「写真」か「文字」かで仕分け
→ AIが読取 → 📊 シート
→ 予定に変換 → 📊 シート
→ 📱 LINEに自動通知
→ 📅 Googleカレンダー
① AIに渡す“設計図”(統合プロンプト)
コードを書かない人は、これをそのままAI(ChatGPTやGemini)に渡すだけ。AIが下のようなコードを書いてくれます。
Google Apps Scriptで、自分専用のLINEボット「ぽけっと手帳」を作ってください。
①LINEに送った予定表の写真をAI(Gemini)で読み取り、日付・予定・時刻をスプレッドシートに自動追記(読めない行は推測せず見送り、登録後に一覧で確認・修正・削除できる)
②自然文(例「明日15時に歯医者」)を送ると予定として解釈して追加
③毎朝に「今日の予定」、前夜に「明日の予定」を自動でLINE通知
④シートの予定をGoogleカレンダーへ一方向ミラー(15分ごとに同期)
⑤送った写真の原本をGoogleドライブとGoogleフォトに保存
※APIキーやトークンはスクリプトプロパティにまとめ、コードに直書きしない。AIが一時的に混雑(503)したら自動でリトライする設計に。
② 全体のコード(書き下ろし・約140行・コピペOK)
「中身も見たい」という人向け。実物のボットそのものではなく、同じ仕組みをゼロから書き起こした学習用の全体版です。トークンやキーはコードに書かず、スクリプトプロパティ(設定欄)から読む安全な作りにしてあります。
▶ 全体のコードを開く(約140行)
/**************************************************************************
* ぽけっと手帳(学習用クリーン版・全体リファレンス)
* 自分専用の LINE 秘書ボット。1つのスクリプトに「部品」を組み合わせています。
*
* できること
* ① 予定表の写真を送る → AIが読み取り → スプレッドシートに追記(+確認返信)
* ② 自然文(例「明日15時に歯医者」)を送る → 予定として追記
* ③ 毎朝「今日の予定」/ 前夜「明日の予定」を自動でLINE通知(時間トリガー)
* ④ シートの予定を Google カレンダーへミラー(時間トリガーで同期)
*
* ★秘密情報(LINEトークン・Geminiキー)はコードに直接書きません。
* エディタ左の[⚙ プロジェクトの設定]→「スクリプト プロパティ」に
* LINE_TOKEN と GEMINI_KEY を登録してください。
**************************************************************************/
// ===== 設定:秘密はスクリプトプロパティから読む(コードに直書きしない)=====
const PROP = PropertiesService.getScriptProperties();
const LINE_TOKEN = PROP.getProperty('LINE_TOKEN'); // LINE チャネルアクセストークン
const GEMINI_KEY = PROP.getProperty('GEMINI_KEY'); // Gemini API キー
const GEMINI_MODEL = 'gemini-2.5-flash';
const SS = SpreadsheetApp.getActiveSpreadsheet();
const SHEET = SS.getSheetByName('予定') || SS.getActiveSheet(); // 列=[日付, 予定, 時刻]
const MY_CAL = CalendarApp.getDefaultCalendar();
/* ============ ① LINE からの入口(Webアプリ:doPost)============ */
function doPost(e) {
const event = JSON.parse(e.postData.contents).events[0];
if (event && event.type === 'message') {
const m = event.message;
if (m.type === 'image') handleImage(m.id, event.replyToken); // 写真
else if (m.type === 'text') handleText(m.text, event.replyToken); // 文字
}
return ContentService.createTextOutput('OK'); // LINEへ即200を返す
}
/* ============ ② 写真 → AIで読み取り → シートに追記 ============ */
function handleImage(messageId, replyToken) {
const blob = UrlFetchApp.fetch(
'https://api-data.line.me/v2/bot/message/' + messageId + '/content',
{ headers: { Authorization: 'Bearer ' + LINE_TOKEN } }
).getBlob();
const rows = readScheduleFromImage(blob);
if (rows.length === 0) {
reply(replyToken, '写真をうまく読み取れませんでした。少し時間をおいて、もう一度お試しください。');
return;
}
rows.forEach(function (r) { SHEET.appendRow([r.date, r.title, r.time]); });
reply(replyToken, '📸 写真から予定を' + rows.length + '件取り込みました。\n'
+ rows.map(function (r) { return r.date + ' ' + r.title; }).join('\n'));
}
// 画像をGeminiに渡し、日付・予定・時刻の配列で受け取る
function readScheduleFromImage(blob) {
const prompt = 'この画像は予定表です。日付・予定・時刻を読み取り、'
+ 'JSON配列 [{"date":"YYYY-MM-DD","title":"予定名","time":"HH:mm"}] だけで返してください。'
+ '読めない項目は空文字に。説明文は不要です。';
const text = callGemini(JSON.stringify({ contents: [{ parts: [
{ text: prompt },
{ inline_data: { mime_type: blob.getContentType(), data: Utilities.base64Encode(blob.getBytes()) } }
] }] }));
return text ? JSON.parse(text.replace(/```json|```/g, '').trim()) : [];
}
/* ============ ③ 文字メッセージ → 予定追加 / 一覧 ============ */
function handleText(text, replyToken) {
if (text === '今日の予定') { reply(replyToken, listText(0)); return; }
if (text === '明日の予定') { reply(replyToken, listText(1)); return; }
// 自然文(例「明日15時に歯医者」)を1件の予定に変換
const prompt = '次の文を予定に変換し、JSON {"date":"YYYY-MM-DD","title":"...","time":"HH:mm"} '
+ 'だけで返してください。今日は ' + ymd(new Date()) + ' です。文:' + text;
const ans = callGemini(JSON.stringify({ contents: [{ parts: [{ text: prompt }] }] }));
if (!ans) { reply(replyToken, 'AIが混み合っています。少し後でお試しください。'); return; }
const r = JSON.parse(ans.replace(/```json|```/g, '').trim());
SHEET.appendRow([r.date, r.title, r.time]);
reply(replyToken, '✅ 追加しました:' + r.date + ' ' + r.title + ' ' + r.time);
}
/* ============ ④ 毎朝/前夜の自動通知(時間トリガーで実行)============ */
function notifyToday() { push(listText(0)); } // 毎朝に
function notifyTomorrow() { push(listText(1)); } // 前夜に
// 指定日(0=今日,1=明日)の予定をテキスト化
function listText(offset) {
const target = ymd(new Date(Date.now() + offset * 86400000));
const items = SHEET.getDataRange().getValues().filter(function (row) {
return row[0] && ymd(new Date(row[0])) === target;
});
if (items.length === 0) return (offset ? '明日' : '今日') + 'の予定はありません。';
return (offset ? '明日' : '今日') + 'の予定\n'
+ items.map(function (row) { return '・' + (row[2] || '') + ' ' + row[1]; }).join('\n');
}
/* ============ ⑤ シート → Google カレンダーへミラー(時間トリガー)======= */
function syncToCalendar() {
SHEET.getDataRange().getValues().forEach(function (row) {
const date = row[0], title = row[1], time = row[2];
if (!date || !title) return;
const start = new Date(date);
const hm = String(time || '09:00').split(':');
start.setHours(Number(hm[0]) || 9, Number(hm[1]) || 0, 0, 0);
// 同じ日に同じ予定が無ければ作成(重複防止の簡易版)
const dup = MY_CAL.getEventsForDay(start).some(function (ev) { return ev.getTitle() === title; });
if (!dup) MY_CAL.createEvent(title, start, new Date(start.getTime() + 3600000));
});
}
/* ============ 共通:LINE返信/通知・日付ユーティリティ ============ */
function reply(replyToken, text) {
lineApi('reply', { replyToken: replyToken, messages: [{ type: 'text', text: text }] });
}
function push(text) { // 自分(友だち)へ配信
lineApi('broadcast', { messages: [{ type: 'text', text: text }] });
}
function lineApi(path, body) {
UrlFetchApp.fetch('https://api.line.me/v2/bot/message/' + path, {
method: 'post', contentType: 'application/json',
headers: { Authorization: 'Bearer ' + LINE_TOKEN },
payload: JSON.stringify(body)
});
}
// Geminiを呼ぶ。一時的な混雑(HTTP 503)は最大3回まで自動リトライ
function callGemini(payload) {
for (let i = 0; i < 3; i++) {
const res = UrlFetchApp.fetch(
'https://generativelanguage.googleapis.com/v1beta/models/' + GEMINI_MODEL + ':generateContent',
{ method: 'post', contentType: 'application/json',
headers: { 'x-goog-api-key': GEMINI_KEY }, muteHttpExceptions: true, payload: payload });
if (res.getResponseCode() < 500) {
return JSON.parse(res.getContentText()).candidates[0].content.parts[0].text;
}
Utilities.sleep(1000 * (i + 1)); // 少し待って再挑戦
}
return '';
}
function ymd(d) { return Utilities.formatDate(d, Session.getScriptTimeZone(), 'yyyy-MM-dd'); }
③ 「どの部分が、どの機能」対応表
| コードの部分 | 機能 | シリーズ |
|---|---|---|
| doPost | LINEの入口(写真/文字で仕分け) | 第2回 |
| handleImage / readScheduleFromImage | 写真→表(OCR) | 第1回=記事06 |
| handleText | 自然文を予定に変換/一覧 | 第2回 |
| notifyToday / listText | 毎朝・前夜の自動通知 | 第4回 |
| syncToCalendar | カレンダーへミラー | 第5回 |
| reply / push / callGemini | 共通部品(通知・AI・503リトライ) | 全回共通 |
このシリーズのこれまで:創刊号(入口)/記事01:フォーム回答をメールで受け取る/記事02:フォーム回答をLINEに即時通知/記事03:フォーム回答を毎朝自動集計してLINEに通知/記事04:名簿から差し込みメールを一斉送信/記事05:長いGmailをAIで3行+やることリストに/記事06:毎朝、未読メールを自動で要約して1通に/記事07:たまった自由記述を、AIでテーマ別に分類・集計/記事08:登録キーワードのニュースを、毎朝LINEに/応用編その1:AI秘書「ぽけっと手帳」(全体像・この記事)/応用編その2:リサーチ秘書(追いたい分野のニュースが毎朝1通)/育てる編①:ためた見出しに"質問"できる(台帳Q&A)/育てる編②:「医療も追って」で集める分野を増減(設定変更)/育てる編③:「今週どうだった?」でためた台帳を週次まとめ/育てる編④:「地図にして」でためた見出しを放射状の地図に(見出しマップ)/育てる編⑤:「再エネ政策を追いたい」で検索語まで含む設定をAIが設計(設定アシスタント・完結)/応用編その3:受発注・請求 秘書(受注が来たら、選ぶだけで見積書・請求書のPDFに)/受発注 育てる編①:入金消込・督促・領収書PDF/受発注 育てる編②:受注した瞬間に在庫を押さえる/受発注 育てる編③:発注点アラートと発注書PDF/受発注 育てる編④:月次集計・売上グラフ・会計CSV(最終回)/基礎編:URLひとつで“自分の申込ページ”を作る(Webアプリ)/基礎編:申し込まれた瞬間に、確認メールが自動で返る/基礎編:申し込んだら、控えのPDFが自動で届く/基礎編:申込の控えが、勝手にドライブへ積み上がっていく/基礎編:申込のひとことを、届いた瞬間にAIが仕分ける/基礎編:誰も画面を開いていないのに、毎朝まとめが届く/基礎編:申込が来た瞬間、ポケットのLINEが鳴る(最終回)/応用編その4:申込・予約 秘書(満席の瞬間、ページが自分から閉まる)/申込・予約 育てる編①:キャンセル待ちと自動繰り上げ/申込・予約 育てる編②:承認制とお断り下書き/申込・予約 育てる編③:前日リマインダーと毎朝まとめ/申込・予約 育てる編④:管理ページ(最終回)
#AI秘書 #ぽけっと手帳



コメント