Twilioでオセロ(Reversi)を作ってみました

本記事は、Twilio Advent Calendar 2020の12月22日担当分です。

はじめに

Twilioについて勉強せねば、と思っていたところでタイミングよくAdvent Calendarの話があったので手を挙げてみました。私は普段、Webやメールを扱うエンジニアです。電話を扱う機会はそれほど多くありません。また、Twilioのハンズオンに参加したりドキュメントを読んだことがある程度で、プロダクトでTwilioを使い込んでいるわけでもありません。今回の記事は、Twilio初心者が実際に使ってみて得られた気付きを共有する内容と思って読んでいただけると幸いです。

新しいサービスやハードウェアを理解しようと思ったときに、チュートリアルに沿って手順通り進めるとたいてい動くものができあがるので、なんとなく理解した気分になってしまうのですが、そこから外れたことをやろうと思った瞬間「何から手を付けりゃいいんだっけ」と思考が固まってしまうことがよくあります(自分だけかもしれませんが)。そんな時は、何でもいいので自分で考えたアプリケーションにそのサービスなりハードウェアを組み込んでみるのが、理解のための近道と思っています。

というわけで、Twilioを使ってオセロ(Twilio-Reversi)を作ってみました。

概要

このアプリは、大きく分けて2つの機能で構成されています。一つは、電話機からTwilioの番号に電話をかけてゲームを進める部分です。もう一つは、打った手を対戦相手の電話機にSMSで通知する部分です。

まずはじめにゲームの進行部分です。

(1)電話機からTwilioの番号に電話をかけると、(2)Twilioからアプリケーションに対してWebhookのリクエストが飛びます。アプリケーションはリクエストに応じてゲームを進行させ、(3)状況に応じた結果をTwiMLとして返すと、(4)Twilioは電話機に対して音声応答を返します。これが基本的な流れですが、石を置けない座標を指定したり、手番じゃない方の電話機で石を置こうとすると、(3)の内容が変化して電話機には入力の拒否ややり直しを伝える音声応答が返ります。

次に対戦相手にSMSで通知する部分です。

こちらは非常にシンプルで、SMS APIを呼び出すと、Twilioが以下のようなメッセージをSMSとして対戦相手の電話に送ってくれます。


2台の電話機で交互に電話をかけて石を置いていき、全マスが埋まるか、両プレイヤーがパスをしたらゲーム終了となり、その旨がSMSで通知されます。

気付き

静的なTwiMLとTwilMLを切り替えるプログラム

今回は、すべてのTwiMLをTwilio Node.jsライブラリを使って動的に生成しましたが、よくよく仕様を整理してみると静的なTwiMLを数パターン(正常系、準正常系数パターン)用意して、あとはプログラムでTwilioに返すTwiMLの切り替えを制御するのが良さそうに思いました。こうすることでコード量が減らせて見通しがよくなりそうです。もちろん、TwiMLに動的な内容の埋め込みが必要な場合は、素直にTwilioライブラリを使うのが良いと思います。

電話機の制約されたインターフェイス

電話機からの入力で使えるのは「数字と*と#」だけです。可変長のデータを入力させる場合は、このうちデータの終端として1種類を使う必要があります。これらの制約はとても厳しくて、使い勝手のよいインターフェイスにするにはいろいろと注意すべき点がありそうです。

一度に入力させるデータの種類は1種類に

 今回は2種類のデータ(ゲームを特定するIDと石を置く座標)を一度にまとめて入力させることにしましたが、たぶんこれは初見でわかりにくいので、できれば一度に入力させるデータは1種類に絞った方がよさそうです。その分、入力ステップ数が増えてしまうのが難点です。

可変長データはできるだけ避けたい

利用者からみると、データが可変長かどうかなんてわからないので、データの終端文字を入力してもらおうと思ってもなかなか期待通り入力してくれません。データはなるべく固定長がよさそうです。

エラー処理は丁寧に

Webのフォームと違って、キーを押した瞬間入力が確定してしまうので、入力ミスをした際に利用者の負担をできるだけ減らしてあげる工夫が必要そうです。エラーメッセージをわかりやすくしたり、再入力のポイントをわかりやすくしたり、といったあたりでしょうか。

セッションが使えない

Webの場合、セッションを利用して状態の保持を簡単に行うことができますが、電話の場合そういった便利な仕組みはありません。今回はゲーム進行の都合上、石を置いたら毎回電話を切らなくてはいけないので、次に電話をかけた際に、石を置くところに復帰させる方法に悩みました。最終的には、ゲームを特定するIDと石を置く座標を数字で指定させることにしました。

等幅フォントを期待してはいけないメッセージアプリ

上のキャプチャを見るとわかるかと思いますが、ボードの状態をSMSで飛ばした瞬間、絶望しました。スマホ側のメッセージアプリが等幅フォントじゃないので崩れるんですね。少なくとも私のスマホでは。アプリ側でフォント設定を変更することもできません。また、1行に表示可能な文字数も機種によって異なるはずなので、こういうUIには無理があるんだな、と気付かされました。

料金負担設計

やる前から薄々感付いてはいましたが、ゲームを最後まで進めるにはそれなりのお金がかかります。例えばドコモ回線で、TwilioでUSの番号を利用した場合、1ゲームあたり概算で、

  • 国際電話(プレイヤー負担):60回(30秒)=60✕31円=1860円
  • 国際SMS送信(サービス提供者負担):60回=60✕9.7円=582円

これくらいの金額がかかります。料金的な意味で、今回のような用途に電話やSMSがあまり合っていないという話は置いておいて、サービスを設計する上では誰がどういう料金をどれくらい負担するのかを考慮した方がよさそうです。メールやWebと違って電話の場合、基本的に使った人が支払う、かつ、極めてシンプルな従量課金の世界であることを実感しました。


チャージはプロジェクトに紐づく

昔チャージしたポイントが、いつの間にか消えていて不思議に思っていたのですが、チャージしたポイントは「プロジェクト」に紐づくんですね。チャージしたのと別のプロジェクトを選択していて全く気付いていませんでした。赤い芸人さんが詳しく説明されているので、読んで理解しました。プロジェクトはダッシュボードの一番左上のプルダウンで選択できます。


チャージ済みのプロジェクトを削除すると返金される

上述のチャージポイントがプロジェクトに紐づくことに気付く前の話ですが、不要なプロジェクトを削除していたら、そのうちの一つにチャージポイントが残っていたようで、しばらくしたら返金する旨メールで連絡が来ていました。ありがたや。

まとめ

今回はVoiceSMSのみを利用してみました。最初、Webhookを受ける機能を用意しなくてはいけないのが分かりづらい印象でしたが、そこさえ乗り越えてしまえば、一般的なWebアプリケーションを作成するのと同じスキルでいろいろな機能が利用できそうです。

他にもサーバサイドをFunctions上で動かしたり、UIをFAXにしたり、VerifyAuthyで二要素認証を入れたり、Videoでお互いの顔が見えるようにしたり、Studioでグラフィカルにワークフローを作成したりとTwilioだけでいろいろな機能を組み込めそうです。

最後に、昔作ったSendGrid-Reversiという同系列のネタもどうぞ。

明日はkimihomさん担当です!

コメント

このブログの人気の投稿

Joinノードを使う(その1)

Execノードを使う

Joinノードを使う(その4)