【Rails】Twitter APIとLINE APIでジャニーズの私物を特定して、すぐに購入できるサービスを作ってみた!!! PF [LINE API部分❷]

#はじめに
PFで**ジャニーズの私物を特定して、すぐに購入できるサービス**を作成したのでまとめます。
あまりにも長くなりそうなので、今回は`LINE Messaging API`を使用した部分についてまとめます。
❶はこちらです。

https://qiita.com/mm_st/items/4ca1ab54d207effdf3ff

#サービスの概要

ジャニーズファンは推しと同じものを日常生活やライブで身につけたいという心理があります。
twitte上にファンの人が私物を特定してツイートをするので、そのツイートをキャッチして、LINEで通知・購入できるサービスになっています。

#なぜこのサービスを作成したか
私物の特定のツイートがされるとすぐファンが購入し、売り切れてしまうことがよくあります。
私自身、私物特定のツイートを発見し、サイトを検索してももう売り切れてしまっていたということが何回もあります。
他のファンよりも早く購入できるサービスがあればいいなと思い、このサービスを作成しました。

##完成したサービス
Webサイトは以下になります↓

https://www.findgoodsofstan.jp/

LINEのQRコードになります。
ぜひ動かしてみてください!!
<img width="150" alt="a163e5fa2b2098ef30926fb607faa8ff.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2133440/a14102d3-51c9-2d22-8d96-89396b192b6d.png">

サービスのイメージがしづらいと思いますがこんな流れになります
1.Twitterで私服特定ツイートがされます。
<img width="150" alt="S__37232668.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2133440/53b8694a-7863-28c2-f542-724b2666d617.jpeg">

2.LINEにこのような通知が届きます。
<img width="150" alt="S__37232670.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2133440/655e3495-461c-9893-14ef-fbb72d2fd061.jpeg">

3.`商品検索ボタン`から検索可能です。
<img width="150" alt="S__37232676.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2133440/0cac9a6b-b08e-177b-f1ba-8fc533464d59.jpeg">

4.`To tweetボタン`からソース元に遷移可能です。
<img width="150" alt="S__37232678aaa.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2133440/eb20d86e-88f7-1fb5-41fe-7d4415d933ab.jpeg">

**LINE友達登録後できること**
1.最新の情報を確認できます。
リッチメニュー右上の`最新の情報ボタン`を押す→情報を確認したい`メンバーを選択`する→`最新の情報`が表示される
<img width="150" alt="S__37232679ss.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2133440/8356275b-8543-573c-7977-8c0be57320cc.jpeg">

<img width="150" alt="S__37232680ss.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2133440/5fd3ae36-11f3-2ab2-dee2-a141e96f05eb.jpeg">

2.情報を受け取るメンバーを選択できます。
リッチメニュー左上の`メンバー設定を変更する`を選択する→設定した`いメンバーを選択`する→通知を`受け取る`か、`受け取らない`かを選択する

<img width="150" alt="S__37232682b.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2133440/4ff385cc-e26b-8c41-82b2-d8193d26bca5.jpeg">

<img width="150" alt="S__37232683b.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2133440/bccb87c7-e4d8-4915-8ead-9572dff76465.jpeg">

もちろん、現在の通知設定についてもリッチメニュー左下の`メンバー設定を確認する` から確認可能です。

<img width="150" alt="S__37232681b.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2133440/993e74a7-e425-3671-d2c8-32306ad510af.jpeg">


##使用した機能
Twitter API
LINE Messaging API
dotenv-rails'
config gem
ransack gem
sorcery gem
kaminari gem
enum_help gem
rails-i18n gem
AWS
heroku

##ER図
![3917056f5181a1ce73694684e344b358.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2133440/c94b651d-f7d3-2072-fdc5-fd44e049b93d.png)

https://drive.google.com/file/d/10gO9reC8SdPK9OE3ZZntfC9CflYIkJHv/view?usp=sharing


##画面遷移図

![d657ee3591bde2b85796fbee48567573.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2133440/26fd44d1-3ee1-0043-31c7-63dc3a8e16b1.png)

https://www.figma.com/file/pFrYU4hSwDQFn4da2XSTyh/Untitled?node-id=32%3A2



##LINE Messaging API

「LINE BOT」とは、メッセージアプリのLINEを使用して、ユーザーの質問に自動で返答できたり、メッセージを送信できたりするプログラムのことです。

https://developers.line.biz/ja/services/messaging-api/

仕組みについてはこちら↓

>Messaging APIを使って、ボットサーバーとLINEプラットフォームの間でデータを交換できます。リクエストは、JSON形式でHTTPSを使って送信されます。

>1.ユーザーが、LINE公式アカウントにメッセージを送信します。
>2.LINEプラットフォームからボットサーバーのWebhook URLに、Webhookイベントが送信されます。
>3.Webhookイベントに応じて、ボットサーバーからユーザーにLINEプラットフォームを介して応答します

