6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

家庭内フードロスを減らそう。LINEbotを使ったレシピの提案

Last updated at Posted at 2020-05-05

夏が近づき暖かくなってきました。
食品も傷みやすい季節になってきましたね!
また、在宅ワークも進み家でのお昼ご飯などのレシピにも困っているのではないでしょうか?

ということで、今回は家庭内でのフードロスが減るよう、余っている食材をメッセージするとレシピのリンクを返してくれるボットを作って見ました。

1 材料に関連したレシピがある場合:

2 材料に関連したレシピがない場合(ランダムで料理のカテゴリを提案します):

※ カルーセルやボタンテンプレートのサムネはprotoout studioさんの可愛いアイコンをかりました。

では説明していきます。

前提

基本的なLINEbotの作り方はここではあまり詳しく書かないので、初めての方は下記ページを参考にしてみてください。
1時間でLINE BOTを作るハンズオン (資料+レポート) in Node学園祭2017 #nodefest

楽天レシピ系API

今回、レシピに関する情報は楽天レシピカテゴリーAPI楽天レシピカテゴリ別ランキングAPIを利用しました。
これらのAPIはドキュメントにあるように、基本的に料理のカテゴリーや、特定のカテゴリーでの上位4位までにランクインされているレシピを返してくれます。

準備

楽天レシピ系のAPIを利用するにはアプリIDの発行が必要なので、まず発行します。
image.png

発行が完了すると、APIを使うときに必要なアプリID/デベロッパーIDなどの情報が表示されるのでそれはメモしておきます。

ソースコード

早速ですが、ソースコードです。

recipe.js
'use strict';

const express = require('express');
const line = require('@line/bot-sdk');
const PORT = process.env.PORT || 3000;
// 追加
const axios = require('axios');

const config = {
    channelSecret: 'xxxxxx',
    channelAccessToken: 'xxxxxx'
};

const app = express();

