2020.10.20
Gmailを使った架電予約メールの作り方
こんにちは。KDDIウェブコミュニケーションズ 技術部の山本です。
弊社でサポート提供しているTwilioを利用して、GASとGmailを使って簡単にできる「架電予約メール」を作ってみました。
本記事では、作り方と苦戦したところを紹介していきます。
Call reservation by mail(架電予約メール)
機能
1. 電話をかけて欲しい'日時'、'電話番号'、'読み上げるメッセージ'を予約用アドレスにメール送信する。
2. 指定した時間になると、Twilioから電話がかかってきて、メッセージが再生される。
機能仕組み

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'を新規で作成する。(各項目の確認箇所は、本記事の最後に記載)
変更するのは、ファイル名と1行目のタイトルだけです。
Googleドライブにアクセスして'GoogleAppsScript'のプロジェクトを作成する。
スクリプトを作成する。
変更するのは、プロジェクト名とスクリプト部分です。
下記のスクリプトを貼り付けて、'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) } }
実行トリガーを作成する。
トリガーの作成から、1分おきで2件作成します。
動作確認
1. 準備したGmailアドレスに、メールを送信する。
CallMe 2020/9/30 9:1 080xxxxxxxx 9時になりました。 お仕事始めますか? 今日は、11時から打ち合わせが予定されています。 さらば!
2. 少しすると受付メールが返信されてくる。
3. 時間になると電話がかかってくる。
苦戦したところ
https://www.twilio.com/docs/ でRestAPIを調べるとできることの説明が少なく、Twilio SDKを利用する前提での説明だった。
今回は、GoogleAppsScriptで作りたいので、TwilioSDKを導入することはできない。 RestAPIで実行する参考情報を入手するために、以下の方法を取りました。
1. Node.jsのSDKをローカルに導入してサンプル実行
2. TwilioConsoleで通話のコールログを確認 3. RequestInspectorにNode.jsからRestAPIリクエストを確認
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
購入電話番号
アカウントSID/AUTHTOKEN
GoogleSpreadSheets
SpreadSheetsID
まとめ

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