##はじめに
前回の楽天市場のAPIを使って買えるマスクを提案してくれるLINEBotを作ってみた - Qiitaをブラッシュアップしました。
##概要
前回できたものは、
LINEBotにほしいもの、「マスク」と入力したら
楽天市場から「マスク」を検索して、絞り込み検索で、購入可能、最安価でソート、最低金額○円以上、最高金額○円以下の商品を商品画像付きで3商品くらい返してくれるBot
結果として出来上がったものの、楽天の商品名ってSEO対策のせいでやたらと長くてかなりエリアを取る、3つ出したのにとても見比べられず実用性が低かったのでリッチメニューを使って使いやすくしようと思いました。
作りたいもの
マスクにセグメントされた楽天市場の商品を3つ引っ張ってきて
- カルーセルパネルで開く
- 複数検索
- 商品名を3行くらいに収める
- 金額かレビューを出す
- (マスクは)怪しいショップが多かったので店舗名を出す
- ngrokで動かしているので何かにデプロイする
作れなかったところ
- ○ → カルーセルパネルで開く
- ○ → 複数検索してるっぽいみため
- ○ → 商品名を3行くらいに収める
- ○ → 金額かレビューを出す
- ○ → (マスクは)怪しいショップが多かったので店舗名を出す
- × → ngrokで動かしているのでHerokuにデプロイする
###環境
Node.js v13.7.0
MacBook Pro macOS Mojave
Visual Studio Code v1.43.1
###できたもの
買えるマスクLINEBot♡
— 3yaka (@3yaka4) April 1, 2020
リッチメニュー使った♡カルーセルしたった!
それにしてもマスクの値段、いつまでこんなに高いのしかないんだろう。。
家庭に2枚届いてもな。。。#protoout pic.twitter.com/M1vEMeJyp2
「マスクある?」と質問することで、楽天市場の中で在庫あり商品、最安価の商品名と商品URLと商品画像のあるマスクをカルーセルパネルを使って横スクロールで商品を提案してくれます。
カルーセルパネルに入れる情報
楽天市場API↓を使って
https://app.rakuten.co.jp/services/api/IchibaItem/Search/20170706?format=json&rakutenAPiurl&keyword=mask&genreId=501143&availability=1&sort=%2BitemPrice&minPrice=1000&maxPrice=4000&carrier=2&hits=3&applicationId=***
//APIが深いので前半をまとめる
const item = items[i].Item;
//商品画像(中サイズ)
const itemimg = `${item.mediumImageUrls[0].imageUrl}`;
//商品URL
const producturl = `${item.itemUrl}`;
//商品名
const productlabellg = `${item.itemName}`;
//商品名を54文字以下にして...をつける
const productlabel = productlabellg.slice(0, 54) + "...";
//商品金額
const productprice = `¥${item.itemPrice}`;
//レビュー平均値
const producavg = `${item.reviewAverage}`;
//ショップ名
const productshop = `${item.shopName}`;
// LINEに入力した文字
const own = `${ownword}`;
let columns = [];
let carousel =
{
"thumbnailImageUrl": itemimg,
"imageBackgroundColor": "#FFFFFF",
"title": own,
"text": productlabel,
"defaultAction": {
"type": "uri",
"label": "View detail",
"uri": producturl
},
"actions": [
{
"type": "message",
"label": productprice,
"text": productprice
},
{
"type": "uri",
"label": productshop,
"uri": producturl
}
]
};
columns.push(carousel);
}
これを3つ分作りたいのでFor文の中に入れちゃいます。
全コード(ngrok)
'use strict';
const express = require('express');
const line = require('@line/bot-sdk');
const axios = require('axios');
const PORT = process.env.PORT || 3000;
const rakutenAPiurl = "https://app.rakuten.co.jp/services/api/IchibaItem/Search/20170706?format=json";
const rakutenAppId = '***';
const guidecat = 'https://i.gyazo.com/97840790b257952c89c59e7c176e114c.png';
const config = {
channelSecret: '***',
channelAccessToken: '***'
};
const app = express();
app.post('/webhook', line.middleware(config), (req, res) => {
console.log(req.body.events);
Promise
.all(req.body.events.map(handleEvent))
.then((result) => res.json(result));
});
const client = new line.Client(config);
function handleEvent(event) {
if (event.type !== 'message' || event.message.type !== 'text') {
return Promise.resolve(null);
}
let mes = event.message.text;
if (mes.indexOf('?') > -1) {
getNodeVer(event.source.userId, mes);
return client.replyMessage(event.replyToken, [{
type: 'image',
originalContentUrl: guidecat,
previewImageUrl: guidecat
},
{ type: "text", text: '在庫があって安いのはこれだよ〜' }]);
} else {
// mes = event.message.text;
return client.replyMessage(event.replyToken, [{
type: 'image',
originalContentUrl: guidecat,
previewImageUrl: guidecat
},
{ type: "text", text: '〇〇ある?って聞いてほしいな' }]);
}
}
const getNodeVer = async (userId, mes) => {
let ownword = mes.split('ある?');
console.log('mes->' + mes + 'ownword->' + ownword);
const res = await axios.get(rakutenAPiurl + '&keyword=' + encodeURIComponent(ownword) + '&genreId=501143&availability=1&sort=%2BitemPrice&minPrice=1000&maxPrice=4000&carrier=2&hits=3&applicationId=' + rakutenAppId);
const items = res.data.Items;
let columns = [];
for (let i = 0, len = items.length; i < len; i++) {
const item = items[i].Item;
const itemimg = `${item.mediumImageUrls[0].imageUrl}`;
const producturl = `${item.itemUrl}`;
const productlabellg = `${item.itemName}`;
const productlabel = productlabellg.slice(0, 54) + "...";
const productprice = `¥${item.itemPrice}`;
const producavg = `${item.reviewAverage}`;
const productshop = `${item.shopName}`;
const own = `${ownword}`;
let carousel =
{
"thumbnailImageUrl": itemimg,
"imageBackgroundColor": "#FFFFFF",
"title": own,
"text": productlabel,
"defaultAction": {
"type": "uri",
"label": "View detail",
"uri": producturl
},
"actions": [
{
"type": "message",
"label": productprice,
"text": productprice
},
{
"type": "uri",
"label": productshop,
"uri": producturl
}
]
};
columns.push(carousel);
}
await client.pushMessage(userId, {
"type": "template",
"altText": "this is a carousel template",
"template": {
"type": "carousel",
"columns": columns,
"imageAspectRatio": "rectangle",
"imageSize": "cover"
}
});
}
app.listen(PORT);
console.log(`Server running at ${PORT}`);
このコードをngrokで動かすと、動きます!
しかしこれをHerokuに持っていくと失敗。。。
課題なのでタイムオーバー。。。
Herokuにはdeployできてるのになぁ。。。
何が起こっているのかな
Herokuを有料にしないといけないとかmにつけたのですが、Heroku側のSSL認証問題なのでしょうか。
[参考URL]
Heroku SSL
https://devcenter.heroku.com/articles/ssl
###now
Herokuは諦めてnowで試してみた
1時間でLINE BOTを作るハンズオンをベースにしたけどうまくいかなかったのでProtoOutの校長先生がさらに書いていただいて、
LINE BOTで天気を返すサンプルがngrokで動いてnowで動かない件を参考に書き換えたら動きましたー!!!!
'use strict';
const express = require('express');
const line = require('@line/bot-sdk');
const axios = require('axios');
const PORT = process.env.PORT || 3000;
const rakutenAPiurl = "https://app.rakuten.co.jp/services/api/IchibaItem/Search/20170706?format=json";
const rakutenAppId = '***';
const guidecat = 'https://i.gyazo.com/97840790b257952c89c59e7c176e114c.png';
const config = {
channelSecret: '***',
channelAccessToken: '***'
};
const app = express();
app.post('/webhook', line.middleware(config), (req, res) => {
console.log(req.body.events);
Promise
.all(req.body.events.map(handleEvent))
.then((result) => res.json(result));
});
const client = new line.Client(config);
async function handleEvent(event) {
if (event.type !== 'message' || event.message.type !== 'text') {
return Promise.resolve(null);
}
let mes = event.message.text;
let linemes = mes.indexOf('ある?');
if ( linemes < 0) {
return client.replyMessage(event.replyToken, [{
type: 'image',
originalContentUrl: guidecat,
previewImageUrl: guidecat
},
{ type: "text", text: '〇〇ある?って聞いてほしいな' }]);
}
await client.replyMessage(event.replyToken, [{
type: 'image',
originalContentUrl: guidecat,
previewImageUrl: guidecat
},
{
type: "text", text: '在庫があって安いのはこれだよ〜' }]);
return getRakuten(event.source.userId, mes);
}
const getRakuten = async (userId, mes) => {
let ownword = mes.split('ある?');
console.log('mes->' + mes + 'ownword->' + ownword);
const res = await axios.get(rakutenAPiurl + '&keyword=' + encodeURIComponent(ownword) + '&genreId=501143&availability=1&sort=%2BitemPrice&minPrice=1000&maxPrice=4000&carrier=2&hits=3&applicationId=' + rakutenAppId);
const items = res.data.Items;
let columns = [];
for (let i = 0, len = items.length; i < len; i++) {
const item = items[i].Item;
const msg = `${item.itemName}\n ${item.itemUrl}\n¥${item.itemPrice}`;
const itemimg = `${item.mediumImageUrls[0].imageUrl}`;
const producturl = `${item.itemUrl}`;
const productlabellg = `${item.itemName}`;
const productlabel = productlabellg.slice(0, 54) + "...";
const productprice = `¥${item.itemPrice}`;
const producavg = `${item.reviewAverage}`;
const productshop = `${item.shopName}`;
const own = `${ownword}`;
let carousel =
{
"thumbnailImageUrl": itemimg,
"imageBackgroundColor": "#FFFFFF",
"title": own,
"text": productlabel,
"defaultAction": {
"type": "uri",
"label": "View detail",
"uri": producturl
},
"actions": [
{
"type": "message",
"label": productprice,
"text": productprice
},
{
"type": "uri",
"label": productshop,
"uri": producturl
}
]
};
columns.push(carousel);
}//for
return client.pushMessage(userId, {
"type": "template",
"altText": "this is a carousel template",
"template": {
"type": "carousel",
"columns": columns,
"imageAspectRatio": "rectangle",
"imageSize": "cover"
}
});
}
(process.env.NOW_REGION) ? module.exports = app : app.listen(PORT);
console.log(`Server running at ${PORT}`);
感想
やり進めていくとFlex Messageの存在を知りました。。。こっちの方がやりたかった。。。
次はこれをやろう。
Deployはやっぱりハマってしまった。
頑張ろうー。
参考サイト
LINE Messaging APIのテンプレートメッセージをまとめてみる | Developers.IO
LINEのBot開発 超入門(前編) ゼロから応答ができるまで - Qiita