作ったもの
機能
以下のメッセージに反応して,百人一首の歌・番号・作者名・決まり字を返します。
- 歌番号
- 決まり字
- 上の句の5文字目くらいまで(一部でも可)
- 作者名
- 取り札の上3文字
作った経緯
趣味で競技かるたをしており、百人一首に触れる機会が多いです。
「ちはやぶる〜の歌の作者って誰だっけ?」「歌番号40の歌って何だっけ?」
そんなときすぐ調べられたら便利だと思い,最近勉強していたGASの練習も兼ねて作ってみました。
準備
サーバーを用意する必要がなく,ブラウザ上で簡単に作れそうなので,LINE BotとGoogle Apps Script(GAS)を組み合わせて作ります。
LINE Developersに登録
登録は前にしていたので,省略します。
以下のリンクが参考になります。
参考:LINEのBot開発 超入門(前編) ゼロから応答ができるまで
「Messaging API設定」の下にある,チャンネルアクセストークン(長期)を取得しておきます。
Google Apps Scriptを開く
歌のデータベース代わりにスプレッドシートを使います。
Googleドライブにログインして,Googleスプレッドシートを選択。
スプレッドシートから「ツール」→「スクリプト エディタ」を選択。
コードを書く
言語はJavaScriptがベースになっています。
メッセージを受け取って返信する関数
メッセージを受け取り,返信メッセージを作り,送信する doPost 関数を書きます。
チャンネルアクセストークンを,ここにアクセストークンを入れます
の部分にコピペします。
返信メッセージを作成する部分以外は,以下のリンク先のコードを参考にさせていただきました。
参考:Google Apps ScriptでLINE BOTつくったら30分で動かせた件
// アクセストークン
const ACCESS_TOKEN = 'ここにアクセストークンを入れます';
function doPost(e) {
// WebHookで受信した応答用Token
const replyToken = JSON.parse(e.postData.contents).events[0].replyToken;
// ユーザーのメッセージを取得
const userMsg = JSON.parse(e.postData.contents).events[0].message.text;
// 応答メッセージ用のAPI URL
const url = 'https://api.line.me/v2/bot/message/reply';
//返信メッセージを作成
if (1 <= parseInt(userMsg) && parseInt(userMsg) <= 100) {
var post_msg = numToMessage(parseInt(userMsg));
} else {
var post_msg = wordToMessage(userMsg);
}
//変数post_msg を本文として返信を送る
UrlFetchApp.fetch(url, {
'headers': {
'Content-Type': 'application/json; charset=UTF-8',
'Authorization': 'Bearer ' + ACCESS_TOKEN,
},
'method': 'post',
'payload': JSON.stringify({
'replyToken': replyToken,
'messages': [{
'type': 'text',
'text': post_msg ,
}],
}),
});
}
歌番号から返信メッセージを作成する関数
受信メッセージが1から100までの数値の場合は,以下の処理を行います。
- 歌番号から該当する歌の情報を取得する
- 返信メッセージを返す
そのための numToMessage 関数を用意します。
予め,以下のようなシートを作っておきます。
(シート名は「tanka_list」としました)
【追記】閲覧用リンクを貼っておきます。
numToMessage 関数のコードを書きます。
//1から100までの歌番号から,メッセージを作成
function numToMessage(msg) {
//tanka_listシートを取得
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('tanka_list');
//歌番号からシートの行番号を取得
const row = get_row(msg, sheet, 'A');
//歌番号,上の句,下の句,詠み人,決まり字を取得
const num = sheet.getRange('A'+ row).getValue();
const kami = sheet.getRange('B'+ row).getValue();
const shimo = sheet.getRange('C'+ row).getValue();
const yomibito = sheet.getRange('D'+ row).getValue();
const kimariji = sheet.getRange('E'+ row).getValue();
//メッセージを作成
return kami + '\n' + shimo +'\n\n歌番号:' + num + '\n詠み人:' + yomibito + '\n決まり字:' + kimariji;
}
コード内で出てくる get_row 関数は,以下の記事のものを使わせていただきました。
参考:GASで列内で特定の値に一致する行番号を取得する - shikaku's blog
//keyに一致する値をもつ行番号を返す
function get_row(key, sh, col){
const array = get_array(sh, col);
const row = array.indexOf(key) + 1;
return row;
}
//シートの1列を全て配列に入れる
function get_array(sh, col){
const last_row = sh.getLastRow();
const range = sh.getRange(col + '1:' + col + last_row)
const values = range.getValues();
const array = [];
for(let i = 0; i < values.length; i++){
array.push(values[i][0]);
}
return array;
}
(例) 受信メッセージが40の場合,以下のようなメッセージを返り値として返します
しのぶれど 色に出でにけり 我が恋は
物や思ふと 人の問ふまで歌番号:40
詠み人:平兼盛
決まり字:しの
決まり字や作者名から歌番号を取得する
受信メッセージが歌番号の数字以外の場合には,2つの場合が考えられます。
- 歌が特定できる場合(決まり字や歌人名)は,まず歌番号を取得し,numToMessage 関数に送る
- 歌が特定できない場合は,候補が複数あるのか,全くないのかの判定をする
そのための wordToMessage 関数を用意します。
予め,以下のようなシートを作っておきます。
(シート名は「word_list」としました)
仮名遣いによる表記揺れや作者の別名(wiki調べ)などに対応するため,800行くらい用意しました。
(本当は正規表現とかでもっと行数減らせるかもしれないですが,未確定のときにも反応してしまいそうなのでやめました)
wordToMessage 関数のコードを書きます。
//決まり字や上の句,歌人名から,メッセージを作成
function wordToMessage(msg) {
//word_listシートを取得
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('word_list');
//メッセージからシートの行番号を取得
const row = get_row(msg, sheet, 'A');
//メッセージを作成
if (row === 0) {
return partToMessage(msg);
} else {
const num = sheet.getRange('B'+ row).getValue();
return numToMessage(num);
};
}
もしここでも一致しない場合には,後述する partToMessage 関数で候補を調べます。
候補が複数あるときのメッセージを作成する
受信メッセージから歌番号が確定できない場合は,以下の2通りの場合が考えられます。
- 歌の候補が複数ある場合には,その候補を返す
- ない場合には,
'見つかりませんでした…'
を返す。
そのための partToMessage 関数を用意します。
予め,以下のようなシートを作っておきます。
(シート名は「part_list」としました)
(上の句の出だしだけでなく,「六歌仙」なども入れてあります)
partToMessage 関数のコードを書きます。
行ごとに最終列の位置が異なるため,以下のようにコードを書いています。
- get_row 関数で該当する行番号を見つける
- 『「ひ」は3枚あります』のような文字列 kouho を作る
- 該当行の最終列の値を取得し,定数 lastCol に代入
参考:もりさんのプログラミング手帳 - for文で3列目から最終列までの値を kouho の後ろに加えていく
- kouho の値を返す
//確定できない場合に,候補の札を示すメッセージを作成
function partToMessage(msg) {
//part_listシートを取得
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('part_list');
//メッセージからシートの行番号を取得
const row = get_row(msg, sheet, 'A');
//メッセージを作成
if (row === 0) {
return '見つかりませんでした…';
} else {
const num = sheet.getRange('B'+ row).getValue();
let kouho = '「' + msg + '」は,' + num + '枚あります。';
const lastCol = sheet.getRange(row, 3).getNextDataCell(SpreadsheetApp.Direction.NEXT).getColumn();
for (i = 3; i <= lastCol; i++) {
let fuda = sheet.getRange(row, i).getValue();
kouho = kouho.concat('\n' + fuda);
}
return kouho;
};
}
アプリを公開する
公開して,LINE Botとして使えるようにするには,デプロイして利用できる状態にしたあと,LINE DevelopersでWebhookの設定をする必要があります。
デプロイする
種類を「ウェブアプリ」に,アクセスできるユーザーを「全員」に設定してから「デプロイ」します。
(初回は,アクセス権の承認画面が出てきます)
Webhookの設定をする
LINE DevelopersのMessaging API設定に,Webhook設定という項目があります。
編集をクリックして,先ほどのウェブアプリのURLを貼り付けます。
また,Webhookの利用をオンにしておきます。
検証をクリックし「成功」と表示されれば正しく設定できています。
あとは実際にLINEで友達登録して返信できるかテストするだけです。
今後やりたいことなど
初めてBotを作ってみましたが,非常に楽しかったです。
制作に10時間ほどかかりましたが,時間が経つのを忘れて作ってしまいました。
画像を送ったり,これから百人一首を覚えたい人に向けてクイズを出すなど,文章を返信する以外の機能を加えたBotなども作れたら面白いなと思っています。
追記:画像送信するものも作りました。
ゴロ合わせの覚え方をイラストとともに紹介します。