「ねこBot」を作ってみた　～猫っぽく話しかけると、適当な鳴き声をランダムに自動返信～

LINE Botの勉強を始め、おうむ返し、APIとの連携と少しずつわかるようになってきました。
今回は、ヤマト運輸のLINE Bot の様に、特定の入力した内容に対し、ランダムな内容が
自動返信できないかと考えてみました。
（予約LINE Botを作ろうと考えている中での、 遊びとしての副産物。）

#作成の流れ
**　①LINE作業**
　　　LINE Developer登録
　　　BOT登録
　　　BOT設定
**　②Google作業**
　　　Googleスプレッドシート作成
　　　GoogleAppsScript(GAS)作成

#BOTの動作内容
　●猫っぽい言葉（「にゃ」「ニャ」「みゃ」「なー」を含む言葉）をBOTに送信すると
　　→　予め用意した鳴き声の候補の中からランダムで返事をする。
　●猫が怒った時の鳴き声（「フー」「ブー」「シャー」を含む言葉）をBOTに送信すると
　　→　予め用意した猫が怒った時の鳴き声の候補の中からランダムに返事をする。
![ねこBot動画.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2165076/f7220530-afff-4463-b054-8247bff5fc47.gif)
　ここで用意する候補というのは、鳴き声リスト。
　鳴き声リストはGoogleスプレッドシートにて管理する。

#実際の作成
###①LINE作業
　LINE DeveloperにログインしLINE Developerコンソール画面を開く。
　https://developers.line.biz/ja/ LINE Developerサイト
　※BOTの登録までは様々なサイトで解説しつくされているので割愛させて頂きます。

###②Google作業
####　(1) Googleスプレッドシート作成
　　Googleドライブより新規スプレッドシートを2つ作る。
**　　(A) 鳴き声リストを作るためスプレッドシート**
　　　●スプレッドシート名：ねこ返答のスプレッドシート　　シート名：ねこ、ねこ（怒）
　　　●シート名（ねこ）にA1セルから順に下へ鳴き声の候補を入力していく。
　　　　同様にシート名（ねこ（怒））にA1セルから順に下へ起こった時の鳴き声の
　　　　候補を入力していく。
![ねこ返答のスプレッドシート01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2165076/664e570b-e4d1-2d52-740a-db8223976a98.png)
![ねこ返答のスプレッドシート02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2165076/ad328bc4-9d2d-4548-17e5-c1d059d2c175.png)

**　　(B) LINEからPOSTされた時のjsonデータから、返事を返す際の返し先のスプレッドシート**
　　　●スプレッドシート名：log　　　シート名：log
　　　●LINEからPOSTされた時のjsonデータから、返事を返す際の返し先を特定する。
　　　　したがって、POSTされた時のjsonデータログを溜める用のスプレッドシートも
　　　　作成する。
　　　　手順は鳴き声リストと同様で、シート名を「log」にする。
　　　　※スプレッドシート名ではなく、下部のシート名なので注意
![ねこ返答のスプレッドシート03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2165076/69091436-6da0-22ae-8f18-7fc4999b74a1.png)

####　(2) GoogleAppsScript(GAS)作成
**　　(A) GoogleAppsScript(GAS)開き、コードを入力する**
　　　　スプレッドシート名：ねこ より、GoogleAppsScript(GAS)を開き
　　　（データ→スクリプトエディタ）、以下のコードをコピー＆ペーストし、
　　　必要情報は書きかえる。
　　※スプレッドシートIDはシートを開いてる際のURL以降の部分がID
　　　URLの中で、https://docs.google.com/spreadsheets/d/スプレッドシートID/edit#gid=シートID


