Twilioブログ

Gmailを使った架電予約メールの作り方

こんにちは。KDDIウェブコミュニケーションズ 技術部の山本です。

弊社でサポート提供しているTwilioを利用して、GASとGmailを使って簡単にできる「架電予約メール」を作ってみました。

本記事では、作り方苦戦したところを紹介していきます。

Call reservation by mail(架電予約メール)

機能

01 02 03

1. 電話をかけて欲しい'日時'、'電話番号'、'読み上げるメッセージ'を予約用アドレスにメール送信する。

2. 指定した時間になると、Twilioから電話がかかってきて、メッセージが再生される。

機能仕組み

04

Reservation (予約)

利用サービス
  • Gmail
  • GoogleSpreadSheets
  • GoogleAppsScript
GoogleAppsScript
1. 'Gmail'から'未読'、'件名: CallMe'で検索し予約メールを取得する。
// Search call request mail.
    const searchQuery = 'is:unread subject:' + REQUEST_MAIL_SUBJECT
    const threads = GmailApp.search(searchQuery)
2. メール本文から予約情報として'日時'、'電話番号'、'読み上げるメッセージ'を取得する。
    // Get request information from mail body.
    const requestBody = thread.getMessages()[0].getPlainBody().replace(/\r\n/g, '\n').split('\n').filter(line => !!line)
    const callTime = requestBody[0]; requestBody.shift();
    const callTelNo = requestBody[0].replace(/-/g, ''); requestBody.shift();
    const callMessage = requestBody.join('\n')
    
3. 予約情報を'GoogleSpreadSheets'に記録する。
    // Open spreadsheet.
    const triggerSheet = SpreadsheetApp.openById(SPREAD_SHEET_ID).getSheets()[0]
    triggerSheet.getRange(insertRow, SPREAD_SHEET_TIME_COLUMN).setValue(callTime)
    triggerSheet.getRange(insertRow, SPREAD_SHEET_TELNO_COLUMN).setValue(callTelNo)
    triggerSheet.getRange(insertRow, SPREAD_SHEET_MESSAGE_COLUMN).setValue(callMessage)
    
4. 予約受付結果をGmailアカウントから返信する。
    replayMessage =
      '電話予約を受付しました。' + '\r\n' +
      '=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\r\n' +
      ' Call time: ' + callTime + '\r\n' +
      ' Phone number: ' + callTelNo + '\r\n' +
      ' Message: \n' + callMessage.replace(/\n/g, '\r\n ') + '\r\n' +
      '=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\r\n'

    // Reply to reception email.
    const triggerMail = thread.getMessages()[0]
    const replayMailDrfut = triggerMail.createDraftReply(replayMessage)
    replayMailDrfut.send()
    

Call (架電)

利用サービス
  • Twilio REST API
  • GoogleAppsScript
  • GoogleSpreadSheets
GoogleAppsScript
1. 'GoogleSpreadSheets'から予約情報を取得する。
    // Open spreadsheet.
    const triggerSheet = SpreadsheetApp.openById(SPREAD_SHEET_ID).getSheets()[0]
    // Get request data from spreadsheet.
    const triggers = triggerSheet.getRange(SPREAD_SHEET_START_ROW,
      SPREAD_SHEET_TIME_COLUMN,
      triggerSheet.getLastRow() - SPREAD_SHEET_START_ROW + 1,
      SPREAD_SHEET_MESSAGE_COLUMN
    ).getValues()
    
2. 時間が到来している場合に、Twilio REST APIを呼び出し電話をかける。
    // Check if the call time has come.
    if ((new Date(trigger[TRIGGER_TIME_INDEX])) > new Date) continue

    // Creating Twilio API request param.
    const postParam = {
      From: TWILIO_FROM_TELNO,
      To: '+81' + trigger[TRIGGER_TELNO_INDEX],
      Twiml: '<Response>' + trigger[TRIGGER_MESSAGE_INDEX].split(/\n/).map(
          message => {return (message? '<Say voice="Polly.Mizuki" language="ja-jp">' + message + '</Say>' : '') }
        ).join('') + '</Response>',
      Timeout: '55'
    }
    // Call Twilio API.
    const response = UrlFetchApp.fetch('https://api.twilio.com/2010-04-01/Accounts/' + TWILIO_ACCOUNT_SID + '/Calls', {
      method: 'POST',
      headers : {Authorization: " Basic " + Utilities.base64Encode(TWILIO_ACCOUNT_SID + ':' + TWILIO_AUTHTOKEN)},
      payload: postParam,
      muteHttpExceptions:true
    })
    

作り方

