そのうち誰かの役に立つ

もしくは誰の役にも立たない

Google Apps Script(GAS)とLINE Messaging APIでユーザアカウント不要のイベント受付システムを作る -4- Gmailでメールを送信したりLINEにPushメッセージを送ったりする

続きもの。

  1. 準備
  2. Googleフォームの回答と編集URLを取得する
  3. Googleドキュメントでチケットを作成する
  4. Gmailでメールを送信したりLINEにPushメッセージを送ったりする ←今ココ
  5. まとめ (2019/11/05更新)

用語

以降、特に説明なくこれらの用語を使う。

ユーザID

LINEの個々のユーザを識別するID。 システム的にはメッセージの送信者や宛先に利用することが推測される。

グループID

LINEで個々のグループを識別するID。 メッセージの宛先が個人(ユーザ)ならユーザID、グループ宛ならグループIDと使い分けることでメッセージをルーティングできると考えられる。

ルームID

LINEで個々のトークルームを識別するID。以下同文。

通知

システムへの回答(予約)があると、何かしら通知をしたくなる。 例えば、ユーザ宛には予約内容の確認や回答編集URL、発行したチケットのURLなどを知らせるため、 管理者宛には予約というイベントが発生したことそのものを知らせるため、といった目的が考えられる。

通知は別にメールだけでもいいのだけど、メールはあんまり速報感がないと思っている(個人の感想)ので、 速報感を出そうとすると何かしらのチャットアプリもしくはメッセージングアプリを使うことになる。 個人的にはslackが一番チェック率が高いのだけど、自分以外に通知するメンバーの共通利用アプリを考えると、まあLINEでいいかとなった。

LINEグループIDを取得するWebアプリをGASで作る

LINE Messaging APIでグループにメッセージを送信する場合、宛先のグループIDを取得する必要が出てくる。 このグループIDというのは(一般ユーザはそんなもの意識する必要がないので)普通にLINEのアプリを触っても出てこない。 ということでGASでちょっとしたWebアプリを作成し、LINE Botと連携させてグループIDを取得する。

前提として、取得したいグループにLINE Botがメンバーとして入っている必要がある。 1つのグループにLINE Botは1つしか参加させられないので注意。

Webアプリのコードは以下。

// LINE Botのアクセストークン
var access_token = PropertiesService.getScriptProperties().getProperty("ACCESS_TOKEN") || "";
// LINE Botにreplyさせるキーワード
var keyword = PropertiesService.getScriptProperties().getProperty("KEYWORD") || "";

function doPost(e) {
  var contents = e.postData.contents;
  var obj = JSON.parse(contents);
  obj["events"].forEach(function(event) {
    if (event.type == "message") {
      // メッセージテキストが特定のキーワードだったらreplyを返す(それ以外での反応を抑制する)
      if (event.message.text == keyword) {    
        var post_data = {
          "replyToken": event.replyToken,
          "messages": [
            {
              "type": "text",
              "text": event.source.groupId
            }
          ]
        };
        var options = {
          "method": "POST",
          "headers": {
            "Content-Type": "application/json",
            "Authorization": "Bearer " + access_token
          },
          "payload": JSON.stringify(post_data),
          "muteHttpExceptions": true
        };
        UrlFetchApp.fetch("https://api.line.me/v2/bot/message/reply", options);
      }
    }
  })
}

このスクリプトをWebアプリとして公開する。 スクリプトエディタのメニューから 公開 > ウェブアプリケーションとして導入 を選択すると公開設定モーダルウィンドウが出てくるので、以下のように設定して公開する。

  • Project version は「New」
  • Execute the app as は「My ({自分のGoogleアカウント})」
  • Who has access to the app は「Anyone, even anonymous」

再度公開設定モーダルを開くと Current web app URL で公開したWebアプリのURLが取得できるので、これをメモする。