https://developers.line.biz/ja/docs/messaging-api/overview/#how-messaging-api-works

###今回の目標
・LINEbotは一つで6人分の情報を送信したい
・友達登録してくれたユーザーが自分で受け取るメンバーを選択できるようにしたい
・メンバー設定等はLINE画面で、全てこなせるようにしたい

###完成系

受信するメンバー設定をLINE画面からできるようにする。
リッチメニュー左上の`メンバー設定を変更する`を選択する→設定した`いメンバーを選択`する→通知を`受け取る`か、`受け取らない`かを選択する。


<img width="150" alt="S__37232682b.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2133440/4ff385cc-e26b-8c41-82b2-d8193d26bca5.jpeg">

<img width="150" alt="S__37232683b.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2133440/bccb87c7-e4d8-4915-8ead-9572dff76465.jpeg">

###流れ
1.リッチメニューを押したときにレスポンスボディに指定する`data`を含ませる
2.`data`の内容を判断させ、処理を行う

####1.リッチメニューを押したときにレスポンスボディに指定する`data`を含ませる

#####リッチメニューの`メンバー設定を変更する`ボタンを押すと、`data`に`set_member`が含まれているようにする

以下のような形で設定しておく。ここの部分については前回の記事でまとめたので省略します。

```
"action": {
        "type": "postback",
        "data": "set_member"
     }
```

####2.`data`の内容を判断させ、処理を行う

#####`data`に`set_member`が含まれていた際に、指定した`message`が返答されるようにする。ここで作成する`message`はユーザーが選択できる形に設定し、`data`にメンバーを判別できる文字を含ませておく。
ここの部分を作成しています。

<img width="100" alt="S__37232682b.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2133440/4ff385cc-e26b-8c41-82b2-d8193d26bca5.jpeg">

```ruby:line_users_conrtoller.rb
def line_responce
    #LINE Messaging API認証
    line_client ||= Line::Bot::Client.new { |config|
      config.channel_secret = ENV["LINE_CHANNEL_SECRET"]
      config.channel_token = ENV["LINE_CHANNEL_TOKEN"]
    }
      body = request.body.read
      signature = request.env['HTTP_X_LINE_SIGNATURE']
      unless line_client.validate_signature(body, signature)
        error 400 do 'Bad Request' end
      end
      #request.bodyを解析する
      events = line_client.parse_events_from(body)
      events.each do |event|
        #user_id取得
        userId = event['source']['userId']
          data = event['postback']['data']
          if data == "start"
            LineUser.start(userId)
          #dataにset_memberが含まれていたら
          elsif data == ("set_member")
            LineUser.set_member(userId)
          elsif data.include?("_info")
            LineUser.settei_member(data, userId)
          elsif data.include?("on_")
            LineUser.on_info(data, userId,)
          elsif data.include?("off_")
            LineUser.off_info(data, userId)
          end
        end
      end
   head :ok
end
```

```ruby:LineUser.rb
#メンバー別に通知を設定(メンバー指定部分)
  def self.set_member(user_id)
    message = 
      {
      "type": "flex",
      "altText": "メンバー設定",
      "contents": {
        "type": "bubble",
        "body": {
          "type": "box",
          "layout": "vertical",
          "spacing": "sm",
          "contents": [
            {
              "type": "text",
              "wrap": true,
              "weight": "bold",
              "size": "xl",
              "text": "メンバー設定"
            },
            {
              "type": "text",
              "text": "通知を受け取るメンバーを設定します。",
              "color": "#aaaaaa",
              "size": "sm",
              "flex": 12
            },
            {
              "type": "text",
              "text": "設定したいメンバーを選択してください。",
              "color": "#aaaaaa",
              "size": "sm",
              "flex": 12
            }
          ]
        },
        "footer": {
          "type": "box",
          "layout": "vertical",
          "spacing": "sm",
          "contents": [
            {
              "type": "button",
              "style": "link",
              "height": "sm",
              "action": {
                "type": "postback",
                "label": "高地優吾さん",
                "displayText": "高地優吾さんの情報について設定する",
                "data": "yugo_info"
              }
            },
            {
              "type": "button",
              "style": "link",
              "height": "sm",
              "action": {
                "type": "postback",
                "label": "京本大我さん",
                "displayText": "京本大我さんの情報について設定する",
                "data": "taiga_info"
              }
            },
            #長いので省略
          ]
        }
      }
    }
    client.push_message(user_id, message)
  end
```

`"data": "yugo_info"`や`"data": "taiga_info"`をそれぞれ設定する。

#####上で返信した`message`をユーザーがタッチすると、また`data`がレスポンスボディとして戻ってくるので、`data`の内容を判断し、どのメンバーを設定するのか判断する。
今回返信する`message`はユーザーが`通知を受け取る`か`受け取らないか`を選択できる形に設定し、`on`と`off`を判別できる文字を含ませておく。

