1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GAS + LINE Messaging APIで安否確認システムを作る(サーバー不要・無料

1
Posted at

はじめに

一人暮らしの高齢者や小規模コミュニティ向けに、毎朝LINEで安否を確認し、応答がなければ管理者に知らせる仕組みを Google Apps Script(GAS)だけで作りました。サーバーもDBも不要で、データはスプレッドシートに残ります。

機能

時刻 / 契機 処理
毎朝 8:00 全利用者へボタン付きテンプレートメッセージを一斉送信
ボタン押下 「元気です/相談したい/今日は外出」をシートに記録
「相談したい」押下 代表者へ即時通知
15:00 本日未応答の人にだけ再通知
15:00 最終応答から48時間以上経過した人を、管理者3名へ同時に訪問依頼アラート

画面イメージ

LINE(利用者・代表者・管理者)

line_screens.png

① 毎朝8:00にボタン付きメッセージが届き、押すと返信が返ります。② 15:00には未応答の人にだけ再通知します。③ 「相談したい」は代表者へ即時、48時間無応答は管理者3名へ同時に届きます。

スプレッドシート

sheet.png

「利用者」シートは最新状態を、「応答ログ」シートは応答の履歴を保持します。

画面はコードの仕様に沿って作成したイメージです(氏名は架空)。実機の見た目とは多少異なります。

全体構成

[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();
}

導入手順

  1. スプレッドシートを作成し、拡張機能 → Apps Script にコードを配置(clasp でも可)

  2. スクリプトプロパティを設定

    キー 内容
    LINE_TOKEN チャネルアクセストークン(長期)
    WEBHOOK_TOKEN Webhook URLに付ける合言葉
    ADMIN_IDS 管理者3名のuserId(カンマ区切り)
    REPRESENTATIVE_ID 代表者のuserId
    DRY_RUN trueで送信せずログのみ(テスト用)
  3. setupTriggers を1回実行(権限を承認)

  4. ウェブアプリとしてデプロイ(実行ユーザー:自分 / アクセス:全員)

  5. https://script.google.com/.../exec?token=<WEBHOOK_TOKEN> をLINE DevelopersのWebhook URLに設定し、Webhookの利用をON、応答メッセージをOFFにする

  6. 利用者が友だち追加すると自動で「利用者」シートに登録される

テスト

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公式アカウントで動作確認してください。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?