LINE DevelopersポータルからLINE Botのチャネル基本設定を開き、メッセージ送受信設定 > Webhook送信 を「利用する」に変更、 Webhook URL に先ほど公開したWebアプリのURLを設定する。

これで、LINE Botが参加しているグループで特定のキーワードをPOSTするとBotがそのグループのグループIDを返信してくれる。

スクリプト

前置きが長くなったが、Gmailでメールを送信したりLINEグループにPushメッセージを送信するスクリプトは下記。 既にフォームの回答 answers を得ており、メールのタイトル subject や本文、メッセージ文章 message はよしなに作成されているものとする。

// LINE Botのアクセストークン
var access_token = PropertiesService.getScriptProperties().getProperty("ACCESS_TOKEN") || "";
// LINEグループのグループID
var group_id = PropertiesService.getScriptProperties().getProperty("GROUP_ID") || "";
// GoogleフォームのオブジェクトID
var form_id = PropertiesService.getScriptProperties().getProperty("FORM_ID") || "";

// Gmailでメールを送る
function sendMail(answers, subject, message, admin) {
    // ユーザにメールを送る
    GmailApp.sendEmail(answers["メールアドレス"], subject, message);
    if (admin) {
      // 必要に応じて管理者にもメールを送る
      GmailApp.sendEmail(Session.getActiveUser().getEmail(), subject_admin, message);
    }
}

// LINEでPushメッセージを送る
function pushMessage(message) {
  var push_data = {
    "to": group_id,
    "messages": [
      {
        "type": "text",
        "text": message,
      }
    ]
  };
  var options = {
    "method": "POST",
    "headers": {
      "Content-Type": "application/json",
      "Authorization": "Bearer " + access_token
    },
    "payload": JSON.stringify(push_data)
    "muteHttpExceptions": true
  };
  UrlFetchApp.fetch("https://api.line.me/v2/bot/message/push", options);
}

// リマインダ
function remind() {
  if (form_id != "") {
    // フォームの回答からキャンセルではなくリマインダ「要」の人にメール送信
    var form = FormApp.openById(form_id);
    var responses = form.getResponses();
    responses.forEach(function(response) {
      var items = response.getItemResponses();
      var answers = {};
      items.forEach(function(item) {
        answers[item.getItem().getTitle()] = item.getResponse();
      }}
      if (answers["お名前"] != "" && answers["メールアドレス"] != "" && answers["ご来場予定者数"] != "キャンセル" && answers["リマインダ希望"] == "要") {
        var subject = "リマインダ";
        var message = "イベントが近づいてきました、準備は大丈夫ですか?";
        // subjectとmessageをよしなに作成する
        sendMail(answers, subject, message, false)
      }
    })
  }
}

ここで remind() 関数について任意の時刻で実行するようトリガーを作るとリマインダになる。 トリガー設定モーダルウィンドウで イベントのソース を「時間主導型」、 時間ベースのトリガーのタイプ を「特定の日時」にして任意の日時を入れればよい(下図)。

Time-based Trigger
時間ベースのトリガー

リファレンスは これとか これとか これとか

実装のポイントとしては

  • 何かの事故でLINE Messaging APIが200以外を返すと例外吐いて死ぬので UrlFetchApp.fetch() 関数のoptionsで "muteHttpExceptions": true にしておく
  • Messaging APIが無料枠で送信できるPushメッセージの数はフリープランで1,000通/月だが、 グループにPushすると1通でBotを除くメンバーの数だけその枠が消費される。 例えば10人いるグループだと実質100通/月の無料枠となるので注意が必要。

最後に

このシリーズで作成したシステムが動いている演奏会はこちらです(宣伝)

2019年11月30日(土)開場:13時00分  開演:13時30分
すみだトリフォニーホール 小ホールにて(東京都墨田区)
【アクセス】JR「錦糸町駅」北口より徒歩5分/東京メトロ「錦糸町駅」3番出口より徒歩5分
入場無料 事前予約制