〰️

Google Form × LINE Messaging API × GAS で、お問い合わせをLINEに自動通知する

に公開

概要

個人開発で運用しているiOSアプリ「Tateca」のユーザーからのお問い合わせ対応のオペレーションを強化する。

現在、ユーザーからのお問い合わせはGoogleフォームを通じてスプレッドシートに記録されているが、手動でシートを確認しなければならず、見落とすと返信が遅れることがある。
そこで、お問い合わせが届いたタイミングでLINEに自動通知する仕組みを構築する。

GASで完結するので、インフラ構築不要・無料枠(200通/月)で運用できます。

使用技術

  • LINE Messaging API
  • Google App Script(以下GAS)

LINE Notifyが2025年3月31日に提供終了とのことなので、今回はLINE Messaging APIを使用する

前提

Google Form と Google Spreadsheet の連携が済んでいること。

完成後のワークフロー

ユーザーがフォームから送信
→ スプレッドシートに追加
onFormSubmit トリガーでWebhook発火
→ LINEにメッセージを送信

実装手順

ステップ1. LINE公式アカウントの準備

こちらの公式ドキュメントに従ってLINE公式アカウントの作成とMessaging APIの有効化を行う

料金プランに記載がありますが、200通までは無料でメッセージが送れるようです。

ステップ2. チャネルアクセストークンを取得

取得したトークンはGAS側でのメッセージ送信に使用する。
今回は長期のチャネルアクセストークンを作成。

ステップ3. userIdを取得するWebhookを実装

LINEメッセージのPush送信には 送信先のuserIdが必要。お問い合わせがあるたびにこのuserIdへメッセージを送信します。

3-1. GASにプログラムを追加

既存のお問い合わせ対応用のスプレッドシートを開き、「拡張機能 → Apps Script」からGASエディタを開いて以下を貼り付けます。

/** 自分のLINE userIdをline_idsに保存するだけのWebhook */
function doPost(e) {
  if (!e || !e.postData || !e.postData.contents) {
    return ContentService.createTextOutput('no body');
  }

  const body = e.postData.contents;
  const json = JSON.parse(body);
  const events = json.events || [];
  if (events.length === 0) {
    return ContentService.createTextOutput('no events');
  }

  // Spreadsheet取得
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  let sheet = ss.getSheetByName('line_ids');
  if (!sheet) {
    sheet = ss.insertSheet('line_ids');
    sheet.getRange(1, 1, 1, 2).setValues([['userId', 'registered_at']]);
  }

  const now = new Date();
  const existingIds = sheet
    .getRange(2, 1, Math.max(sheet.getLastRow() - 1, 0), 1)
    .getValues()
    .flat()
    .filter(String);

  // イベントからuserIdを抽出
  events.forEach(ev => {
    const id = ev?.source?.userId;
    if (id && !existingIds.includes(id)) {
      sheet.appendRow([id, now]);
    }
  });

  return ContentService.createTextOutput('ok');
}

3-2. GASをデプロイ

設定項目 Note
次のユーザーとして実行 自分(=スクリプトの所有者) スプレッドシートに書き込むため、自分を選択
アクセスできるユーザー 全員(匿名ユーザーを含む) LINEサーバーはGoogleアカウントを持たないため必要

3-3. Webhook URLをMessaging APIに登録

LINE Official Account Managerの設定 -> Messaging API -> Webhook URLにGASのデプロイURLを設定。
これで、Bot宛てにメッセージを送るとGASが発火します。

3-4. userIdを取得

Botにメッセージを送ると、line_idsシートに userIdが保存されます。公式アカウントに対して適当なメッセージを送って下さい。

ステップ4. 問い合わせフォームの送信をLINEに通知

取得した userId を使って、問い合わせが届いたときに自分のLINEへ自動通知します。

4-1. GASのコードを上書き
上記のコードを以下の内容に置き換え、再デプロイします。

/** Push送信(userIdを環境変数から取得) */
function pushMessage_(text) {
  const props = PropertiesService.getScriptProperties();
  const ACCESS_TOKEN = props.getProperty('CHANNEL_ACCESS_TOKEN');
  const TO_ID = props.getProperty('LINE_USER_ID');
  if (!ACCESS_TOKEN || !TO_ID) throw new Error('必要なプロパティが設定されていません。');

  const url = 'https://api.line.me/v2/bot/message/push';
  const payload = { to: TO_ID, messages: [{ type: 'text', text }] };
  const params = {
    method: 'post',
    contentType: 'application/json',
    headers: { Authorization: 'Bearer ' + ACCESS_TOKEN },
    payload: JSON.stringify(payload),
  };

  const res = UrlFetchApp.fetch(url, params);
  if (res.getResponseCode() >= 300) {
    throw new Error(`LINE push error ${res.getResponseCode()}: ${res.getContentText()}`);
  }
}

function onFormSubmit(e) {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheetUrl = ss.getUrl();

  const named = e && e.namedValues ? e.namedValues : {};
  const lines = Object.entries(named).map(([k, v]) => `${k}: ${Array.isArray(v) ? v.join(', ') : v}`);

  const text =
    `【新着フォーム回答】\n` +
    `受信: ${Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy/MM/dd HH:mm')}\n` +
    `${lines.join('\n')}\n\n` +
    `開く: ${sheetUrl}`;

  pushMessage_(text);
}

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

  • CHANNEL_ACCESS_TOKEN:長期チャネルアクセストークン
  • LINE_USER_ID:自分のLINE userId(前ステップで取得したもの)

4-3. トリガーを追加

GASコンソールの「トリガー」から以下を設定:

  • 実行する関数を選択:onFormSubmit
  • 実行するデプロイを選択:Head
  • イベントのソースを選択:スプレッドシートから
  • イベントの種類を選択:フォーム送信時

ステップ5. 完成

これでフォーム送信のたびにLINEに通知が届くようになりました 🎉

こんな感じ:
LINE通知の例

Discussion