作成した経緯
友人がITパスポートの勉強をしていると聞いたので、アプリではなくLinebotで一問一答を作成し、スプレッドシートで単語を管理して自分だけの一問一答ができるのではないかと考え…という建前の元、何か作りたかっただけ(笑)
使用技術
- Google App Script
- LINE Messaging API
機能一覧
まだ未実装のものもありますが、ざっと基本的なところまで
1. 問題出題
スプレッドシートから単語を取得して、その単語の意味を出題してくれる機能です。
2. 選択肢
答えの選択肢を ダミー(2) + 正答(1) で出してくれる機能です。
3. 答え
4. 終了
5. ジャンル変更
出題される単語のジャンルを変更する機能。ITパスポートはストラテジ系
, マネジメント系
, テクノロジ系
があったはずだからその変更に使います。まだ実装できていませんが^^;
6. リマインダー
定時でリマインドする機能。こちらもまだできていません^^;
各機能のコード
各機能をGAS(Google Apps Script)で作成したのでその説明
メッセージに対する分岐
Userが送ってきた各メッセージに対する分岐の部分。
var ACCESSTOKEN = 'YOUR_ACCESS_TOKEN';
const URL = 'YOUR_LINEBOT_REPLY_URL';
const PUSH_URL = 'YOUR_LINEBOT_PUSH_URL';
const SPREADSHEET_ID = 'YOUR_SPREADSHEET_ID';
const SpreadsheetName = "問題用のスプレッドシート";
const ManegerSpreadsheetName = "管理用のスプレッドシート";
function doPost(e) {
let data = JSON.parse(e.postData.contents)
var event = data.events[0];
let targetColumn = getColumn(event);
switch (event.message.text) {
case '出題':
let answer = replyGame(event);
insertData(answer, targetColumn);
break;
case '終了':
quitReply(event, targetColumn);
deleteData(targetColumn);
break;
case '答え':
correctReply(event, targetColumn);
break;
case '増やす':
let options = makeOption(event, "ごめん!その機能は今実装中だから待って!");
UrlFetchApp.fetch(URL, options);
break;
case 'リマインド':
let options2 = makeOption(event, "ごめん!その機能は今実装中だから待って!");
UrlFetchApp.fetch(URL, options2);
break;
case '選択肢':
choices(event, targetColumn);
break;
default:
let options4 = makeOption(event, "メニューから言葉を選んでね!");
UrlFetchApp.fetch(URL, options4);
break;
}
}
コードの解説
この2行はおまじない
let data = JSON.parse(e.postData.contents)
var event = data.events[0];
これは今回のLinebotの仕様上、登録されているユーザー識別列が必要になるので回収しているだけ
let targetColumn = getColumn(event);
event.message.text
にUserのテキストメッセージが代入されるのでそれをSwitchで分岐
switch (event.message.text) {
case '出題':
let answer = replyGame(event);
insertData(answer, targetColumn);
(以下略)
問題出題機能
まずは使っている関数をざっと
先ほど紹介した分岐処理のdoPost
case '出題':
let answer = replyGame(event);
insertData(answer, targetColumn);
break;
スプレッドシートを読み込み、単語を一つ抽出して返答を作成するreplyGame
function replyGame(event) {
var Spreadsheet = SpreadsheetApp.openById(SPREADSHEET_ID);
let sheet = Spreadsheet.getSheetByName(SpreadsheetName);
let questionNumber = generateRandomNumArray(35, 1); // (※1)
var question = sheet.getRange(questionNumber[0], 2).getValue();
var answer = sheet.getRange(questionNumber[0], 1).getValue();
let options = makeQuestionOption(event, question); // (※2)
UrlFetchApp.fetch(URL, options);
return answer
}
コードの解説
1. スプレッドシートを取得
var Spreadsheet = SpreadsheetApp.openById(SPREADSHEET_ID);
let sheet = Spreadsheet.getSheetByName(SpreadsheetName);
2. 問題の決定
generateRandomNumArray
でランダムな整数を一つ取得。今回は試験用として最大値の第1引数に35
を渡しています(1~35までの数が取得可能)。ですが、本来なら単語の総数を代入。
スプシでは、1列目が単語、2列目が単語の意味になっているので、questionNumber
行目の単語、単語の意味をそれぞれanswer
、question
に代入。
let questionNumber = generateRandomNumArray(35, 1); // (※1)
var question = sheet.getRange(questionNumber[0], 2).getValue();
var answer = sheet.getRange(questionNumber[0], 1).getValue();
(※1) ランダムな整数を配列にして返してくれるgenerateRandomNumArray
/**
ランダム整数を生成して配列に格納するクラス
引き数1:最大値
引き数2:配列に格納するランダムな数字の数
**/
function generateRandomNumArray(maxNum, generateArrayLength) {
let generateArray = []; //ランダム格納用配列
let numberArray = []; // ランダム生成用配列
//(2~maxNum)ランダム生成用配列を作成
for (let i = 2; i <= maxNum; i++) {
numberArray[i - 2] = i;
}
//ランダム格納用配列にランダム整数を格納
for (let j = 0, len = numberArray.length; j < generateArrayLength; j++, len--) {
let rndNum = Math.floor(Math.random() * len);
generateArray.push(numberArray[rndNum]);
numberArray[rndNum] = numberArray[len - 1];
}
return generateArray;
}
3. 返答の作成
返答用のoptions
を作成して2行目で返答
let options = makeQuestionOption(event, question); // (※2)
UrlFetchApp.fetch(URL, options);
(※2) 返答用のFlexMessageを作成するmakeQuestionOption
//問題用のFlexMessageOption作成
function makeQuestionOption(event, message) {
HEADERS = {
"Content-Type": "application/json; charset=UTF-8",
"Authorization": "Bearer " + ACCESSTOKEN,
}
postData = {
"replyToken": event.replyToken,
"messages" : [
{
'type':'flex',
'altText':'問題',
'contents':
{
"type": "bubble",
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "問題",
"weight": "bold",
"color": "#4F6F52",
"size": "sm",
"align": "center"
},
{
"type": "separator",
"color": "#4F6F52",
},
{
"type": "text",
"text": message,
"weight": "bold",
"size": "lg",
"margin": "md",
"wrap": true
}
],
"backgroundColor": '#E8DFCA'
},
"styles": {
"footer": {
"separator": true
}
}
}
}
]
}
let options = {
"headers": HEADERS,
"method": "post",
"payload": JSON.stringify(postData),
};
return options;
}
"type": "box"
、"layout": "vertical"
で垂直レイアウト方向を定義。
"type": "text"
でテキストコンポーネントを作成
"type": "separator"
で分割線を作成
wrap
プロパティをtrue
にすることでテキストを折り返し表示指定
FlexMessageのコンポーネントについてより知りたい方はこちら。
FlexMessageのレイアウトについてより知りたい方はこちら。
4. 回答履歴の保存
insertData
によって出題した単語(answer
)をスプシに保存。(これは回答を出力するときに使用します)
具体的には、管理用スプシにおける解答Userの列を引数であるcolumn
で特定済みなので、あとはその最終行targetRow
にanswer
を書き込む
//管理用シートの最終行に回答状況を登録する
function insertData(value = '空', column) {
var Spreadsheet = SpreadsheetApp.openById(SPREADSHEET_ID);
let sheet = Spreadsheet.getSheetByName(ManegerSpreadsheetName);
const targetRow = sheet.getRange(1, column).getNextDataCell(SpreadsheetApp.Direction.DOWN).getRow() + 1;
sheet.getRange(targetRow, column).setValue(value);
}
次回に続く
参考サイト