```javascript
// LINE Developerのアクセストークン
var access_token = "YourAccessToken"
// リプライ先を特定するためのログ管理スプレッドシートID
var spreadsheet_id = "logスプレッドシート のスプレッドシートID"
// ねこリストのスプレッドシートID
var cat_spreadsheet_id = "ねこ返答のスプレッドシート のスプレッドシートID"
 
/**
 reply
 */
function reply(data) {
  var url = "https://api.line.me/v2/bot/message/reply";
  var headers = {
    "Content-Type" : "application/json; charset=UTF-8",
    'Authorization': 'Bearer ' + access_token,
  };
  var text = "";

    //送信されたメッセージ（data.events[0].message.text）の中に、
    //「にゃ」「みゃ」「ニャ」「なー」が含まれる（includes）場合 
  if (data.events[0].message.text.includes('にゃ') || data.events[0].message.text.includes('みゃ') || data.events[0].message.text.includes('ニャ')  || data.events[0].message.text.includes('なー')) {
    // ねこリストスプレッドシートを取得
    var cat = SpreadsheetApp.openById(cat_spreadsheet_id).getSheetByName("ねこ");
    // A1セルから入力されている最終行まで一気に取得
    var catData = cat.getRange(1, 1, cat.getLastRow());
    // ランダムで候補を選ぶ
    var intRandomNum = Math.round(Math.random()*cat.getLastRow());
    text = catData.getValues()[intRandomNum][0];

    //送信されたメッセージ（data.events[0].message.text）の中に、
    //「フー」「ブー」「シャー」が含まれる（includes）場合 
  }else if(data.events[0].message.text.includes('フー') || data.events[0].message.text.includes('ブー') || data.events[0].message.text.includes('シャー')){
    // ねこリストスプレッドシートを取得
    var cat = SpreadsheetApp.openById(cat_spreadsheet_id).getSheetByName("ねこ（怒）");
    // A1セルから入力されている最終行まで一気に取得
    var catData = cat.getRange(1, 1, cat.getLastRow());
    // ランダムで候補を選ぶ
    var intRandomNum = Math.round(Math.random()*cat.getLastRow());
    text = catData.getValues()[intRandomNum][0];
  
  }else {
    text = "ねこっぽくて話しかけてね。"
  }
 
  var postData = {
      "replyToken" : data.events[0].replyToken,
      "messages" : [
        {
          'type':'text',
          'text':text,
        }
      ]
    };
 
  var options = {
    "method" : "post",
    "headers" : headers,
    "payload" : JSON.stringify(postData)
  };
 
  return UrlFetchApp.fetch(url, options);
}
 
/**
 LINEからのPOST受け取り
 */
function doPost(e) {
  var json = JSON.parse(e.postData.contents);
  var data = SpreadsheetApp.openById(spreadsheet_id).getSheetByName('log').getRange(1, 1).setValue(json.events);
 
  reply(json);
}
```

**　　(B) 公開**
　　　GASを公開可能にする。
　　●やり方①　新しいエディタの場合
　　　右上の「デプロイ」ボタンを押し、「新しいデプロイ」を選択。
　　　開いたウインドウの左上の「種類の選択」の⚙を押し、「ウェブアプリ」を選択。
　　　出現した画面下のアクセスできるユーザーを「全員」に変更し、
　　　右下の「デプロイ」ボタンを押せば公開終了。
　　　数秒後に開いたウインドウのウェブアプリURLをコピーする。

　　●やり方②　以前のエディタの場合
　　　右上の「以前のエディタへ切替え」ボタンを押す。
　　　切り替わったら上のメニューバーの「公開」メニューから
　　　「ウェブアプリケーションとして導入」を選択。
　　　アプリケーションにアクセスできるユーザーを「全員(匿名ユーザーを含む)」
　　　にして導入ボタンを押せば、公開完了。
　　　最後に現在のウェブアプリケーションのURL（Current web app URL）の
　　　画面が出るので、コピーする。

　　ウェブアプリURL（以前のエディタの場合は、現在のウェブアプリケーションのURL
　　（Current web app URL））を、LINE Developerコンソール画面を開き、
　　　Webhook設定に張り付ける。
　　　　（node(VSCode)と違い”/webhook”は後ろに付けない。）


#工夫を要した箇所
　① 「疑似彼氏、作りました。」のコメント
"パターンを列挙していくとどんどんコードが長くなるのでexcelなどでパターンを書いておいて，ファイルを呼び出す形の方が全体的にすっきりする"
　　　を参考に、「LINE BOTとGASを使って晩ごはんを選んでくれるBOTを作る」
　　　を組み合わせて作ってみました。
　②　猫の鳴き声を返すリストを作成するとき、
　　　”通常の鳴き声”と”怒った時の鳴き声”を同じシートに記載したが上手く
　　　いかなかった。別シートにすることにより上手く動作しました。
　③　今回は”猫は気まぐれ”という前提で、話しかけたときに普通だったり、
　　　甘えたり、怒っていたりと適当に返答されるように、怒った言葉に対しては
　　　怒った鳴き声を返すという設定で作りました。

他にやってみたかったこと（今回の試みの中ではできなかったこと）
　①APIを叩く
　　まだそこまで知識がなくAPIを叩くところまでは出来ませんでした。
　　今後以下の様なことができるとよいと考えています。
　　●「疑似親友、作りました。」の様に、送信した言葉の感情をに対して適切な言葉を
　　　返信する。
　　●「疑似彼氏、作りました。」の様に、天気情報を取得して情報を返す。

　②こちらから話しかける前に、ねこ側から話しかける。

