恋人が”食べたいもの”も”レシピ”も言ってくるワガママLINE Bot

## 妻が何を食べたいか言わないからBotに言ってもらおう
在宅勤務が増え、自分が晩御飯を作る機会も増えてきた。
そしていざ何かを作ろうと妻に問いかけると、
「鶏もも肉で」「手羽先」「豚解凍してあるから」
である。
何が食べたいかメニューまで指定して欲しい！

ということで、今回はLINE Botに妻の代わりを務めてもらうとしよう。
LINE Botと楽天APIの1つである[楽天レシピカテゴリ別ランキングAPI](https://webservice.rakuten.co.jp/documentation/recipe-category-ranking)を組み合わせることにした。

## 完成作品
先に完成作品を載せておく。
パスタ好きな恋人Romaです。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">楽天API×LINE Bot<br>メニュー画面追加したよー<a href="https://twitter.com/hashtag/%E3%82%A4%E3%82%BF%E3%83%AA%E3%82%A2%E3%83%B3?src=hash&amp;ref_src=twsrc%5Etfw">#イタリアン</a><a href="https://twitter.com/hashtag/API?src=hash&amp;ref_src=twsrc%5Etfw">#API</a><a href="https://twitter.com/hashtag/protoout?src=hash&amp;ref_src=twsrc%5Etfw">#protoout</a> <a href="https://t.co/bCJFpulk2Z">pic.twitter.com/bCJFpulk2Z</a></p>&mdash; long66tail (@ySuljbjE7vLmUov) <a href="https://twitter.com/ySuljbjE7vLmUov/status/1514922402102521861?ref_src=twsrc%5Etfw">April 15, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## 環境等
<dt>環境</dt>
<dd>Heroku環境上でNode-REDを使用</dd>
<dt>API</dt>
<dd>LINEmessagingAPI</dd>
<dd>楽天レシピカテゴリ別ランキングAPI</dd>
<details><summary>楽天レシピカテゴリ別ランキングAPIの概略</summary>

[楽天レシピ](https://recipe.rakuten.co.jp/)のカテゴリ別でのランキングを返すAPI。トップ4までのレシピを取得する。
</details>

## どんな恋人（Bot）にするか
[Noteで自己紹介している](https://note.com/long66tail/n/n0a53146fb2cd)のだが、
実は私はイタリアン料理を作るのが好きで得意である。
ということで、都合よくパスタ好きな恋人にしようと思う。
名前はRoma（由来はイタリアのローマから）にする。
アイコンはお手製のペペロンチーノだ。

パスタまたはスパゲッティが食べたいのかと聞くと、作ってほしいパスタ料理を答え、
イタリアンについて聞いてみると、作ってほしいイタリアン料理を答える。
何を食べたいかを聞くと「パスタが食べたい」と答える。
この時、料理のレシピは楽天レシピカテゴリ別ランキングAPIからランダムに取得して表示することにする。
これで食べたい料理まで指定してくるワガママBotの完成だ。
```mermaid
graph TD;
    つぶやく-->パスタ;
    つぶやく-->スパゲッティ;
    つぶやく-->イタリアン;
    つぶやく-->何を食べたいか;
    パスタ-->パスタのレシピ表示;
    スパゲッティ-->パスタのレシピ表示;
    イタリアン-->イタリアンのレシピ表示;
    何を食べたいか-->パスタが食べたい;
```
それでは、本題に入る。

## 完成フロー
フローの全体観を載せておく。
パスタかスパゲッティ、イタリアンと答えるとレシピを返す上段・中段ルートに分岐し、
それ以外だと「パスタが食べたい」と答える下段ルートに分岐する。
（何を食べたいか聞くという予定だったが、とにかく「パスタが食べたい」になっている）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2607557/0e6de0e9-2d0d-a32d-4f39-8ffccbdc0c15.png)

JSONはこちら。
~~~
[{"id":"1817ac43.9a6af4","type":"ReplyMessage","z":"6f69e0fd.984ac","name":"","replyMessage":"","x":980,"y":580,"wires":[]},{"id":"8f56be70.5aa9","type":"function","z":"6f69e0fd.984ac","name":"","func":"var min = 0 ;\nvar max = 3 ;\nvar i = Math.floor( Math.random() * (max + 1 - min) ) + min ;\n\n//const result = msg.payload;\nconst result = '気分的には「' + msg.payload.result[i].recipeTitle + '」かな。作り方はここに載ってるから作って♡' + msg.payload.result[i].recipeUrl;\n// 返信メッセージをhttp requestの結果にする\nmsg.payload  = result;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":780,"y":440,"wires":[["1817ac43.9a6af4"]]},{"id":"39551b5c.ef30c4","type":"http request","z":"6f69e0fd.984ac","name":"","method":"GET","ret":"obj","paytoqs":"ignore","url":"https://app.rakuten.co.jp/services/api/Recipe/CategoryRanking/20170426?applicationId=1051058085654770771&categoryId={{{categoryId}}}","tls":"","persist":false,"proxy":"","authType":"","x":610,"y":440,"wires":[["8f56be70.5aa9"]]},{"id":"185e2f5d.a34491","type":"function","z":"6f69e0fd.984ac","name":"","func":"// 配列\nvar arr = [\"15-687\",\"15-137\",\"15-676\",\"15-681\",\"15-369\",\"15-677\",\"15-683\",\"15-682\",\"15-678\",\"15-679\",\"15-684\",\"15-680\",\"15-138\",\"15-139\",\"15-140\",\"15-141\",\"15-142\",\"15-685\",\"15-686\",\"15-143\",\"15-145\",\"15-146\",\"15-144\",\"15-147\",\"15-151\",\"15-382\"] ;\n\n// 配列からランダムで値を選択\nvar id = arr[ Math.floor( Math.random() * arr.length ) ] ;\n\nmsg.categoryId = id;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":440,"y":440,"wires":[["39551b5c.ef30c4"]]},{"id":"3efb5240.a1f21e","type":"switch","z":"6f69e0fd.984ac","name":"","property":"payload","propertyType":"msg","rules":[{"t":"cont","v":"パスタ","vt":"str"},{"t":"cont","v":"スパゲ","vt":"str"},{"t":"cont","v":"イタリア","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":4,"x":270,"y":500,"wires":[["185e2f5d.a34491"],["185e2f5d.a34491"],["ad037ab7.9b31d8"],["3e31ec31.ead224"]]},{"id":"3e31ec31.ead224","type":"change","z":"6f69e0fd.984ac","name":"","rules":[{"t":"delete","p":"payload","pt":"msg"},{"t":"set","p":"payload","pt":"msg","to":"パスタが食べたいな・・・","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":460,"y":580,"wires":[["1817ac43.9a6af4"]]},{"id":"2b47ae57.eed8b2","type":"Webhook","z":"6f69e0fd.984ac","name":"","url":"/webhook","x":180,"y":420,"wires":[["3efb5240.a1f21e"]]},{"id":"7d0d9ad1.7a9664","type":"function","z":"6f69e0fd.984ac","name":"","func":"var min = 0 ;\nvar max = 3 ;\nvar i = Math.floor( Math.random() * (max + 1 - min) ) + min ;\n\n//const result = msg.payload;\nconst result = 'イタリアンもいいかも。たとえば「' + msg.payload.result[i].recipeTitle + '」とか。作り方はここに載ってるよ♡' + msg.payload.result[i].recipeUrl;\n// 返信メッセージをhttp requestの結果にする\nmsg.payload  = result;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":780,"y":520,"wires":[["1817ac43.9a6af4"]]},{"id":"8baa81bc.35918","type":"http request","z":"6f69e0fd.984ac","name":"","method":"GET","ret":"obj","paytoqs":"ignore","url":"https://app.rakuten.co.jp/services/api/Recipe/CategoryRanking/20170426?applicationId=1051058085654770771&categoryId={{{categoryId}}}","tls":"","persist":false,"proxy":"","authType":"","x":610,"y":520,"wires":[["7d0d9ad1.7a9664"]]},{"id":"ad037ab7.9b31d8","type":"function","z":"6f69e0fd.984ac","name":"","func":"// 配列\nvar arr = [\"43-569\",\"43-570\",\"43-578\",\"43-571\",\"43-577\",\"43-572\",\"43-573\",\"43-574\",\"43-575\",\"43-576\",\"43-579\",\"43-580\",\"43-581\",\"43-582\"];\n\n// 配列からランダムで値を選択\nvar id = arr[ Math.floor( Math.random() * arr.length ) ] ;\n\nmsg.categoryId = id;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":440,"y":520,"wires":[["8baa81bc.35918"]]}]
~~~

## 1.食べたいレシピを答えてもらう機能の構築
主に、Node-REDと楽天レシピカテゴリ別ランキングAPIについて述べる。
LINE Botの基本設定などは本記事では割愛する。

### 1-1.パスタを食べたいと答えてもらう
まずは、何を聞いても「パスタが食べたい」と答えてもらう機能を構築した。（以下の赤枠内）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2607557/a20bb9a7-5a80-4dbb-89c2-0091da02855a.png)

*switch*ノードは、以下のように設定した。
パスタ、スパゲ、イタリアを含まなければ、その他のルートに遷移させる。
（イタリアに行きたいと言ってもイタリアン料理のレシピを返してしまう仕組みではある）
<img width="500" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2607557/8e03af57-a5c7-225c-ec49-84e36ac46f14.png">

その後の*change*ノードでは、以下のように「パスタが食べたいな・・・」と返答してもらうよう設定した。
<img width="500" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2607557/562ef84e-01fe-f05d-2056-726874ea4d32.png">


### 1-2.食べたいレシピを答えてもらう（API事前準備編）
パスタかスパゲッティと伝えると、パスタのレシピを答えてもらうようにする。（以下の赤点線枠内）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2607557/06f8d01f-66fb-5bd0-a4e3-24fe95e06dde.png)



パスタ・スパゲッティの*function*ノードは、以下のように設定した。
~~~JavaScript
// 配列
var arr = ["15-687","15-137","15-676","15-681","15-369","15-677","15-683","15-682","15-678","15-679","15-684","15-680","15-138","15-139","15-140","15-141","15-142","15-685","15-686","15-143","15-145","15-146","15-144","15-147","15-151","15-382"] ;

// 配列からランダムで値を選択
var id = arr[ Math.floor( Math.random() * arr.length ) ] ;

msg.categoryId = id;

return msg;
~~~

arrに、各種パスタ料理のカテゴリIDを準備し、ランダムにIDを設定することにした。
楽天レシピは、パスタでもカルボナーラ、ミートソース等と細分化されている。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2607557/58d3d563-0526-21f3-eca9-aa76f91a7e6c.png)
IDについては、既に一覧を取得している記事があったため、記事中のパスタ部分を使用させて頂いた。

https://nokin-taro.com/rakuten-recipe-id/

同じ要領で、イタリアンと伝えるとイタリアン料理のレシピを答えてもらうようにしている。
これにより、恋人がランダムでレシピを言ってくるワガママさを実現できる。
~~~JavaScript
// 配列
var arr = ["43-569","43-570","43-578","43-571","43-577","43-572","43-573","43-574","43-575","43-576","43-579","43-580","43-581","43-582"];

// 配列からランダムで値を選択
var id = arr[ Math.floor( Math.random() * arr.length ) ] ;

msg.categoryId = id;

return msg;
~~~
### 1-3.食べたいレシピを答えてもらう（APIのリクエスト編）
楽天レシピカテゴリ別ランキングAPIのリクエストURLを設定する。（以下の赤点線枠内）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2607557/52847057-0221-fdf7-251d-88b764b322c9.png)

*http request*ノードは、以下のように設定した。
<img width="500" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2607557/a5beb783-fee0-79fa-2e68-4cb6758a0d77.png">

applicationIdには、取得したアプリIDを設定する。
categoryIdは、前述の*function*ノードで設定した値である。

~~~
https://app.rakuten.co.jp/services/api/Recipe/CategoryRanking/20170426?applicationId=アプリID&categoryId={{{categoryId}}}
~~~
### 1-4.食べたいレシピを答えてもらう（返答編）
JSON形式で返却された値を使用して、料理とレシピを返答する部分を構築する。（以下の赤点線枠内）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2607557/888c2813-4f71-0442-08a9-9c3613b2a333.png)

*function*ノードは、以下のように設定した。
ランダムにiの値を設定し、取得したレシピの内、1つを選択させる。
選択したrecipeTitle、recipeUrlを返答文に設定する。

~~~JavaScript
var min = 0 ;
var max = 3 ;
var i = Math.floor( Math.random() * (max + 1 - min) ) + min ;

//const result = msg.payload;
const result = '気分的には「' + msg.payload.result[i].recipeTitle + '」かな。作り方はここに載ってるから作って♡' + msg.payload.result[i].recipeUrl;
// 返信メッセージをhttp requestの結果にする
msg.payload  = result;

return msg;
~~~

これでランダムに選んだパスタカテゴリの中での1つのレシピを返答してくれるようになった。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2607557/ca5eaa19-dac2-441c-3cc8-5b83fbe8ff34.png)