ここの部分を作成しています。
<img width="100" alt="S__37232682b.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2133440/4ff385cc-e26b-8c41-82b2-d8193d26bca5.jpeg">

`data": "メンバー名_info`の形で作成しているので、以下のように判断させる。

```ruby:line_users_conrtoller.rb
def line_responce
      #省略
      events = line_client.parse_events_from(body)
      events.each do |event|
        #省略
          #今回はここの部分
           #dataに_infoが含まれていたら
          elsif data.include?("_info")
            LineUser.settei_member(data, userId)
        end
      end
   head :ok
end
```

dataの部分も引数で受け取り、名前を判断しています。

```ruby:LineUser.rb
def self.settei_member(data, user_id)
    data_member_check(data)
    #dateのメンバー名の部分のみ抜き出し
    data.slice!(-5,5)
    message = 
      {
      "type": "flex",
      "altText": "メンバー別通知設定",
      "contents": {
        "type": "bubble",
        "body": {
          "type": "box",
          "layout": "vertical",
          "spacing": "sm",
          "contents": [
            {
              "type": "text",
              "wrap": true,
              "weight": "bold",
              "size": "xl",
              "text": "通知設定"
            },
            {
              "type": "text",
              "text": "#{@name}さんの通知について設定します。",
              "color": "#aaaaaa",
              "size": "sm",
              "flex": 12
            },
            {
              "type": "text",
              "text": "複数のメンバーの通知を受け取る場合、",
              "color": "#aaaaaa",
              "size": "sm",
              "flex": 12
            },
            {
              "type": "text",
              "text": "それぞれ設定してください。",
              "color": "#aaaaaa",
              "size": "sm",
              "flex": 12
            },
            {
              "type": "text",
              "text": "いつでも変更可能です。",
              "color": "#aaaaaa",
              "size": "sm",
              "flex": 12
            }
          ]
        },
        "footer": {
          "type": "box",
          "layout": "vertical",
          "spacing": "sm",
          "contents": [
            {
              "type": "button",
              "style": "link",
              "height": "sm",
              "action": {
                "type": "postback",
                "label": "通知を受け取る",
                "displayText": "#{@name}さんの情報を受け取る",
                "data": "on_#{data}"
              }
            },
            {
              "type": "button",
              "style": "link",
              "height": "sm",
              "action": {
                "type": "postback",
                "label": "通知を受け取らない",
                "displayText": "#{@name}さんの情報を受け取らない",
                "data": "off_#{data}"
              }
            }
          ]
        }
      }
    }
    client.push_message(user_id, message)
  end

#dataでユーザーを判定する
  def self.data_member_check(data)
    @name = '高地優吾' if data.include?('yugo')
    @name = '京本大我' if data.include?('taiga')
    @name = '田中樹' if data.include?('juri')
    @name = '松村北斗' if data.include?('hokuto')
    @name = 'ジェシー' if data.include?('jess')
    @name = '森本慎太郎' if data.include?('shintarou')
  end
```


#####はユーザーが`通知を受け取る`か`受け取らないか`についての`data`がレスポンスとして戻ってくるので、処理を行う

ここの部分を作成しています。
<img width="150" alt="S__37232683b.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2133440/bccb87c7-e4d8-4915-8ead-9572dff76465.jpeg">

```ruby:line_users_conrtoller.rb
def line_responce
    #省略
      events = line_client.parse_events_from(body)
      events.each do |event|
        #user_id取得
        userId = event['source']['userId']
          data = event['postback']['data']
          #省略
          #今回はここの部分
          elsif data.include?("on_")
            LineUser.on_info(data, userId,)
          elsif data.include?("off_")
            LineUser.off_info(data, userId)
        end
      end
   head :ok
end
```

```ruby:LineUser.rb
def self.on_info(data, user_id)
    data_member_check(data)
    @user = LineUser.find_by(line_user_id: user_id)
    if data.include?('yugo')
      @user.yugo_on!
    elsif data.include?('taiga')
      @user.taiga_on!
    elsif data.include?('juri')
      @user.juri_on!
    elsif data.include?('hokuto')
      @user.hokuto_on!
    elsif data.include?('jess')
      @user.jess_on!
    elsif data.include?('shintarou')
      @user.shintarou_on!
    end
    message = 
      {
      "type": "flex",
      "altText": "メンバー別通知設定",
      "contents": {
        "type": "bubble",
        "body": {
          "type": "box",
          "layout": "vertical",
          "spacing": "sm",
          "contents": [
            {
              "type": "text",
              "wrap": true,
              "weight": "bold",
              "size": "xl",
              "text": "通知設定完了"
            },
            {
              "type": "text",
              "text": "#{@name}さんの情報について",
              "size": "md",
              "flex": 12
            },
            {
              "type": "text",
              "text": "通知をお送りします。",
              "size": "md",
              "flex": 12
            }
          ]
        }
      }
    }
    client.push_message(user_id, message)
  end
```

offの方は省略します。

以上の流れで実装しました。