app.get('/', (req, res) => res.send('Hello LINE BOT!(GET)')); //ブラウザ確認用(無くても問題ない)
app.post('/webhook', line.middleware(config), (req, res) => {
    // LINE上で入力された
    console.log(req.body.events[0].message.text);
    
    //ここのif文はdeveloper consoleの"接続確認"用なので後で削除して問題ないです。
    if(req.body.events[0].replyToken === '00000000000000000000000000000000' && req.body.events[1].replyToken === 'ffffffffffffffffffffffffffffffff'){
        res.send('Hello LINE BOT!(POST)');
        console.log('疎通確認用');
        return; 
    }
    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 = ''

    var result = event.message.text;
    mes = result + 'を使ったレシピ'; //wikiのurlの前の一言
    await client.replyMessage(event.replyToken, {
      type: 'text',
      // text: event.message.text //実際に返信の言葉を入れる箇所
      text : mes 
    });
    return getMenu(event.source.userId,result);
 
} //handleevent 


const getMenu = async (userId,food) =>{

    const res = await axios.get('https://app.rakuten.co.jp/services/api/Recipe/CategoryList/20170426?format=json&formatVersion=2&categoryType=small&applicationId='+ {楽天のアプリID/デベロッパーID});

    var name = [];
    var catgid = [];
    var url = [];

    var len = res.data.result.small.length;
    var a = 0;

    for(var i = 0; i < len ; i++){
        if(res.data.result.small[i].categoryName.indexOf(food) > -1 ){
            name[a] = res.data.result.small[i].categoryName;
            catgid[a] = res.data.result.small[i].parentCategoryId;
            url[a] = res.data.result.small[i].categoryUrl;
            a = a + 1;
        }
    }

    console.log(name);
    console.log(catgid);
    console.log(url);

    let col= [];
    
    // 何も引っかからなかった時の処理
    if(name.length <= 0){
        // とりあえず返事する
        await client.pushMessage(userId, {
            type: 'text',
            text: 'ごめん、その食材の使い方は知らない。代わりのおすすめメニュー↓↓',
        });

        // ランダムでこの中の物を返すためのリスト
        let category = [
            {
                categoryName: "人気メニュー",
                categoryId: "30",
                categoryUrl: "https://recipe.rakuten.co.jp/category/30/"
            },
            {
                categoryName: "定番の肉料理",
                categoryId: "31",
                categoryUrl: "https://recipe.rakuten.co.jp/category/31/"
            },
            {
                categoryName: "定番の魚料理",
                categoryId: "32",
                categoryUrl: "https://recipe.rakuten.co.jp/category/32/"
            },
            {
                categoryName: "卵料理",
                categoryId: "33",
                categoryUrl: "https://recipe.rakuten.co.jp/category/33/"
            },
            {
                categoryName: "パスタ",
                categoryId: "15",
                categoryUrl: "https://recipe.rakuten.co.jp/category/15/"
            },
            {
                categoryName: "鍋料理",
                categoryId: "23",
                categoryUrl: "https://recipe.rakuten.co.jp/category/23/"
            },
            {
                categoryName: "簡単料理・時短",
                categoryId: "36",
                categoryUrl: "https://recipe.rakuten.co.jp/category/36/"
            },
            {
                categoryName: "節約料理",
                categoryId: "37",
                categoryUrl: "https://recipe.rakuten.co.jp/category/37/"
            },
            {
                categoryName: "今日の献立",
                categoryId: "38",
                categoryUrl: "https://recipe.rakuten.co.jp/category/38/"
            },
            {
                categoryName: "健康料理",
                categoryId: "39",
                categoryUrl: "https://recipe.rakuten.co.jp/category/39/"
            },
        ]

        var len = category.length;
        var rand = Math.floor(Math.random()*(len+1));

        // 毎度ランダムに変わるようにする
        var categoryName = category[rand].categoryName + 'のレシピだよ!';
        var url = category[rand].categoryUrl;
    
        // ボタンテンプレート
        return client.pushMessage(userId,{
            
                "type": "template",
                "altText": "This is a buttons template",
                "template": {
                    "type": "buttons",
                    "thumbnailImageUrl": "{サムネにしたい画像のURL}",
                    "imageAspectRatio": "rectangle",
                    "imageSize": "cover",
                    "imageBackgroundColor": "#FFFFFF",
                    "title": "おすすめMenu",
                    "text": categoryName,
                    "defaultAction": {
                        "type": "uri",
                        "label": "View detail",
                        "uri": url
                    },
                    "actions": [
                        {
                          "type": "uri",
                          "label": "レシピへGO",
                          "uri": url
                        }
                    ]
                }
        });
    
     
    }else{
    
    var len2 = name.length;
    if(len2 > 10){len2 = 10;} // カルーセルの上限が10個なので10以上ある場合は10にする

    // "columns" の中身を作っておく  
    for(var k = 0 ; k < len2 ; k++){
        const title = name[k] ;
        const text = name[k] + 'のレシピを紹介するよ!';
        const uri = url[k] ;

        let carousel =
        {
          "thumbnailImageUrl": "{サムネにしたい画像のURL}",
          "imageBackgroundColor": "#FFFFFF",
          "title": title,
          "text": text,
          "defaultAction": {
            "type": "uri",
            "label": "View detail",
            "uri": uri
          },
          "actions": [

            {
              "type": "uri",
              "label": "レシピへGO",
              "uri": uri
            }
          ]
        };
        col.push(carousel);

    }

    return client.pushMessage(userId, {
        "type": "template",
        "altText": "this is a carousel template",
        "template": {
          "type": "carousel",
          "columns": col,
          "imageAspectRatio": "rectangle",
          "imageSize": "cover"
        }
      });

    }
}


(process.env.NOW_REGION) ? module.exports = app : app.listen(PORT);
console.log(`Server running at ${PORT}`);

説明

本当は材料をメッセージしたらその材料を使った料理を返すようにしたかったのですが、それは楽天レシピAPIをそのまま使うだけではできなかったので、まず使ったAPIは/Recipe/CategoryListのほうをcategoryType=smallで取ることで、入力した値をヒットしやすくなります。

入力値がAPIで返ってきた値とヒットした場合は、それを配列に入れてカルーセルで出すための準備をします。
もし、「ゴメス」のようなヒットしない値だった場合は、categoryType=largeの分類のボタンテンプレートでランダムに出すようにしています。

LINEのカルーセルやボタンテンプレートなどに関しては下記が参考になります。
■楽天のAPIを使ってマスクを買えるBotを作ってみた[LINEBotリッチメニュー]
■LINEの公式ドキュメント

作ってみて

厳密に食材に対してのレシピを返してくれるわけじゃないけど、まあまあ使えると思います。
ぜひお試しください!

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?