イタリアンも同じ仕組みで設定した。
~~~JavaScript
var min = 0 ;
var max = 3 ;
var i = Math.floor( Math.random() * (max + 1 - min) ) + min ;

//const result = msg.payload;
const result = 'イタリアンもいいかも。たとえば「' + msg.payload.result[i].recipeTitle + '」とか。作り方はここに載ってるよ♡' + msg.payload.result[i].recipeUrl;
// 返信メッセージをhttp requestの結果にする
msg.payload  = result;

return msg;
~~~
## 2.リッチメニューの追加
ここまで作りはしたが、作った私はともかく「パスタ」「スパゲッティ」「イタリアン」と最初からピンポイントで言うことは難しいかもしれない。
そこで、リッチメニューを追加してみた。

メニューアイコンをタップすれば、決まったコメントを返却する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2607557/e2ecc3ca-4618-02d9-848e-d27263120b79.png)

LINE Official Account Managerからリッチメニューを設定した。
これで開発の意図に沿ったメッセージを送ることが出来る。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2607557/7251e974-f30b-7a39-0114-a7fa84c7c92e.png)

## その他の料理も提案して欲しい
今回はパスタ、イタリアンのレシピを取得させるに留まったが、全ての料理カテゴリから取得したいという想いもある。
実は楽天APIの中に、カテゴリ一覧を返す[楽天レシピカテゴリ一覧API](https://webservice.rakuten.co.jp/documentation/recipe-category-list)がある。
先にカテゴリ一覧を取得してからcategoryIdに設定すれば、対応することが出来るはずだ。

## 作ってみた感想
APIは、ルールの中であれば多くの情報を返してくれる。
LINE BotはUIに優れ、何よりも多くの人が使い慣れている。
今回作成したBotも、説明書が無くともリッチメニューをタップすればレシピが返却されるため使いやすい。（妻も説明無しで使えた）

これらを組み合わせることで、比較的容易に素早くそれらしい開発が出来てしまうことは素晴らしいと感じた。
そして、優れたツールに適応して、組み合わせて、素早くリリースすることを求められる時代がもう間もなく来るんだろうということもヒシヒシと感じる。

これからも気を引き締めて、時代に取り残されないようにしないといけない・・・！