【Node-RED】AIによる広島名物とのそっくり度からオススメのお店を紹介するLINE BOTを作ったよ





今回は、9月12日・13日にオンラインで開催されたハッカソンで作ったものの作り方を紹介します。

![94cfca8a5887d584dda15e8d86631fb6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/291592/0124d24f-b381-fa68-c31e-0db9c1cb2209.png)

https://hmcn.connpass.com/event/185853/


##ハッカソンのテーマ
今すぐ "GoTo 広島" の人にも、今すぐには行かなくても将来行くかもしれない人にも お勧めできる ～ "ひろしま" 体験プロダクト ～　

##作ったもの

###タイトル
【広島県へ提案】AI時代のそっくりさんをGO TO広島で地方創生

###説明
新型コロナで旅行者が激減しており、新たな観光施策が必要です。
ただし、「ばらまき」は一過性にすぎないので、広島に興味をもってもらうような施策を考えました。
そこで、私は、
<b>「AI時代のそっくりさん、GoTo広島キャンペーン」</b>
を提案します。

私たちは、自分に似ている人に特別に親しみをもつ傾向があります。
そこで、広島にゆかりのあるものに似ている人を呼ぼうと考えます。

とはいえ、ただ似ているだけでは面白くありません。
時はAI時代です。
AIが見た「そっくりさん」を呼びましょう。
AIが見た「そっくりさん」は我々が認識する「そっくりさん」とは違います。人知を超えた感覚に触れることができるでしょう。
それが、AI時代の観光キャンペーンなのです。

<br>
めちゃくちゃ意味不明ですが、ハッカソンという短時間の間で自分の技術の範囲内で実装するアイデアが浮かび、後付けで無理やり「GoTo広島」にこじつけたのが実態ですwww


###デモ

