はじめに
一人暮らしの高齢者や小規模コミュニティ向けに、毎朝LINEで安否を確認し、応答がなければ管理者に知らせる仕組みを Google Apps Script(GAS)だけで作りました。サーバーもDBも不要で、データはスプレッドシートに残ります。
機能
| 時刻 / 契機 | 処理 |
|---|---|
| 毎朝 8:00 | 全利用者へボタン付きテンプレートメッセージを一斉送信 |
| ボタン押下 | 「元気です/相談したい/今日は外出」をシートに記録 |
| 「相談したい」押下 | 代表者へ即時通知 |
| 15:00 | 本日未応答の人にだけ再通知 |
| 15:00 | 最終応答から48時間以上経過した人を、管理者3名へ同時に訪問依頼アラート |
画面イメージ
LINE(利用者・代表者・管理者)
① 毎朝8:00にボタン付きメッセージが届き、押すと返信が返ります。② 15:00には未応答の人にだけ再通知します。③ 「相談したい」は代表者へ即時、48時間無応答は管理者3名へ同時に届きます。
スプレッドシート
「利用者」シートは最新状態を、「応答ログ」シートは応答の履歴を保持します。
画面はコードの仕様に沿って作成したイメージです(氏名は架空)。実機の見た目とは多少異なります。
全体構成
[GASトリガー 8:00/15:00] ──push/multicast──▶ LINE ──▶ 利用者
│ ボタン(postback)
[GAS Webアプリ doPost] ◀──Webhook─────────────┘
│
▼
スプレッドシート(利用者 / 応答ログ)
ファイル構成は役割ごとに分けています。
src/
Config.gs 設定(秘密情報はスクリプトプロパティ)
Line.gs LINE API呼び出し・テンプレート
Store.gs シートの読み書き
Jobs.gs 8:00送信 / 15:00再通知 / 48hチェック / トリガー設定
Webhook.gs doPost(応答記録・相談通知)
Tests.gs DRY_RUNテスト
実装のポイント
1. ボタン付きテンプレートメッセージ
buttons テンプレートに postback アクションを3つ並べます(ボタンは最大4つ)。
function safetyTemplate_(heading) {
const actions = Object.keys(CFG.RESPONSES).map(function (k) {
return { type: 'postback', label: CFG.RESPONSES[k].label,
data: 'action=' + k, displayText: CFG.RESPONSES[k].label };
});
return {
type: 'template',
altText: heading + ':安否確認のお願い',
template: { type: 'buttons', title: heading, text: '今日の状況を教えてください', actions: actions },
};
}
2. 一斉送信は multicast(500件ずつ)
multicast は1回あたり最大500人なので分割して送ります。
function multicast_(userIds, messages) {
let ok = true;
for (let i = 0; i < userIds.length; i += CFG.MULTICAST_LIMIT) {
const chunk = userIds.slice(i, i + CFG.MULTICAST_LIMIT);
ok = lineCall_('/message/multicast', { to: chunk, messages: messages }) && ok;
}
return ok;
}
管理者3名への同時アラートも同じ関数を使います。
3. 「本日未応答」だけに再通知
利用者シートに「本日応答日」列を持たせ、応答時に当日の日付を書きます。15:00 の処理では、これが今日でない人だけを抽出します。
function afternoonReminder() {
const day = today_();
const pending = activeUsers_().filter(function (u) { return u.today !== day; });
if (pending.length) {
multicast_(pending.map(function (u) { return u.id; }), [safetyTemplate_('まだ確認できていません')]);
}
checkLongAbsence();
}
4. 48時間無応答の判定
最終応答日時から48時間以上経過した人を検出します。一度も応答していない人は登録日時を基準にします。同じ人へのアラートが毎日重複しないよう、「48hアラート送信日」列で 1人1日1回 に制限しています。
const overdue = activeUsers_().filter(function (u) {
const base = u.lastAt || u.registered;
return base && now - new Date(base).getTime() >= limit && u.alertDay !== day;
});
5. Webhook(doPost)と排他制御
GASをウェブアプリとしてデプロイし、そのURLをLINEのWebhook URLにします。応答が同時に来てもシートが壊れないよう LockService で排他します。
function doPost(e) {
const ok = ContentService.createTextOutput('OK');
if (!e || !e.parameter || e.parameter.token !== prop_('WEBHOOK_TOKEN', true)) return ok;
const events = JSON.parse(e.postData.contents).events || [];
const lock = LockService.getScriptLock();
lock.waitLock(20000);
try {
events.forEach(function (ev) { handleEvent_(ev); });
} finally { lock.releaseLock(); }
return ok;
}
「相談したい」のときだけ代表者へPushします。
if (key === 'consult') {
const rep = prop_('REPRESENTATIVE_ID');
if (rep) pushText_(rep, '【相談希望】' + name + ' さんが「相談したい」を押しました。');
}
6. トリガーは setupTriggers で一括作成
function setupTriggers() {
ScriptApp.getProjectTriggers().forEach(function (t) { ScriptApp.deleteTrigger(t); });
ScriptApp.newTrigger('morningBroadcast').timeBased().everyDays(1).atHour(8).create();
ScriptApp.newTrigger('afternoonReminder').timeBased().everyDays(1).atHour(15).create();
}
導入手順
-
スプレッドシートを作成し、拡張機能 → Apps Script にコードを配置(
claspでも可) -
スクリプトプロパティを設定
キー 内容 LINE_TOKENチャネルアクセストークン(長期) WEBHOOK_TOKENWebhook URLに付ける合言葉 ADMIN_IDS管理者3名のuserId(カンマ区切り) REPRESENTATIVE_ID代表者のuserId DRY_RUNtrueで送信せずログのみ(テスト用) -
setupTriggersを1回実行(権限を承認) -
ウェブアプリとしてデプロイ(実行ユーザー:自分 / アクセス:全員)
-
https://script.google.com/.../exec?token=<WEBHOOK_TOKEN>をLINE DevelopersのWebhook URLに設定し、Webhookの利用をON、応答メッセージをOFFにする -
利用者が友だち追加すると自動で「利用者」シートに登録される
テスト
DRY_RUN=true にして testAll を実行すると、LINEへは送らず [DRY_RUN] としてログに送信内容が出ます。8:00送信、15:00再通知、48時間アラート、相談通知をまとめて確認できます。
ハマりどころ・制約
-
署名検証ができない:GASの
doPostはX-Line-Signatureヘッダーを読めません。代替としてWebhook URLに秘密のトークンを付けて照合しています。トークンは漏らさないでください。 - トリガーの時刻精度:時間主導トリガーは指定時刻から約1時間の幅で実行されます。8:00ちょうどは保証されません。
- メッセージ数の上限:Push/multicastはLINE公式アカウントのプランの月間メッセージ数に計上されます。利用者数 ×(1〜2通/日)+アラート分で見積もってください。
- 個人情報:シートには氏名と応答履歴が入ります。共有範囲は最小限にしてください。
まとめ
サーバーレスで、無料枠の範囲でも運用できる安否確認の仕組みになりました。ロジックは「時間トリガー」「Webhook」「シート」の3つだけなので、応答の選択肢や通知時刻の変更も Config.gs を触るだけで済みます。
注:本記事のコードは構文チェックとDRY_RUN前提のテスト設計までで、実運用前に必ずご自身のLINE公式アカウントで動作確認してください。

