【LineBot】地域、都道府県の選択を対話形式で行う


# はじめに
LineBotの作成の際に
「都道府県の選択を簡単に行えればいいな〜」
「対話形式で都道府県選択を行いたいな〜」
と思ったので自作してみました。

まだまだプログラミング初心者なので、DRYになっていない部分多々ありますが興味あればご覧ください。

基本的なLineBot作成方法は下記の記事などを参考にしてください。（すごく分かりやすいです。）
[今更ながらRails5+line-bot-sdk-ruby+HerokuでLineBot作成してみたら、色々詰まったのでまとめました。](https://qiita.com/y428_b/items/d2b1a376f5900aea30dc)

# 環境
- Ruby on rails
- heroku

# 作ったもの
下記の手順で都道府県を選択できるようにしました。
①地域の選択肢を、クイックリプライで表示させる。
②ユーザーが地域を選択する。
②選択した地域に含まれる各都道府県を、クイックリプライで表示させる。
④ユーザーが都道府県を選択する。
⑤LineBot側で選択された都道府県を認識してメッセージが来る。


以下がイメージ画像です。
※今回作成する部分は「どこの地域で遊ぶ？」〜「東京都で遊ぶ！」までの部分です。
<img width="320" alt="IMG_4121.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/356866/9b67f296-f207-5957-4c76-4ae1135a16c4.jpeg">　→　<img width="320" alt="IMG_4120.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/356866/8f9895c2-aaac-fa26-5be9-ac3cb34fbd41.jpeg">　→　<img width="320" alt="IMG_4122.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/356866/20ecae04-fe8c-bc8a-2eca-ae8a79d13ba2.jpeg">
灰色の部分がクイックリプライと呼ばれる機能で、タッチすると`MessageType::Text`として送信されます。


# 作成方法
## 概要
①都道府県モデル（Prefectureモデル）とseedデータ作成。
②地域と都道府県選択のためのmessageを用意。
③linebot_controllerのcallbackアクションに、分岐を作成。
といった流れで作成していきます。

## ①都道府県モデルとseedデータ作成。
まずは都道府県モデル（Prefectureモデル）を作成していきます。
`$bundle exec rails g model Prefecture name:string`
`$bundle exec rails db:migrate`

その後、都道府県データを作るためにseedをいじっていきます。

```ruby:db/seeds.rb
# 47都道府県の配列を作る。
prefectures = [ "北海道", "青森県", ・・・・・・ "沖縄県" ]  

# prefecturesの配列を元にデータ作成する。
prefectures.each do |prefecture|
  Prefecture.create!(
    name: prefecture
  )
end
```

作成できたら
`$bundle exec rails db:seed`

これで下記のようなデータが出来るはずです。

| id  | name |
|:---|----:|
| 1   | 北海道 |
| 2   | 青森県 | 
| ・・・ | ・・・ | 
| 47  | 沖縄県 |


## ②地域と都道府県選択のためのmessageを用意。

LineBotからリプライされるメッセージを作っていきます。
非常に量が多いため、`app/controllers/linebot_controller.rb`には記載せず、
今回は`app/models/line_clinet.rb`に記載しました。

<details><summary>app/models/line_clinet.rbのサンプルコード</summary><div>

```ruby:app/models/line_clinet.rb
class LineClient
  # 1回目の質問(地域選択部分)
  def self.first_reply
    { "type": "text",
                    "text": "どこの地域で遊ぶ？",
                    "quickReply": {
                      "items":
                      [
                        {
                          "type": "action",
                          "action": {
                          "type": "message",
                          "label": "北海道,東北",
                          "text": "北海道,東北"
                          }
                        },
                        {
                          "type": "action",
                          "action": {
                          "type": "message",
                          "label": "関東",
                          "text": "関東"
                          }
                        },
                        {
                          "type": "action",
                          "action": {
                          "type": "message",
                          "label": "甲信越,東海",
                          "text": "甲信越,東海"
                          }
                        },
                        {
                          "type": "action",
                          "action": {
                          "type": "message",
                          "label": "関西,北陸",
                          "text": "関西,北陸"
                          }
                        },
                        {
                          "type": "action",
                          "action": {
                          "type": "message",
                          "label": "中国,四国",
                          "text": "中国,四国"
                          }
                        },
                        {
                          "type": "action",
                          "action": {
                            "type": "message",
                            "label": "九州",
                            "text": "九州"
                            }
                        }
                      ]
                    }
                  }
  end

  # 2回目の質問(都道府県の選択部分)
  def self.second_reply_hokkaido_tohoku
    { "type": "text",
                    "text": "どの県で遊ぶの？",
                    "quickReply": {
                      "items":
                      [
                        {
                          "type": "action",
                          "action": {
                          "type": "message",
                          "label": "北海道",
                          "text": "北海道"
                          }
                        },
                        {
                          "type": "action",
                          "action": {
                          "type": "message",
                          "label": "宮城県",
                          "text": "宮城県"
                          }
                        },
                        {
                          "type": "action",
                          "action": {
                            "type": "message",
                            "label": "青森県",
                            "text": "青森県"
                          }
                        },
                        {
                          "type": "action",
                          "action": {
                          "type": "message",
                          "label": "岩手県",
                          "text": "岩手県"
                          }
                        },
                        {
                          "type": "action",
                          "action": {
                          "type": "message",
                          "label": "秋田県",
                          "text": "秋田県"
                          }
                        },
                        {
                          "type": "action",
                          "action": {
                          "type": "message",
                          "label": "山形県",
                          "text": "山形県"
                          }
                        },
                        {
                          "type": "action",
                          "action": {
                          "type": "message",
                          "label": "福島県",
                          "text": "福島県"
                          }
                        },
                        {
                          "type": "action",
                          "action": {
                          "type": "message",
                          "label": "地域を選び直す",
                          "text": "地域を選び直す"
                          }
                        }
                      ]
                    }
                  }
  end

  def self.second_reply_kanto
     { "type": "text",
                  "text": "どの県で遊ぶの？",
                  "quickReply": {
                    "items":
                    [
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "東京都",
                        "text": "東京都"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                          "type": "message",
                          "label": "神奈川県",
                          "text": "神奈川県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "千葉県",
                        "text": "千葉県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "埼玉県",
                        "text": "埼玉県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "群馬県",
                        "text": "群馬県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "栃木県",
                        "text": "栃木県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "茨城県",
                        "text": "茨城県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "地域を選び直す",
                        "text": "地域を選び直す"
                        }
                      }
                    ]
                  }
                }
  end

  def self.second_reply_kousinnetu_tokai
     { "type": "text",
                  "text": "どの県で遊ぶの？",
                  "quickReply": {
                    "items":
                    [
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "山梨県",
                        "text": "山梨県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                          "type": "message",
                          "label": "長野県",
                          "text": "長野県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "新潟県",
                        "text": "新潟県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "愛知県",
                        "text": "愛知県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                          "type": "message",
                          "label": "岐阜県",
                          "text": "岐阜県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "三重県",
                        "text": "三重県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "静岡県",
                        "text": "静岡県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "地域を選び直す",
                        "text": "地域を選び直す"
                        }
                      }
                    ]
                  }
                }
  end

  def self.second_reply_kansai_hokuriku
     { "type": "text",
                  "text": "どの県で遊ぶの？",
                  "quickReply": {
                    "items":
                    [
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "大阪府",
                        "text": "大阪府"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                          "type": "message",
                          "label": "京都府",
                          "text": "京都府"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "兵庫県",
                        "text": "兵庫県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "奈良県",
                        "text": "奈良県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "和歌山県",
                        "text": "和歌山県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "滋賀県",
                        "text": "滋賀県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "石川県",
                        "text": "石川県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                          "type": "message",
                          "label": "富山県",
                          "text": "富山県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "福井県",
                        "text": "福井県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "地域を選び直す",
                        "text": "地域を選び直す"
                        }
                      }
                    ]
                  }
                }
  end

  def self.second_reply_chugoku_shikoku
     { "type": "text",
                  "text": "どの県で遊ぶの？",
                  "quickReply": {
                    "items":
                    [
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "広島県",
                        "text": "広島県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                          "type": "message",
                          "label": "岡山県",
                          "text": "岡山県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "山口県",
                        "text": "山口県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "鳥取県",
                        "text": "鳥取県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "島根県",
                        "text": "島根県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "香川県",
                        "text": "香川県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                          "type": "message",
                          "label": "愛媛県",
                          "text": "愛媛県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "徳島県",
                        "text": "徳島県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "高知県",
                        "text": "高知県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "地域を選び直す",
                        "text": "地域を選び直す"
                        }
                      }
                    ]
                  }
                }
  end

  def self.second_reply_kyusyu
     { "type": "text",
                  "text": "どの県で遊ぶの？",
                  "quickReply": {
                    "items":
                    [
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "福岡県",
                        "text": "福岡県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                          "type": "message",
                          "label": "佐賀県",
                          "text": "佐賀県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "長崎県",
                        "text": "長崎県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "熊本県",
                        "text": "熊本県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "大分県",
                        "text": "大分県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "宮崎県",
                        "text": "宮崎県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "鹿児島県",
                        "text": "鹿児島県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "沖縄県",
                        "text": "沖縄県"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "地域を選び直す",
                        "text": "地域を選び直す"
                        }
                      }
                    ]
                  }
                }
  end

  # 3回目の質問
  def self.third_reply(prefecture)
     { "type": "text",
                  "text": "#{prefecture}で遊ぶでいい?",
                  "quickReply": {
                    "items":
                    [
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "#{prefecture}で遊ぶ!",
                        "text": "#{prefecture}で遊ぶ!"
                        }
                      },
                      {
                        "type": "action",
                        "action": {
                        "type": "message",
                        "label": "地域を選び直す",
                        "text": "地域を選び直す"
                        }
                      }
                    ]
                  }
                }
  end

end
```

</div></details>

変数を渡したりしたいので、基本的にはmethodとして定義しています。

## ③linebot_controllerのcallbackアクションに、分岐を作成。

送信されるメッセージに応じて、LineBotの返信メッセージを変えていきます。
想定通りのフローでユーザーが動くと
`first_reply`
`second_reply_〇〇`
`third_reply`
の順に表示されて、反応していくことになります。

<details><summary>app/controllers/linebot_controller.rbのサンプルコード</summary><div>

```ruby:app/controllers/linebot_controller.rb
class LinebotController < ApplicationController
  require 'line/bot'

  protect_from_forgery :except => [:callback]

  def client
    @client ||= Line::Bot::Client.new { |config|
      config.channel_secret = ENV["LINE_CHANNEL_SECRET"]
      config.channel_token = ENV["LINE_CHANNEL_TOKEN"]
    }
  end

  def callback
    body = request.body.read

    signature = request.env['HTTP_X_LINE_SIGNATURE']
    unless client.validate_signature(body, signature)
      head :bad_request
    end

    events = client.parse_events_from(body)

    # 作った都道府県データを再度変数に保存しておく。
    prefectures = Prefecture.all

    events.each { |event|
      #ここでLINEで送った文章を取得。スペースなどの空白は削除
      @message = event.message['text'].gsub(" ", "") 
      # eventの種類による場合分け(MesageやBeaconなどがあります)
      case event
      when Line::Bot::Event::Message # Event::Messageの場合の処理
        case event.type
        when Line::Bot::Event::MessageType::Text # MessageType::Textの場合の処理
          case @message # 送信されたメッセージに応じて分岐させる。

          # 地域選択された際に、該当地域の各都道府県を表示する部分。←２枚目の画像の部分
          when "北海道,東北"
            message = ::LineClient.second_reply_hokkaido_tohoku
            client.reply_message(event['replyToken'], message)
          when "関東"
            message = ::LineClient.second_reply_kanto
            client.reply_message(event['replyToken'], message)
          when "甲信越,東海"
            message = ::LineClient.second_reply_kousinnetu_tokai
            client.reply_message(event['replyToken'], message)
          when "関西,北陸"
            message = ::LineClient.second_reply_kansai_hokuriku
            client.reply_message(event['replyToken'], message)
          when "中国,四国"
            message = ::LineClient.second_reply_chugoku_shikoku
            client.reply_message(event['replyToken'], message)
          when "九州"
            message = ::LineClient.second_reply_kyusyu
            client.reply_message(event['replyToken'], message)

          # Prefectureモデルに該当するメッセージの場合に反応する。←３枚目の画像の部分
          when *prefectures.pluck(:name) #prefecturesのnameカラムに値があるものに反応するように記載。
            prefecture = Prefecture.find_by(name: @message)
            message = ::LineClient.third_reply(prefecture.name)
            client.reply_message(event['replyToken'], message)

          # 最初の質問部分（設定以外のテキストが送られてきた場合に表示） ←１枚目の画像の部分
          else
            message = ::LineClient.first_reply
            client.reply_message(event['replyToken'], message)
          end
        end
      end
    }


    head :ok
  end

end
```

</div></details>

# まとめ
コードを見ていただくと分かる通り、実はメッセージによって分岐させているだけなので、一番初めに「北海道」などと送信すると、LineBotは対話形式を無視していきなり反応してしまいます。欠陥品です。（笑）

「(笑)じゃねーよ、もっといい方法あるだろがボケ」と思われている方いらっしゃると思いますので、もし思われている方いればいい方法教えて欲しいです。（gemとかどっかにありそう）

# おまけ
もしよければ「ずぼらデート」というLineBotを作ったので、友達登録してみてください。

↓QRコード
![yemHjR88WA.png](https://qiita-image-store.s3.amazonaws.com/0/356866/90fcc5c3-7661-4506-5546-6a0103e6b046.png)