コンテンツへスキップ

LINE公式アカウントで定員・予約管理

LINE公式アカウントを触る機会があったので、定員・予約管理ができる仕組みの作り方を忘れないうちにメモ。

費用

  • LINE公式アカウント:
    無料枠の範囲で使えます。自動の定員・予約管理のほかに、手動の情報発信もしたいのであれば有料プランにしたほうがいいと思います。
  • Google Sheets:
    無料です。

手順

LINEの準備

  1. LINE公式アカウントを作成します。電話番号認証が必要です。利用規約とかに同意してLINE公式アカウントの作成が完了すると、LINE Official AccountManagerという管理画面が表示されます。
  2. LINE Developersにログインし、Developersアカウントを作成します。
  3. LINE Developersで新規プロバイダーを作成します。プロバイダー名はユーザーに見えないのでなんでもいいです。
  4. プロバイダーにMessaging APIのチャネルを追加します。チャネル名がユーザーに表示される公式LINEのの名称です。後から変えられます。
  5. チャネルが作成できたら、「チャネル基本設定」タブのチャネルシークレットと「Messaging API設定」タブのチャネルアクセストークンを後で使うのでメモしておきます。

Googleシートの準備

  1. Googleアカウントを持っていなかったら作成します。
  2. Google Sheetsにログインし、新しいスプレッドシートを作成します。
  3. スプレッドシート名(左上の標準で「無題のスプレッドシート」となっている箇所)を「人数管理」に変更します。シート(左下)を2つ作成し、名称を「Capacity」と「Reservations」に変更します。
  4. 「Capacity」シートのA1セルに「Current」と入力し、A2セルに「Capacity」を入力します。
  5. 「拡張機能」メニューから「Apps Script」をクリックします。App Scriptの無題のプロジェクト画面が別タブで開かれます。
  6. プロジェクト名を(左上の標準で「無題のプロジェクト」となっている箇所)を「Line Chat」に変更し、コード入力欄に以下のコードを貼り付けます。
const LINE_ACCESS_TOKEN = 'VQ8E6ED/7AYY++FcsWYxPizi32tqpSzjQ6JBFWDYff0MuoDwHRG62vUpdTVgPQvR0rb4LXrDoLydSzp8LRRLCbB7AUAN0n/f8TUQa/4OupWnZhgLnseiKHGilzWYciGjG9LvNfbR1mECy/lwNuUlzQdB04t89/1O/w1cDnyilGU=';
const SPREADSHEET_ID = '1n5mDl0mu3ABiflP4S1OEyGeMBBtp4tZ4c7Paaxgneuo';
const SHEET_NAME_CAPAC = 'Capacity';
const SHEET_NAME_RESERVE = 'Reservations';

function doPost(e) {
  const sheetCapacity = SpreadsheetApp.openById(SPREADSHEET_ID).getSheetByName(SHEET_NAME_CAPAC);
  const sheetReserve = SpreadsheetApp.openById(SPREADSHEET_ID).getSheetByName(SHEET_NAME_RESERVE);
  const data = JSON.parse(e.postData.contents);
  
  if (data.events[0].type === 'message' && data.events[0].message.type === 'text') {
    const userMessage = data.events[0].message.text;
    const replyToken = data.events[0].replyToken;
    const userId = data.events[0].source.userId;
    const userProfile = getUserProfile(userId);
    const userName = userProfile.displayName;
    const now = new Date();
    
    // 混雑状況の確認
    if (userMessage === '混雑状況') {
      const currentCustomers = sheetCapacity.getRange('B1').getValue();
      const maxCustomers = sheetCapacity.getRange('B2').getValue();
      const lastUpdateTime = sheetCapacity.getRange('B3').getValue();
      const lastUpdater = sheetCapacity.getRange('B4').getValue();
      replyToUser(replyToken, `現在の人数は${currentCustomers}人です。\n今日の最大プレイ可能人数は${maxCustomers}人までです。\n最終更新時間: ${lastUpdateTime}\n更新者: ${lastUpdater}`);
    
    // 混雑状況の更新
    } else if (userMessage === '混雑状況更新方法') {
      replyToUser(replyToken, `現在の人数と最大プレイ可能人数をスペースで区切って送信してください。\n例: "15 32" または "15 32"(半角スペースと全角スペースの両方が使用可能です)`);

    // 混雑状況の更新
    } else if (/^\d+\s+\d+$/.test(userMessage) || /^\d+ \d+$/.test(userMessage)) { // 半角スペースと全角スペースに対応
      const numbers = userMessage.split(/\s+| +/).map(Number);
      const currentCustomers = numbers[0];
      const maxCustomers = numbers[1];
      sheetCapacity.getRange('B1').setValue(currentCustomers);
      sheetCapacity.getRange('B2').setValue(maxCustomers);
      sheetCapacity.getRange('B3').setValue(now);
      sheetCapacity.getRange('B4').setValue(userName);
      replyToUser(replyToken, `現在の人数と最大プレイ可能人数を更新しました。\n現在の人数: ${currentCustomers}\n最大プレイ可能人数: ${maxCustomers}`);

    // 予約
    } else if (userMessage === '予約') {
      // 既に今日予約しているか確認
      const reservations = sheetReserve.getRange('A:B').getValues();
      for (let i = 0; i < reservations.length; i++) {
        const reservationDate = new Date(reservations[i][1]);
        if (reservations[i][0] === userName && isSameDay(reservationDate, now)) {
          replyToUser(replyToken, `${userName}さんは既に${reservationDate.toLocaleTimeString()}に予約されています。`);
          return;
        }
      }
      // 新しい予約を登録
      sheetReserve.appendRow([userName, new Date()]);
      replyToUser(replyToken, `${userName}さん、予約が完了しました。`);

    // 予約をキャンセル
    } else if (userMessage === 'キャンセル') {
      const reservations = sheetReserve.getRange('A:B').getValues();
      let reservationCancelled = false;
      for (let i = reservations.length - 1; i >= 0; i--) {
        const reservationDate = new Date(reservations[i][1]);
        if (reservations[i][0] === userName && isSameDay(reservationDate, now)) {
          sheetReserve.deleteRow(i + 1);
          reservationCancelled = true;
        }
      }
      if (reservationCancelled) {
        replyToUser(replyToken, `${userName}さんの今日の予約をキャンセルしました。`);
      } else {
        replyToUser(replyToken, `${userName}さんは今日予約がされていません。`);
      }

    // 想定外のメッセージを受け取った場合
    } else {
      replyToUser(replyToken, `${userMessage}は無効なメッセージです。`);
    }
  }
}