[![Image from Gyazo](https://i.gyazo.com/cf2e097aad1de5314a37a5d596c246d8.gif)](https://gyazo.com/cf2e097aad1de5314a37a5d596c246d8)

写真を投稿すると、「広島の名物と似てる度」をAIが算出し、その広島名物に関するオススメのお店を紹介してくれます。

このデモでは、最初の男性はつけ麺度７１％なので「風風ラーメン」というお店をレコメンドし、次の男性は汁なし担担麺度が５３％なので、「武蔵房」というお店をレコメンドしました。

他の広島名物には、広島風お好み焼き、牡蠣、尾道ラーメンを登録しています。

審査員や他の参加者から「確かに、似てると言われたら行きたくなる」などのコメントいただきました。

広島県さん、「GO TO広島キャンペーン」にいかがでしょうか？

###チャットボットのQRコード
実際に動いてますので、是非ためしてみてください。
<IMG SRC="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/291592/1d35635a-e3fd-4801-8204-f6dc9f8f3808.png" width=40%>


<br>
##システム
###構成図
![プレゼンテーション2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/291592/6fa2c185-c9b5-dbcf-78b9-065e1f9a5ad0.jpeg)

enebular、Node-REDをクラウドの中にいれてるような書き方でいいのか分かりませんが雰囲気的には伝わると思いますのでヨシとします。

###環境
開発　  ：enebular + Node-RED 
クラウド：Heroku





##機械学習モデル作成

まずは、機械学習用に、ネットから広島名物（お好み焼き、汁なし担担麺、つけ麺、牡蠣、尾道ラーメン）の画像をスクレイピングのプログラムを使って取得します。

スクレイピングのプログラムはこちらを拝借しました。
https://itstudio.co/2018/12/28/8664/
<br>
画像が揃ったら、[Teachable Machine](https://teachablemachine.withgoogle.com/)という簡単に機械学習モデルを作れるサービスで学習させました。
[![Image from Gyazo](https://i.gyazo.com/7e4caf1f9731592edc6d93ddf654720c.png)](https://gyazo.com/7e4caf1f9731592edc6d93ddf654720c)




<br>

##Node-RED詳細
Node-REDで作りました。



![スクリーンショット 2020-09-16 20.45.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/291592/8916c89e-8b55-bf37-1d7b-84d7ded759f4.png)

###参考サイト
以下の２つのサイトをめちゃくちゃ参考にしてます。
本当にありがとうございました。

１〜４、１２、１３は、
[Teachable Machine x LINE Bot x Node-RED ハンズオン](https://www.katacoda.com/gaomar/scenarios/teachablemachine-linebot-node-red-playground)

１１は、
[Node-REDで待ち合わせ（同期・非同期）フローの作る方法を丁寧に説明してみました。](
https://qiita.com/island_peak/items/38556ac7eb6dc3b14d23)

###ノードを追加
デフォルトにはないTeachable MachineとLINE Massaging APIのノードを「パレットの管理」欄から追加

- node-red-contrib-teachable-machine

- node-red-contrib-line-messaging-api
<br>

###各ノードの説明

<B>１．[post]_linebot</B>
[![Image from Gyazo](https://i.gyazo.com/068d94174cbed6f852edd7b9643d36c4.png)](https://gyazo.com/068d94174cbed6f852edd7b9643d36c4)

<B>２．function01</B>

```function01.js
const messageID = msg.payload.events[0].message.id;
const replyToken = msg.payload.events[0].replyToken;

msg.messageID = messageID;
msg.replyToken = replyToken;

return msg;
```
<br>
<B>３．http request</B>
[![Image from Gyazo](https://i.gyazo.com/87a37b682dae61dab58512706f114a8f.png)](https://gyazo.com/87a37b682dae61dab58512706f114a8f)

トークンは、LINE Messaging APIのChannel access tokenを入力

<br>
<B>４．teachable machine</B>


[![Image from Gyazo](https://i.gyazo.com/ca3b45dd9fc882428cb9d486e5413bb2.png)](https://gyazo.com/ca3b45dd9fc882428cb9d486e5413bb2)

Url欄に Teachable Machineで作った機械学習モデルのURLを入力
Output欄は、作品に応じて

<br>
<B>５．AI-function</B>
後ほどの１１の非同期処理のためにタグをつけます。

```function01.js
msg.tag = "AI";
return msg;
```

<br>
<B>６．change</B>
[![Image from Gyazo](https://i.gyazo.com/3374ab6787177d800685c9262b35fb78.png)](https://gyazo.com/3374ab6787177d800685c9262b35fb78)

<br>
<B>７．ぐるなび function01</B>

```GuruNavi-function01.js
msg.payload = encodeURIComponent(msg.payload)
return msg;
```

<br>
<B>８．http request</B>

[![Image from Gyazo](https://i.gyazo.com/f7f1042f9552200bb32d39eab93236e5.png)](https://gyazo.com/f7f1042f9552200bb32d39eab93236e5)

URLにAPIのURLを入力

「広島駅から3000メートル以内でAIが算出した広島名物がキーワードのお店」という設定にしました。

```
https://api.gnavi.co.jp/RestSearchAPI/v3/?keyid=YourAPIKey&latitude=34.392777&longitude=132.457525&range=5&freeword={{{payload}}}
```

ぐるなびのAPIはこちらを参照
https://api.gnavi.co.jp/api/manual/restsearch/
<br>
<B>９．json</B>
[![Image from Gyazo](https://i.gyazo.com/3b2cb6427d5c759cb2bf38ea43e8eea3.png)](https://gyazo.com/3b2cb6427d5c759cb2bf38ea43e8eea3)
<br>
<B>１０．ぐるなび function02</B>
後ほどの１１の非同期処理のためにタグをつけます。

```GuruNavi-function01.js
msg.tag = "GNAVI";
return msg;
```
<br>
<b>１１． AIとぐるなび両方揃うまで待機</B>

```AI-GuruNavi-Taiki.js
let chA = context.get('Ai')||[];
let chB = context.get('Gnavi')||[];
let tag = msg.tag;
delete msg.tag;

if (tag == "AI") {
    chA.push(msg);
} else if (tag == "GNAVI") {
    chB.push(msg);
    
} else {
    return msg;
}

let outA;
let outB;
if ((chA.length === 0) || (chB.length === 0)) {
    context.set('Ai',chA);
    context.set('Gnavi',chB);
    node.warn('hogehoge:'+chB.length);
    return null;
} else {
    outA = chA.shift();    
    outB = chB.shift();    
    context.set('Ai',chA);
    context.set('Gnavi',chB);
}

let newMsg = {payload:""};
newMsg.ai = outA;
newMsg.gnavi = outB;

return newMsg;
```
<br>
<B>１２．LINE BOT に出力する前処理</B>

実はオススメのお店はただの乱数でした。

``` maeshori.js
const className = msg.ai.payload[0].class;
const score = parseInt(msg.ai.payload[0].score*100);

//オススメのお店　（１０先から乱数）
let rand = Math.floor( Math.random () * msg.gnavi.payload.rest.length);
const msgText = className+"度"+score+"％\n\n[オススメのお店]\n"+msg.gnavi.payload.rest[rand].name+"\n"+msg.gnavi.payload.rest[rand].url;

msg.payload = {};
msg.payload.events = [
    {
        "type": "message",
        "replyToken": msg.ai.replyToken,
        "message":
            {
            "type": "text",
            "text": msgText
            }        
    }

];
return msg;

```
<br>
<B>１３．LINE BOT出力</B>

![スクリーンショット 2020-09-16 23.15.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/291592/9f41246e-9d98-0fa7-b0d6-5cd9c12401b4.png)


LINE Developerからmessaging APIのシークレットキーとアクセストークンを入力

<br><br>
これで完成！！

是非、広島に遊びに来てください！
そのときは、AIのオススメするお店でお食事しましょう。
<br><br>