事前準備

  • Twilioのアカウントを作成し、電話番号を購入する。(各項目の確認箇所は、本記事の最後に記載)
    • 取得項目
      • 購入電話番号
      • アカウントSID
      • AUTHTOKEN
  • Googleアカウントを作成し、Gmailアドレスを取得する。
    • 取得項目
      • メールアドレス

作成

Googleドライブにアクセスをして'GoogleSpreadSheets'を新規で作成する。(各項目の確認箇所は、本記事の最後に記載)

05

変更するのは、ファイル名と1行目のタイトルだけです。

Googleドライブにアクセスして'GoogleAppsScript'のプロジェクトを作成する。

06

07

スクリプトを作成する。

08

変更するのは、プロジェクト名とスクリプト部分です。

下記のスクリプトを貼り付けて、'SPREAD_SHEET_ID'、'TWILIO_ACCOUNT_SID'、'TWILIO_AUTHTOKEN'、'TWILIO_FROM_TELNO'を確認した値に書き換えます。

RequestCall.gs

const REQUEST_MAIL_SUBJECT = 'CallMe'
const FAIL_REPLY_MESSAGE =
  '電話予約を受付できませんでした。' + '\r\n' +
  '指定された条件を再度ご確認ください。\r\n' +
  '=-=-=-Format-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-\r\n' +
  '[1行目]: 電話をかける時間 (例) 2020/10/01 12:45\r\n' +
  '[2行目]: 電話番号 (例) 080-1234-1234\r\n' +
  '[3行目以降]: 読み上げるメッセージ\r\n' +
  '=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\r\n'

const SPREAD_SHEET_ID = 'XXXXXXXXXXXXXXXXXXXXXXXXXXX'
const SPREAD_SHEET_START_ROW = 2
const SPREAD_SHEET_TIME_COLUMN = 1
const SPREAD_SHEET_TELNO_COLUMN = 2
const SPREAD_SHEET_MESSAGE_COLUMN = 3

const TRIGGER_TIME_INDEX = 0
const TRIGGER_TELNO_INDEX = 1
const TRIGGER_MESSAGE_INDEX = 2

const TWILIO_ACCOUNT_SID = 'ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
const TWILIO_AUTHTOKEN = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
const TWILIO_FROM_TELNO = '+8150xxxxxxxx'

/**
 * Function : getCallRequestFromMail
 * Overview : Get the call request from the email and write it on the spread sheet.
 */
function getCallRequestFromMail() {
  // Open spreadsheet.
  const triggerSheet = SpreadsheetApp.openById(SPREAD_SHEET_ID).getSheets()[0]

  // Search call request mail.
  const searchQuery = 'is:unread subject:' + REQUEST_MAIL_SUBJECT
  const threads = GmailApp.search(searchQuery)

  // Reply to reception email function.
  const sendReplayMail = (thread, replayMessage) => {
    const triggerMail = thread.getMessages()[0]
    const replayMailDrfut = triggerMail.createDraftReply(replayMessage)
    replayMailDrfut.send()
  }

  for (const thread of threads) {
    let replayMessage = ''
    // Get request information from mail body.
    const requestBody = thread.getMessages()[0].getPlainBody().replace(/\r\n/g, '\n').split('\n').filter(line => !!line)

    // Line count check.
    if (requestBody.length < 3) {
      // Reply to reception email.
      sendReplayMail(thread, FAIL_REPLY_MESSAGE)
      // Mark as read.
      thread.markRead()
      continue
    }

    const callTime = requestBody[0]; requestBody.shift();
    const callTelNo = requestBody[0].replace(/-/g, ''); requestBody.shift();
    const callMessage = requestBody.join('\n')
    const insertRow = triggerSheet.getLastRow() + 1

    // Check request information.
    if (!callTime.match(/^\d{4}[\/-]\d{1,2}[\/-]\d{1,2} \d{1,2}:\d{1,2}$/) ||
        !callTelNo.match(/^0\d+$/) || callTelNo.length < 10 || callTelNo.length > 11 || !callMessage) {
      // Reply to reception email.
      sendReplayMail(thread, FAIL_REPLY_MESSAGE)
      // Mark as read.
      thread.markRead()
      continue
    }

    // Set request data from spreadsheet.
    triggerSheet.getRange(insertRow, SPREAD_SHEET_TIME_COLUMN).setValue(callTime)
    triggerSheet.getRange(insertRow, SPREAD_SHEET_TELNO_COLUMN).setValue(callTelNo)
    triggerSheet.getRange(insertRow, SPREAD_SHEET_MESSAGE_COLUMN).setValue(callMessage)
    replayMessage =
      '電話予約を受付しました。' + '\r\n' +
      '=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\r\n' +
      ' Call time: ' + callTime + '\r\n' +
      ' Phone number: ' + callTelNo + '\r\n' +
      ' Message: \n' + callMessage.replace(/\n/g, '\r\n ') + '\r\n' +
      '=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\r\n'

    // Reply to reception email.
    sendReplayMail(thread, replayMessage)

    // Mark as read.
    thread.markRead()
  }
}

