こんにちは、hagaです。
テーマは前回に引き続き、「 Twilio + WebSheetsで忘年会に使える抽選アプリを作ってみた 」です。
前回は、WebSheetsの連携とTwilioの初期設定について解説いたしました。
概要や構成図なんかも解説しておりますので、まだという方は是非ご覧あれ。
Twilio + Google SpreadSheet で忘年会に使える抽選アプリを作ってみた【前編】
本記事ではアプリの主軸となるTwilioの構築をしていきます。
それでは、Twilio + Google SpreadSheetで忘年会に使える抽選アプリを作ってみた 後編と参りましょう。
目次
Twilio Function & Google SpreadSheet連携
画面左のメニュータブ、・・・マークを開くと、「 Functions 」というサービスがあります。
ここでは、サーバレスでNode.jsのプログラムを実行することができます。
今回はデータベースの代わりとしてGoogle SpreadSheetを利用しますので、そこら辺も含めてやっていきたいと思います。
ちなみにFunctionsには最新版(ベータ)とClassic版がありますが、今回はClassicを利用しますので、作業を行う場所には注意をしてください。
スプレッドシート作成
Functionを作る前にデータベースとして扱うスプレッドーシートの作成を行います。
とは言ってもスプレッドシート自体はWebSheets連携の際に作成したものを利用しますので、そこにシートを加えベースの形を作るだけとなります。
画像を参考に、電話番号のエントリーと当選結果を管理するシートを作成しましょう。
スプレッドシート連携
次にスプレッドーシートの連携を行います。
↓のTwilioの公式ブログを参考に準備作業を行っていただくのですが、いくつか注意点があります。
まず、スプレッドシートはWebSheets連携で作成したものを利用してください。
また、ブログ内のStaffとShiftワークシートIDの代わりにListとEntryのワークシートIDを控えましょう。
ブログ内”Node.jsプロジェクトの作成とパッケージのインストール”以降の作業は不要です。スプレッドシートの情報を控えるところまでを行っていください。
https://www.twilio.com/blog/load-data-from-google-spreadsheet-jp
スプレッドシート側の前準備ができたら、Functionsに戻り環境変数とパッケージを設定していきます。
画面左のFunctionsメニューから”Functions(Classic)”をクリックし、”Configuration[設定]”を開きましょう。
“Environment Variables”では環境変数が、”Dependencies”ではNodeで利用するパッケージを指定することができます
控えておいたスプレッドシートのIDをSPREADSHEET_IDに、ListのワークシートIDを、LIST_WORKSHEET_IDに、EntryのワークシートIDをENTRY_WORKSHEET_IDにそれぞれ値として入力します。
TWILIO_STUDIO_FLOW_IDには、後ほど作成するフローのIDを入れますので、この時点ではなくて大丈夫です。
今回使用するパッケージは「 dotenv 」と「 google-spreadsheet 」です。
動作を確認したのはこれらのバージョンのみとなります。
エントリー用Functionの作成
“Functions(Classic)”を開き、画面左部の+ボタンをクリックします。
ここでFunctionのテンプレートを指定できるのですが、今回はblankを選択しCreateをクリックします。
名前とPathは画像の通り設定し、CODEの欄に↓コードをまるっと貼り付けましょう。
9行目credentialsの部分には、先ほどの準備で発行しダウンロードした資格情報ファイルの中身をそのまま貼り付けてください。”~”の部分が生成されているはずです。
34行目の”B30″という部分には、任意の数値を記載してください。20名の想定なら”B30″といった感じです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
exports.handler = async function(context, event, callback) { require('dotenv').config(); const { GoogleSpreadsheet } = require('google-spreadsheet'); console.log('Success'); // スプレッドシートIDと資格情報を用いてGoogleスプレッドシートをロード const doc = new GoogleSpreadsheet(context.SPREADSHEET_ID); const credentials = { "type": "service_account", "project_id": "~", "private_key_id": "~", "private_key": "~", "client_email": "~", "client_id": "~", "auth_uri": "~", "token_uri": "~", "auth_provider_x509_cert_url": "~", "client_x509_cert_url": "~" }; await doc.useServiceAccountAuth(credentials); await doc.loadInfo(); const entrySheet = await doc.sheetsById[context.ENTRY_WORKSHEET_ID]; const entryRows = await entrySheet.getRows(); const n = await entryRows.length + 1; let result = { "string": "no" }; await entrySheet.loadCells('B1:B30'); //"30"で設定した数が最大エントリー数になるので、想定するエントリー人数+余分の数値を設定 for (let i = 1; i < n+1; i++) { if (entrySheet.getCell(i, 1).value === event.phoneNumber) { result.string = "yes" } } if (result.string === 'no') { await entrySheet.addRow({ EntryNumber:n, PhoneNumber:`'${event.phoneNumber}`, Categories:'未当選' }); } callback(null, result); }; |
設定ができたら、画面下部の”Save”ボタンで変更を保存しましょう。
抽選用Functionの作成
同じ手順で抽選用のFunctionを作成していきます。
先ほどと同じく、↓コードの9行目に資格情報を貼り付けます。
51行目の数字部分には用意した景品の数を入力しましょう。
また、33行目, 55行目, 79行目にも//以降の指示の通り記載をしてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
exports.handler = async function(context, event, callback) { require('dotenv').config(); const { GoogleSpreadsheet } = require('google-spreadsheet'); console.log('Success'); // スプレッドシートIDと資格情報を用いてGoogleスプレッドシートをロード const doc = new GoogleSpreadsheet(context.SPREADSHEET_ID); const credentials = { "type": "service_account", "project_id": "~", "private_key_id": "~", "private_key": "~", "client_email": "~", "client_id": "~", "auth_uri": "~", "token_uri": "~", "auth_provider_x509_cert_url": "~", "client_x509_cert_url": "~" }; await doc.useServiceAccountAuth(credentials); await doc.loadInfo(); const entrySheet = await doc.sheetsById[context.ENTRY_WORKSHEET_ID]; const entryRows = await entrySheet.getRows(); const n = await entryRows.length + 1; let list = []; await entrySheet.loadCells('A1:C30'); // "30"で設定した数が最大エントリー数になるので、想定するエントリー人数+余分の数値を設定 for (let i = 1; i < n+1; i++) { if (entrySheet.getCell(i, 2).value === '未当選') { let entryTarget = [ `${entrySheet.getCell(i, 0).value}`, `${entrySheet.getCell(i, 1).value}` ]; list.push(`${entryTarget}`); } } const raffleTarget = await list[Math.floor(Math.random()*list.length)]; const entryNumber = await raffleTarget.split( ',' )[0]; const phoneNumber = await raffleTarget.split( ',' )[1]; const listSheet = await doc.sheetsById[context.LIST_WORKSHEET_ID]; const listRows = await listSheet.getRows(); const m = 16; // 用意した景品の数を記載 let rowNumber1 = -1; await listSheet.loadCells('F2:F30'); // "30"で設定した数が最大景品数になるので、用意した景品数+余分の数値を設定 for (let i = 1; i < m+1; i++) { if (listSheet.getCell(i, 5).value === '未当選') { rowNumber1 += 1; } } const premium = await listRows[rowNumber1].Subtitle; const rowNumber2 = Number(`${entryNumber}`) - 1; entryRows[rowNumber2].PhoneNumber = `'${phoneNumber}`; entryRows[rowNumber2].Categories = await premium; await entryRows[rowNumber2].save(); const phoneNumberLast4 = phoneNumber.substr(-4); listRows[rowNumber1].Categories = '当選済み'; listRows[rowNumber1].Description = await `当選者: ${phoneNumberLast4}`; await listRows[rowNumber1].save(); const client = context.getTwilioClient(); client.studio.v1.flows(context.TWILIO_STUDIO_FLOW_SID).executions.create({ to: phoneNumber, from: '+815000000000', // Twilioで取得した電話番号をE.164形式で記載 parameters: { "premium": premium } }) .then((call) => { callback(null, "OK"); }) .catch((error) => { callback(error); }); }; |
“Save”ボタンで変更を保存したらFunctionの作成は完了です。
Twilio Studio
実行するFunctionが完成したので、次はTwilio側の処理の流れを設定していきます。
まずはメニューから「 Studio 」というサービスを探しクリックしましょう。
こちらは、電話がかかってきたときやメッセージを受信した際のフローを作成、管理することができるサービスです。
今回はエントリーの受け付けを行うフローと抽選を行うフロー、当選者に通知するフローの3つを作成していきます。
エントリー用Flowの作成
Studioを開き、画面左部の青い+マークをクリックします。任意のフロー名を入力し、”Next”で次へ進みましょう。
フローのテンプレートを選択できるので、”Start from scratch.”を選択します。
これがTwilioのフロー作成画面です。
主な操作はドラック&ドロップです。画像ウィジェット内の赤線部がウィジェットの分類名ですので、こちらを参考に右メニューからウィジェットを配置しましょう。
全体像ができたら、次にウィジェットごとの設定をしていきます。
左から、Run Function, Split Based On…, Say/Playの設定となります。
Run Functionで実行するFunctionは後ほど作成しますので、URLの部分は空欄で大丈夫です。
また、Say/Playは3つ配置していますが文言以外は同じ設定になります。文言は↓の全体画像を参考に設定しましょう。
画像の紫矢印で繋がっている部分も説明していきます。
Split Based On…ウィジェット右下部の赤い”New”をクリックし、“Condition Matches”をクリックすることで分岐を追加できます。今回は2つほど追加し、画像のようにSay/Prayウィジェットとの接続を行ってください。
分岐条件はウィジェット設定の2ページ目、”Transitions”で設定できます。
↓画像の通り設定していきましょう。
一通りの設定ができたら右上の赤い”Publish”ボタンをクリックし、変更を保存したらエントリー用フローの完成となります。
抽選用Flowの作成
次に抽選を行うフローの作成です。とは言っても抽選処理は全てFunctionで行っているので、Functionを実行するだけのとてもシンプルなものです。
画像にはないですが、今回フロー名は「 RaffleFlow 」としました。
↓の通り設定を行い、”Publish”で保存しましょう。
最後に、当選通知用のフローを作成します。
当選通知用Flowの作成
このフローは抽選Functionから実行されるフローになります。フロー名は「 OutGoingFlow 」としました。
当選者の電話番号と景品情報を受け取り、対象番号へ自動で発信を行うものです。
発信を行いアナウンスするだけなので、これもとてもシンプルです。↓の通り作成しちゃいましょう。
Say/Playの設定は先程のエントリー用のフローを参考に、↓画像のように文言を変えて設定してください。
聞き逃さないよう、”NUMBER OF LOOPS”という設定項目は2にしておくといいと思います。
フローを保存したら、最後に当選通知用FlowのFlowSIDを環境変数として登録する作業です。
フローの一覧ページ、対象フローのSIDをコピーしてください。↓画像の赤線部分です。
コピーしたら、Functions(Classic)の設定ページにて、
先程追加しなかった”TWILIO_STUDIO_FLOW_SID”のValue部分にコピーしたSIDを追加し保存しましょう。
お疲れ様です。以上でTwilio側で行う作業は完了となります。
Twilioの抽選フローを実行するWebアプリを作る
さて、Twilioの構築は完了し、エントリーはいつでもできるようになりました。
あとは抽選のフローを任意のタイミングで実行できれば抽選アプリとしては完成となります。
Twilio FlowはトリガーとしてREST APIをサポートしており、外部からAPIをたたくことで実行することができます。
認証情報や宛先電話番号などの必須パラメータを付与してREST APIのURLにPOSTしてあげればいいので、curlコマンドで
1 |
curl -X POST "{REST APIのURL}" -u {アカウントSID}:{AUTH TOKEN} --data-urlencode "To={宛先電話番号(E.164)}" --data-urlencode "From={発信元電話番号(E.164)}" |
とするだけでできます。
しかし、忘年会の会場でポチポチとコマンド叩くのはなんか合わない & ついでにTwilioのライブラリを試してみたい、ということで、
今回はNode.js用のTwilioライブラリを使って超簡単なWebアプリを作ってみました。
Webアプリ構成
- Windows 10
- npm 6.14.4
- Node.js 12.17.0
- React 17.0.1
- Twilio Node 3.42.2
プロジェクトの作成
NodeやReactはインストールは省略して、プロジェクトの作成とTwilioライブラリのインストールをやっていきます。
1 2 3 4 5 |
// reactアプリの作成 npx create-react-app twilio-lottery-kicker cd twilio-lottery-kicker // twilioライブラリのインストール npm install --save twilio@3.42.2 |
Twilioライブラリのバージョンは3.42.2に指定してインストールします。
最初のうちはこのときの最新バージョン(3.52.0)で開発していたのですが、APIリクエストをするときにライブラリの内部エラーが発生してしまったためです。
あまり詳しく調べられていませんが、最新版で使っているURLクラスあたりのエラーのようです。
コード
ボタンを押してAPIリクエストするだけのアプリを作ります。
必須パラメータは環境変数で設定すると想定して、以下のコードでリクエストの実装ができます。
APIのリクエスト先は抽選用のフロー(RaffleFLow)を指定してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
function App() { const kickLottery = () => { // APIクライアント生成 const client = require('twilio')( process.env.REACT_APP_TWILIO_ACCOUNT_SID, // アカウントSID process.env.REACT_APP_TWILIO_AUTH_TOKEN // AUTH TOKEN ) try { // APIリクエスト client.studio.flows(process.env.REACT_APP_TWILIO_FLOW_ID) // フローID .executions.create({ from: process.env.REACT_APP_TWILIO_FROM_NUMBER, // 発信元番号 to: process.env.REACT_APP_TWILIO_TO_NUMBER // 宛先番号 }) .then(flow => { console.log(flow) alert("リクエストが送信されました") }) .catch(err => { console.log(err) alert("エラーが発生しました") }) } catch (e) { console.log(e); } } return ( {/* domは省略 */} <button onClick={() => { kickLottery() }}> START </button> ); } |
最終的に以下のようなWebアプリを作成しました。
これでいつでもどこでも抽選を開始できるようになりました!
抽選アプリ完成です!
終わりに
長くなってしまいましたが、ここまで来ればあとは電話をかけて抽選を行うだけとなります。
お疲れ様でした。
Twilioには今回利用したもの以外にもたくさんサービスがありますので、是非触ってみていただけたらと思います。
- AWS Lambda コンソールの組み込みコードエディタを利用する際の注意点 - 2022-04-18
- Twilio + Google SpreadSheet で忘年会に使える抽選アプリを作ってみた【後編】 - 2020-12-25
- QNAPからLinuxへ rsyncでバックアップをとろう - 2020-10-20