#【参照したホームページ】
LINE BOTとGASを使って晩ごはんを選んでくれるBOTを作る
　　https://www.dcom-web.co.jp/lab/bot/make_a_line_bot_with_gas
疑似彼氏、作りました。
　　https://qiita.com/mnana/items/3836aee1b749670804dc
疑似親友、作りました。
　　https://qiita.com/miso_taku/items/b938df6ae895102d4e98


#追記(2021/11/22)
　鳴き声だけの返信では何だかさみしかったので、ランダムに猫の画像を取得して鳴き声とともにLINE Botへ返すようなコードへ改変してみました。

　2021年11月20日に投稿した
　「VSCodeで動く天気Botを作り、GASで動く天気Botへ書き換えてみた。」
　　　https://qiita.com/21HideK/items/9764e2aee9447932cf19
　　　　　　の一部を応用しています（学んだ箇所、工夫を要した箇所②）。

　猫の画像を取得するAPIは以下のHPを利用しました。
　　　random.cat - MEOW!
　　　https://aws.random.cat/view/876
![Produce.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2165076/801de659-313b-c7e7-0f85-3689fc68d96e.gif)



```javascript
// LINE Developerのアクセストークン
var access_token = "YourAccessToken"
// リプライ先を特定するためのログ管理スプレッドシートID
var spreadsheet_id = "logスプレッドシート のスプレッドシートID"
// ねこリストのスプレッドシートID
var cat_spreadsheet_id = "ねこ返答のスプレッドシート のスプレッドシートID"

/**
 LINEからのPOST受け取り
 */
function doPost(e) {
  var json = JSON.parse(e.postData.contents);
  var data = SpreadsheetApp.openById(spreadsheet_id).getSheetByName('log').getRange(1, 1).setValue(json.events);
 
  reply(json);
}

/**
 reply
 */
function reply(data) {
  var url = "https://api.line.me/v2/bot/message/reply";
  var headers = {
    "Content-Type" : "application/json; charset=UTF-8",
    'Authorization': 'Bearer ' + access_token,
  };
  var cattext = "";

  var catapi = 'https://aws.random.cat/meow'
  var catres = UrlFetchApp.fetch(catapi).getContentText();  // 猫画像のAPIを叩く
  var catjson = JSON.parse(catres);

  if (data.events[0].message.text.includes('にゃ') || data.events[0].message.text.includes('みゃ') || data.events[0].message.text.includes('ニャ')  || data.events[0].message.text.includes('なー')) {
    // ねこリストスプレッドシートを取得
    var cat = SpreadsheetApp.openById(cat_spreadsheet_id).getSheetByName("ねこ");
    // A1セルから入力されている最終行まで一気に取得
    var catData = cat.getRange(1, 1, cat.getLastRow());
    // ランダムで候補を選ぶ
    var intRandomNum = Math.round(Math.random()*cat.getLastRow());
    
    cattext = catData.getValues()[intRandomNum][0];

        UrlFetchApp.fetch(url, {  // LINE BotのAPIを叩き返信
          'headers': headers,
          'method': 'post',
          'payload': JSON.stringify({
            'replyToken': data.events[0].replyToken,
            'messages': [{
               'type':'text',
               'text':cattext,
            },{
                'type': 'image',
                'originalContentUrl': catjson.file,
                'previewImageUrl': catjson.file
            
            }],
          }),
        })

  }else if(data.events[0].message.text.includes('フー') || data.events[0].message.text.includes('ブー') || data.events[0].message.text.includes('シャー')){
    // ねこリストスプレッドシートを取得
    var cat = SpreadsheetApp.openById(cat_spreadsheet_id).getSheetByName("ねこ（怒）");
    // A1セルから入力されている最終行まで一気に取得
    var catData = cat.getRange(1, 1, cat.getLastRow());
    // ランダムで候補を選ぶ
    var intRandomNum = Math.round(Math.random()*cat.getLastRow());
    
    cattext = catData.getValues()[intRandomNum][0];
        UrlFetchApp.fetch(url, {  // LINE BotのAPIを叩き返信
          'headers': headers,
          'method': 'post',
          'payload': JSON.stringify({
            'replyToken': data.events[0].replyToken,
            'messages': [{
               'type':'text',
               'text':cattext,
            }],
          }),
          })

  }else {
    cattext = "ねこっぽくて話しかけてね。"
            UrlFetchApp.fetch(url, {  // LINE BotのAPIを叩き返信
          'headers': headers,
          'method': 'post',
          'payload': JSON.stringify({
            'replyToken': data.events[0].replyToken,
            'messages': [{
               'type':'text',
               'text':cattext,
            }],
          }),
          })
  }
 
}
```