// 【関数】 ユーザー名取得
function getUserProfile(userId) {
  const url = `https://api.line.me/v2/bot/profile/${userId}`;
  const options = {
    method: 'get',
    headers: {
      'Content-Type': 'application/json; charset=UTF-8',
      'Authorization': 'Bearer ' + LINE_ACCESS_TOKEN,
    },
  };
  const response = UrlFetchApp.fetch(url, options);
  const userProfile = JSON.parse(response.getContentText());
  return userProfile;
}

// 【関数】 同じ日付か確認
function isSameDay(date1, date2) {
  return date1.getFullYear() === date2.getFullYear() &&
         date1.getMonth() === date2.getMonth() &&
         date1.getDate() === date2.getDate();
}

// 【関数】 ユーザーに返信
function replyToUser(replyToken, message) {
  const url = 'https://api.line.me/v2/bot/message/reply';
  const payload = {
    replyToken: replyToken,
    messages: [{ type: 'text', text: message }],
  };
  const options = {
    method: 'post',
    headers: {
      'Content-Type': 'application/json; charset=UTF-8',
      'Authorization': 'Bearer ' + LINE_ACCESS_TOKEN,
    },
    payload: JSON.stringify(payload),
  };
  UrlFetchApp.fetch(url, options);
}
  1. コードのアクセストークンとスプレッドシートIDは自分のものに変更してください。スプレッドシートIDはURLの/d/の後に続く1n5mDl0mu3ABiflP4S1OEyGeMBBtp4tZ4c7Paaxgneuoのような文字列です。
  2. プロジェクトを保存し、右上の「デプロイ」ボタンから「新しいデプロイ」をクリックします。種類は「ウェブアプリ」を選択し、アクセスできるユーザーは「全員」にして、「デプロイ」をクリックします。
  3. 「アクセスを承認」ボタンをクリックします。「このアプリは確認されていません」と言われるので「詳細を表示」からLine Chatに遷移するリンクをクリックします。Line ChatにGoogle Accountにアクセスする許可をしてください。
  4. デプロイが完了したら、ウェブアプリのURLを後で使うのでメモしておきます。

アプリをLineに登録

  1. Line Developersのチャネル画面を開きます。
  2. 「Messaging API設定」タブのWebhook URLに先程メモしたウェブアプリのURLを設定し、Webhookの利用を有効化します。「検証」ボタンをクリックし、「成功」と表示されることを確認します。
  3. LINE Official AccountManagerの設定画面を開きます。
  4. 「設定」の「応答設定」を開き、応答機能でWebhookが有効化されていることを確認します。チャットは迷惑ユーザーの認識に使えるので有効化しておきます。あいさつメッセージと応答メッセージは無効化します(ちゃんと使いこなせるのであれば有効化してもいいです)。
  5. 「ホーム」の「リッチメニュー」を開き、「混雑状況確認」「混雑状況更新」「予約」「予約キャンセル」の4種類のボタンを作ります。各ボタンのアクションをテキストにして、混雑状況確認ボタンは「混雑状況」、混雑状況更新ボタンは「混雑状況更新方法」、予約ボタンは「予約」、予約キャンセルボタンは「キャンセル」と入力します。

テスト

  1. 自分のLINEに自動的に友だち登録された公式アカウントはGoogleシートと連動していない方です。Line Developersで左上からチャネル名のアカウントに切り替え、一番左下の「友だち追加ガイド」から公式LINEのを自分のLINEに友だち登録します。
  2. リッチメニューを使ってちゃんと返答されるか確認します。
  3. コードを更新する際は、デプロイしなおし、Webhook URLを更新します。