##今回やりたいこと
今回はPHPでLINE Messaging APIを使いクイズアプリを作成します。
######仕様
・リッチメニューをタップしクイズを出題
・4つの選択肢から回答する
・正解の場合のみ解説の参考サイトurlを添付
・クイズはDB管理とし、クイズの追加・編集・削除機能がある管理画面の作成
※管理画面は後日公開予定です。
こちらから友達追加してお試しいただけます!
https://lin.ee/3AOdmRRlo
以上の仕様を実装します。
##VPSで環境構築
VPSはこちらを参考に構築しました。詳細は下記サイトから確認してみてください。他にも参考になる記事がたくさんあるのでググってみて下さい!
ネコでもわかる!さくらのVPS講座 〜第一回:VPSてなんだろう?〜
##Messaging APIを設定
Messaging APIの概要はこちら
##プログラム
######DBカラム
quizzes : id | quiz | ans1 | ans2 | ans3 | ans4 | correct | url | deleted
quiz:問題文
ans1~4:選択肢1~4
correct:正解番号
url:解説サイトのurl
deleted:論理削除用
<?php
$accessToken = 'アクセストークン';
//file_get_contents()関数でPOSTされたJSON文字列を取り出し
$jsonString = file_get_contents('php://input');
//エラーログ記録
error_log($jsonString);
//json_decode()関数でJSONをデコードして変数に格納
$jsonObj = json_decode($jsonString);
//メッセージイベントを取得
//スタンプ、テキスト、画像などの場合がある
$message = $jsonObj->{"events"}[0]->{"message"};
//ReplyToken取得
//受信したイベントに対して返信を行うために必要
$replyToken = $jsonObj->{"events"}[0]->{"replyToken"};
//メッセージタイプ取得
//ここで、受信したメッセージがテキストか画像かなどを判別
$type = $jsonObj->{'events'}[0]->{'type'};
// 送られてきたメッセージの中身からレスポンスのタイプを選択
//$typeがmessageの場合
if ($type == 'message') {
//messageがtextであり、かつtextがクイズの場合クイズを返信する
if ($message->{"text"} == 'クイズ') {
//DB接続
$dbh = new PDO("mysql:host=localhost;dbname=DB名", 'ユーザー名', 'パスワード');
//sql文を変数にセット
//DBから論理削除されていないクイズIDの配列を作る
$sql = "SELECT id FROM quizzes WHERE deleted = 0";
$res = $dbh->query($sql);
$quizIdArray = array();
foreach ($res as $row) {
array_push($quizIdArray, $row['id']);
}
//クイズIDをランダムに並べ替える
shuffle($quizIdArray);
//クイズID配列の先頭要素をキーに、クイズを取得し表示する
$targetQuizId = $quizIdArray[0];
$sql2 = "select * from quizzes where id = $targetQuizId";
$stmt = $dbh->query($sql2);
$targetRow = $stmt->fetch();
//$messageDataにクイズの内容を代入する
$messageData = [
'type' => 'template',
'altText' => 'クイズ',
'template' => [ 'type' => 'buttons', 'text' => $targetRow['quiz'],
'actions' => [
[ 'type' => 'postback', 'label' => $targetRow['ans1'], 'data' => 'ans=' . $targetRow['ans1'] . '&num=1&correct=' . $targetRow['correct'] . '&id=' . $targetRow['id'] ],
[ 'type' => 'postback', 'label' => $targetRow['ans2'], 'data' => 'ans=' . $targetRow['ans2'] . '&num=2&correct=' . $targetRow['correct'] . '&id=' . $targetRow['id'] ],
[ 'type' => 'postback', 'label' => $targetRow['ans3'], 'data' => 'ans=' . $targetRow['ans3'] . '&num=3&correct=' . $targetRow['correct'] . '&id=' . $targetRow['id'] ],
[ 'type' => 'postback', 'label' => $targetRow['ans4'], 'data' => 'ans=' . $targetRow['ans4'] . '&num=4&correct=' . $targetRow['correct'] . '&id=' . $targetRow['id'] ],
]
]
];
//messageがtextであり、かつtextがいいえであった場合の返信を$massageDataに代入
}elseif ($message->{"text"} == 'いいえ') {
$messageData = [ 'type' => 'text', 'text' => 'いつも[解剖学クイズbot]をご利用いただきありがとうございます。クイズチャレンジお疲れ様でした!楽しんでいただけましたでしょうか?またのチャレンジをお待ちしております!' ];
$messageData2 = [ 'type' => 'text', 'text' => '再度クイズにチャレンジする場合は、「クイズにチャレンジ!」をタップしてください!' ];
} else {
//$typeが上記以外の場合の返信内容を$messageDataに代入
$messageData = [ 'type' => 'text', 'text' => '申し訳ありません、その操作には対応しておりません。「クイズにチャレンジ!」をタップしてクイズにチャレンジしてみて下さい。' ];
}
}
//$typeがpostbackの場合
if ($type == 'postback') {
// JSONデータからポストバックデータを取得
$postback = $jsonObj->{"events"}[0]->{"postback"}->{"data"};
//文字列を名前と値に分解し変数に代入
parse_str($postback, $data);
//各値を変数に代入
$answer = $data["ans"];
$number = $data["num"];
$id = $data["id"];
$correct = $data["correct"];
//ユーザーの選択した選択肢と正解番号を比較し、正解・不正解の場合で返信内容を$massageDataに代入
//正解の場合
if ($number == $correct) {
$dbh = new PDO("mysql:host=localhost;dbname=DB名", 'ユーザー名', 'パスワード');
//選択された選択肢のIDから解説サイトのurlを取得
$sql3 = "SELECT url FROM quizzes WHERE id = $id";
$stmt2 = $dbh->query($sql3);
$targetRowUrl = $stmt2->fetch();
$url = $targetRowUrl['url'];
//$massageDataに返信内容を代入
$messageData = [ 'type' => 'text', 'text' => $answer . 'ですね。' ];
$messageData2 = [ 'type' => 'text', 'text' => '素晴らしい!『正解』です!!!' ];
$messageData3 = [
'type' => 'template',
'altText' => '解説',
'template' => [
'type' => 'buttons',
'title' => '解説です!',
'text' => '確認してみてね!',
'actions' => [
[
'type' => 'uri',
'label' => 'Wikipediaへ移動',
'uri' => "$url",
]
]
]
];
$messageData4 = [
'type' => 'template',
'altText' => '次のクイズにチャレンジしますか?',
'template' => [ 'type' => 'confirm', 'text' => '次のクイズにチャレンジしますか?',
'actions' => [
[ 'type' => 'message', 'label' => 'はい', 'text' => 'クイズ' ],
[ 'type' => 'message', 'label' => 'いいえ', 'text' => 'いいえ' ],
]
]
];
}else{
//不正解の場合の返信内容を$massageDataに代入
$messageData = [ 'type' => 'text', 'text' => $answer . 'ですね。' ];
$messageData2 = [ 'type' => 'text', 'text' => 'むむ、、『不正解』です。' ];
}
}
//メッセージの数に合わせ条件分岐
if ($messageData4 == true) {
$response = [ 'replyToken' => $replyToken, 'messages' => [$messageData,$messageData2,$messageData3,$messageData4] ];
}elseif ($messageData3 == true) {
$response = [ 'replyToken' => $replyToken, 'messages' => [$messageData,$messageData2,$messageData3] ];
}elseif ($messageData2 == true) {
$response = [ 'replyToken' => $replyToken, 'messages' => [$messageData,$messageData2] ];
}else{
$response = [ 'replyToken' => $replyToken, 'messages' => [$messageData] ];
}
error_log(json_encode($response));
//curlセッション初期化。urlも設定
$ch = curl_init('https://api.line.me/v2/bot/message/reply');
//curlオプション設定
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($response));
curl_setopt($ch, CURLOPT_HTTPHEADER, array( 'Content-Type: application/json; charser=UTF-8', 'Authorization: Bearer ' . $accessToken ));
//curl実行
$result = curl_exec($ch);
//エラーログ記録
error_log($result);
//curlセッション終了
curl_close($ch);
?>
##工夫した点
・クイズをDB管理とし、追加・編集・削除機能をつけた
・クイズをランダムに出題
・ユーザーからのレスポンスに応じて異なる処理
##改善案
・メッセージの数に合わせた条件分岐がダサいのでスマートに書けないものか。。。
・クイズ内容を身体の部位ごとに指定するなど、分野を分ける
・解説を外部サイトに依存しているが、bot内で完結しても良いか
・その場合、画像付きの解説などを入れるとUXが高められそう