#目的
LINEBotを使って、対話でうまくブルワリーの情報を探したい!
なぜLINEか?
⇒___老若男女問わず使えるインターフェースですから!___
なぜブルワリーか?
⇒お酒が、、好きなんです。
#前提
記事を読まれる前に、、、
まだまだやりたいことの最初の慣れの部分なので、
えっ?なんで?と思うことは多いかと思います。
#今回やったこと
ブルワリー検索APIの「Open Brewery DB」と「LINE MessagingAPI」を使って、
LINEのトーク画面からニューヨークのブルワリーを検索して、一覧をLINEのレスポンスとして返す。
URL付きでページにとんだり、電話をかけやすくするなど。
#やってみてわかったこと
★流れが何となく見えた。
ユーザがLINEから何かメッセージをうつ。
→LINEサーバにまず送られる。
→LINEのサーバからWebhookでイベントがとんでくる。
→イベントの何を取るのか、チェックしつつ、来た内容に対してどう返すかをプログラムで作る。
リクエストのイベントが来るところの受け方は、
詳しくはLINEのドキュメントをもっと見たほうが良いと思った。
https://developers.line.biz/ja/reference/messaging-api/
#ソース
'use strict'; // おまじない
// ########################################
// 初期設定など
// ########################################
// パッケージを使用します
const express = require('express');
const line = require('@line/bot-sdk');
const axios = require('axios');
const fs = require('fs');
// ローカル(自分のPC)でサーバーを公開するときのポート番号です
const PORT = process.env.PORT || 3000;
// Messaging APIで利用するクレデンシャル(秘匿情報)です。
const credential = JSON.parse(fs.readFileSync('./credential/credential.json', 'utf8'));
const config = {
channelSecret: credential.channelSecret,
channelAccessToken: credential.channelAccessToken
};
// ユーザのメッセージに対してどう返すかを定義
const sampleFunction = async (event) => {
// ユーザーメッセージが想定しているものかどうか
if (check_params(event.message.text)) {
return client.replyMessage(event.replyToken, {
type: 'text',
text: 'ニューヨークのブルワリーは?と話しかけてね'
});
} else {
// 「リプライ」を使って先に返事しておきます
await client.replyMessage(event.replyToken, {
type: 'text',
text: '調べています……'
});
let pushText = '';
try {
// axiosでニューヨークブルワリーリストのAPIを叩きます(少し時間がかかる・ブロッキングする)
const res = await axios.get('https://api.openbrewerydb.org/breweries?by_state=new_york');
console.log(res.data);
const brewery_data = res.data;
for (let j =0 ; j<brewery_data.length ; j++) {
const b_name = brewery_data[j].name;
const b_url = brewery_data[j].website_url;
console.log(b_name);
if(j === 0){
pushText = 'ここ!\r\n';
}else{
pushText = pushText + '店名 ' + b_name + '\r\n' + 'URL ' + b_url + '\r\n\r\n';
}
}
} catch (error) {
pushText = '検索中にエラーが発生しました。ごめんね。';
// APIからエラーが返ってきたらターミナルに表示する
console.error(error);
}
// 「プッシュ」で後からユーザーに通知します
return client.pushMessage(event.source.userId, {
type: 'text',
text: pushText,
});
}
};
function check_params(event_text) {
if(event_text === 'ニューヨークのブルワリーは?'){
return false;
}else {
return true;
}
}
// ########################################
// LINEサーバーからのWebhookデータを処理する部分
// ########################################
// LINE SDKを初期化します
const client = new line.Client(config);
// LINEサーバーからWebhookがあると「サーバー部分」から以下の "handleEvent" という関数が呼び出されます
async function handleEvent(event) {
// 受信したWebhookが「テキストメッセージ以外」であればnullを返すことで無視します
if (event.type !== 'message' || event.message.type !== 'text') {
return Promise.resolve(null);
}
// サンプル関数を実行します
return sampleFunction(event);
}
// ########################################
// Expressによるサーバー部分
// ########################################
// expressを初期化します
const app = express();
// HTTP POSTによって '/webhook' のパスにアクセスがあったら、POSTされた内容に応じて様々な処理をします
app.post('/webhook', line.middleware(config), (req, res) => {
// Webhookの中身を確認用にターミナルに表示します
console.log(req.body.events);
// 検証ボタンをクリックしたときに飛んできたWebhookを受信したときのみ以下のif文内を実行
if (req.body.events[0].replyToken === '00000000000000000000000000000000' && req.body.events[1].replyToken === 'ffffffffffffffffffffffffffffffff') {
res.send('Hello LINE BOT! (HTTP POST)'); // LINEサーバーに返答します
console.log('検証イベントを受信しました!'); // ターミナルに表示します
return; // これより下は実行されません
}
// あらかじめ宣言しておいた "handleEvent" 関数にWebhookの中身を渡して処理してもらい、
// 関数から戻ってきたデータをそのままLINEサーバーに「レスポンス」として返します
Promise.all(req.body.events.map(handleEvent)).then((result) => res.json(result));
});
// 最初に決めたポート番号でサーバーをPC内だけに公開します
// (環境によってはローカルネットワーク内にも公開されます)
app.listen(PORT);
console.log(`ポート${PORT}番でExpressサーバーを実行中です…`);
#環境
Node.js v14.9.0
Visual Studio Code v1.49.3
@line/bot-sdk
#【おまけ】本当は、やれたらやりたいこと
■技術
・ユーザから送られたメッセージのチェック(関数切り出し)
・LINEに表示された際に、URLから取得されたサイトのサムネイル?がMAX5件ぐらいなので、
取得も5件ぐらいの方が見やすそう。
・認証情報は、見えそうなファイルよりも、もっと隠れたところから取得したほうが良いと思う。
・リッチメニューを使ってそもそもどんな操作をすればよいのかユーザにわかりやすくする。
(フリーテキストって何入れればええのかわからない。)
・Googleマップのリンクを付けて保存しやすくする。
■企画
・ビールも好きだけど、肉やカレーも。。。