妻の誕生日にLINE BotとLIFFを使って謎解きアプリを作った話

# はじめに

妻はリアル謎解きゲームが趣味なのですが、今年は新型コロナウイルスの影響で公演への参加が出来ていませんでした。
そこで、勉強中のLINE BotやLIFFアプリの技術を使用して謎解きアプリを自作することは出来ないかと試みたのが本記事の内容になります。

主に使用したLINEの機能は

- Messaging APIのWebhook
- イメージマップメッセージ
- LIFFのQRコードリーダー
- シェアターゲットピッカー

です。
実際の謎を解く流れに沿って、どのように上記機能を使用したのかを紹介します。


# 謎解きの流れ

## 1. LINE BotのQRコードを読み取って友だち登録  

本アプリは、友だち追加されたことをトリガーに以下の画像を送信します。
この画像から、問題が9問存在することを示唆しています。

![解答用紙.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314315/9eb30acc-f712-5787-78d8-2789bbba4890.png)


友だち追加されたことはLINE Messaging APIの「フォローイベント」というWebhookイベントを受信することで知ることが出来ます。

参考：[Webhookイベントのタイプ](https://developers.line.biz/ja/docs/messaging-api/receiving-messages/#webhook-event-types)

今回はAPI Gateway + AWS Lambdaを使用してWebhookイベントを受信しています。


## 2. リッチメニューからLIFFアプリを起動

リッチメニューにLIFFアプリのURLを設定してあり、タップすると以下のようなスマートフォンのホーム画面をイメージしたようなアプリが起動します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314315/1cad97c4-86c5-428b-71f7-2eab8b6db777.png)


このLIFFアプリは主に

1. 問題の表示
2. QRコード読み込み
3. シェア

の3つの機能を持っています。これらの機能を使用して、

1. 部屋に隠されたQRコードを探す
2. 「QRコード読み込み」機能で上記QRコードを読み込む
3. 「問題の表示」エリアに追加された謎を解く
4. 「シェア」機能を使用することによって表示される謎を解く

といった手順で進めていくことを想定しました。


### 2.1. 問題の表示

このアプリではLIFFアプリを開いた段階で解放されている問題のみを表示するよう制御する必要があります。
今回は、解放された問題の管理にDynamoDBを使用し、以下のようなフローで表示する問題を取得しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314315/a27623af-e8fd-55ff-c1af-552a2b17a782.png)

LIFFアプリでユーザー情報を取得する方法はいくつかあるのですが、今回はサーバにユーザ情報を送信する必要があったためID Tokenを使用しています。
ユーザー情報の扱い方については公式のドキュメントが詳しいので、必ず参照してから設計することをお勧めします。

参考：[LIFFアプリおよびサーバーでユーザー情報を使用する](https://developers.line.biz/ja/docs/liff/using-user-profile/#page-title)

### 2.2. QRコード読み込み

LIFFアプリではLIFF SDKを使用することで簡単にQRコードリーダーを使用することが出来ます。（ただし、2020/09/25現在はAndroidのみ対応）

参考：[QRコードリーダーを表示する](https://developers.line.biz/ja/docs/liff/developing-liff-apps/#opening-qr-code-reader)

基本的なフローは2.1.と変わらないので割愛しますが、今回はQRコードで読み込んだ内容をAWS LambdaにPOSTしてDynamoDBの情報を更新しました。

### 2.3. シェア

LIFFアプリには、ユーザーの任意の友だちにメッセージを送信する「[シェアターゲットピッカー](https://developers.line.biz/ja/docs/liff/developing-liff-apps/#share-target-picker)」という機能が存在します。
本LIFFアプリでは、シェアボタンをタップするとターゲットピッカーが表示され、シェアを行うと以下のようなメッセージを送信します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314315/37ee3c46-b279-8601-8580-b4a35c77f722.png)

この「シェア謎」という、ボタンのようなメッセージは「[テンプレートメッセージ](https://developers.line.biz/ja/docs/messaging-api/message-types/#template-messages)」というもので、今回は「シェア謎」ボタンをタップすると先ほどとは別のLIFFアプリが表示されるように作りました。

シェア謎は、LIFFアプリが表示されたトークルームに応じて出題する謎を変化させています。
表示されたトークルームは[`liff.getContext()`メソッド](https://developers.line.biz/ja/docs/liff/developing-liff-apps/#getting-context)を使用することで取得できます。

1対1トークで開いたときには2人でコミュニケーションすることで解ける謎、グループやトークルームでは多人数で協力することで解ける謎を作成しました。


## 3. 最後の謎

9問の謎を解いて1.の画像の解答欄に答えを当てはめると、（非常にありがちなのですが）矢印の先が「ディナータップしろ」という指示になります。
最初の画像は「[イメージマップメッセージ](https://developers.line.biz/ja/docs/messaging-api/message-types/#imagemap-messages)」というメッセージで送信しており、「ディナー」の部分をタップするとイベントが発生するようにしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314315/695b3c2d-b768-c948-3030-9d65a4c914a8.png)


イメージマップメッセージは通常はドキュメントにも記載されているように画像を４分割などして、メニュー的な用途で使うのだと思います。
実際、[ALICE(Game Creator)](https://line.me/R/ti/p/%40312jzqac)という謎解きLINE Botでは問題の出題に活用されています。


自分はいかにも謎解き向きな機能だなと感じて、今回は最後の謎を表示するための仕組みとして使用しました。


ちなみに、イメージマップメッセージで送信する画像はS3などのストレージに配置するのが良いと思いますが、画像を配置する際に拡張子が含まれていると表示されません。
参考：[画像の設定方法](https://developers.line.biz/ja/reference/messaging-api/#base-url)

地味にハマってしまい、ちゃんと画像が表示されるまで少し時間を要しました。


# さいごに

謎解きアプリの出題はサプライズで行ったのですが、準備から実際の回答まで大きな問題なく終えました。
そこそこ設計をきっちりと行って開発したので、その点は良かったかなと感じています。

謎の作成は初めてだったので色々と改善の余地がありましたが、小謎の出題方法から最終問題を出題するギミックまでをLINE上で行えたのは面白かったです。

LINEにはまだまだ謎解きに利用できる機能が多く存在しており、LIFFアプリも単にWebアプリのプラットフォームとしてLINEの機能を使えるようにしているだけなので、技術力があればもっと多くのことが出来たかなと思います。

大変でしたが、また謎解きアプリを作ってみたいと思えるほど面白かったので次は公開できるような質に出来ると良いなと考えています。