/**
 * Function : callFromTwilio
 * Overview : Get the call request from the email and write it on the spread sheet.
 */
function callFromTwilio() {
  // Open spreadsheet.
  const triggerSheet = SpreadsheetApp.openById(SPREAD_SHEET_ID).getSheets()[0]

  // Confirmation of existence of request data.
  if (triggerSheet.getLastRow() < SPREAD_SHEET_START_ROW) return

  // Get request data from spreadsheet.
  const triggers = triggerSheet.getRange(SPREAD_SHEET_START_ROW,
    SPREAD_SHEET_TIME_COLUMN,
    triggerSheet.getLastRow() - SPREAD_SHEET_START_ROW + 1,
    SPREAD_SHEET_MESSAGE_COLUMN
  ).getValues()

  for (let i = 0; i < triggers.length; i++) {
    const trigger = triggers[i]

    // Check if the call time has come.
    if ((new Date(trigger[TRIGGER_TIME_INDEX])) > new Date) continue

    // Creating Twilio API request param.
    const postParam = {
      From: TWILIO_FROM_TELNO,
      To: '+81' + trigger[TRIGGER_TELNO_INDEX],
      Twiml: '<Response>' + trigger[TRIGGER_MESSAGE_INDEX].split(/\n/).map(
          message => {return (message? '<Say voice="Polly.Mizuki" language="ja-jp">' + message + '</Say>' : '') }
        ).join('') + '</Response>',
      Timeout: '55'
    }
    // Call Twilio API.
    const response = UrlFetchApp.fetch('https://api.twilio.com/2010-04-01/Accounts/' + TWILIO_ACCOUNT_SID + '/Calls', {
      method: 'POST',
      headers : {Authorization: " Basic " + Utilities.base64Encode(TWILIO_ACCOUNT_SID + ':' + TWILIO_AUTHTOKEN)},
      payload: postParam,
      muteHttpExceptions:true
    })
    console.log(JSON.stringify(response.getContentText("UTF-8")))

    // Delete called request data.
    triggerSheet.deleteRows(i + SPREAD_SHEET_START_ROW)
  }
}

実行トリガーを作成する。

09

トリガーの作成から、1分おきで2件作成します。

10

11

動作確認

1. 準備したGmailアドレスに、メールを送信する。

CallMe

2020/9/30 9:1
080xxxxxxxx
9時になりました。
お仕事始めますか?
今日は、11時から打ち合わせが予定されています。
さらば!

2. 少しすると受付メールが返信されてくる。

12

3. 時間になると電話がかかってくる。

苦戦したところ

https://www.twilio.com/docs/ でRestAPIを調べるとできることの説明が少なく、Twilio SDKを利用する前提での説明だった。

今回は、GoogleAppsScriptで作りたいので、TwilioSDKを導入することはできない。 RestAPIで実行する参考情報を入手するために、以下の方法を取りました。

1. Node.jsのSDKをローカルに導入してサンプル実行

2. TwilioConsoleで通話のコールログを確認 3. RequestInspectorにNode.jsからRestAPIリクエストを確認

13

4. 無事にRestAPIのリクエスト設定内容が取得できたので、curlコマンドで試して見ると、Node.JSで行ったことと同じ結果が得られる。

curl -X POST 'https://api.twilio.com/2010-04-01/Accounts/ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/Calls' \
    -d 'From=%2B8150xxxxxxxx' \
    -d 'To=%2B81xxxxxxxxxx' \
    -d 'Twiml=Ahoy there!' \
    -d 'Timeout=55' \
    -u 'ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:xxxxxxxxxxxxxxxxxxxxxxxxxxxxx'

付録(各項目の確認方法)

Twilio

購入電話番号

14

アカウントSID/AUTHTOKEN

15

GoogleSpreadSheets

SpreadSheetsID

16

 

まとめ

アプリケーションエンジニア 山本 祥平
アプリケーションエンジニア 山本 祥平

技術本部第二開発部に所属し、レンタルサーバー「CPI」のバックオフィスシステム再構築(マイグレーション・モダナイゼーション)PJに従事。 CI/CD、DevOps、チーム力/品質向上などに力を入れて取り組んでいる。

CTA_まずはtwilioを使ってみる。

Share!!

この記事を読んだ人へのオススメ

  • お役立ち情報
  • イベント情報
  • 相談会申込
  • 導入事例