公式LINE(Messaging API)を友達追加したときにLINEIDをDBに保存する方法　Rails sorcery

## はじめに
プログラミングスクール｢RUNTEQ｣を卒業し、
未経験にてエンジニア転職を目標に活動しています。
未経験であり、内容が間違っている可能性があります。気になる点がありましたら連絡ください。
また、記事で紹介しているやり方は私自身で考えた方法であるため、他に良いやり方があると思いますので、あくまで参考までにいただけたらと思います。


今回、個人開発ポートフォリオとして｢ぶさかわフォト｣を開発しました。
ぶさかわフォトについては下記記事を見て頂けたらうれしいです！
https://busakawa.com


ぶさかわフォトではユーザーをフォローし、フォローしたユーザーが投稿したときにLINE通知が来るように設定しました。
開発の中で、公式LINEを友達追加したときにLINEIDをDBへ保存する方法に苦戦したため、記事を書きたいと思います。

## 使用技術
| カテゴリ | 技術 |
| ---- | ---- |
| フレームワーク | Rails7.1.3 |
| バックエンド | Ruby3.2.3 |

## DB
・Usersテーブルの"line_user_id"カラムに保存する
・LineBotTokensテーブルには"line_user_id"と"line_user_id_token"のカラムを準備し、一時的に保存する


## 前提として
1.sorceryにてアカウント作成後、ログインしている状態である。

※LINEにてログインしている場合は、sorceryの設定にてDB上にLINEIDを保存することは容易である。今回はsorceryでアカウントを作成後、公式LINEを友だち追加し、LINEIDをDBに保存する方法です。

## 手順
1.公式LINEのアカウント登録ボタンを作成
2.公式LINEを追加することで発生するwebhookイベントオブジェクトの処理
3.webhookイベント後の処理

## 1.公式LINEのアカウント登録ボタンを作成

先にコードを貼り付けます。
```
<%= link_to "https://lin.ee/ldrvLk4" do %>
  <img src="https://scdn.line-apps.com/n/line_add_friends/btn/ja.png" alt="友だち追加" height="36" border="0">
<% end %>
```
リンクにつきましては、LINE Official Accountの友達追加ガイドを参照してください。

[![Image from Gyazo](https://i.gyazo.com/55fe5f2a26b5a11dff8c31563c45a90f.png)](https://gyazo.com/55fe5f2a26b5a11dff8c31563c45a90f)
リンクを開くとこのようになり、
[![Image from Gyazo](https://i.gyazo.com/face977d1c0027107b162653330457d1.png)]
公式LINEを表示できます。
(今回はテストの為友だちに登録済みです。となっています)

## (2).公式LINEを追加することで発生するwebhookイベントオブジェクトの処理
ここからが今回の記事のキーポイントとなります。

### 前知識
・そもそもwebhookイベントとは？となると思いますので、下記URLを共有いたします。
https://developers.line.biz/ja/docs/messaging-api/receiving-messages/
　一言で言えば、LINE公式アカウントを友だち追加したときに発生するHTTP POSTリクエストということです。

・webhookイベントオブジェクトはJSON形式で送られてきます。
https://developers.line.biz/ja/docs/messaging-api/receiving-messages/#webhook-event-in-one-on-one-talk-or-group-chat

では、説明してきます。
先にコードを共有いたします。
```
def webhook
    body = request.body.read
    events = client.parse_events_from(body)
    events.each do |event|
      case event
      when Line::Bot::Event::Follow
        user_id = event['source']['userId']
        token = SecureRandom.alphanumeric(15)
        LineBotToken.create(line_user_id: user_id, line_user_id_token: token)
        message = [
          {type: "text", text: "下記URLよりLINE認証を行ってください"},
          {type: "text", text: "https://busakawa.com/line_bots/new?state=#{token}&openExternalBrowser=1"}
        ]
        client.push_message(user_id,message)
      end
    end
```
webhookイベントが発生すると、ルーティングにて、webhookアクションを発生させるようにしています。(routes設定は省略いたします)
webhookイベントでは、
1.Followイベントの場合、userIDを取得し、変数へ。
2.SecureRandomnにて安全なランダム値を作成。
  SecureRandomnについての詳細は下記参照
  https://docs.ruby-lang.org/ja/latest/class/SecureRandom.html
3.messageの設定を行い公式LINE追加時にトークを送信する。
といった流れになっています。
・&openExternalBrowser=1を付けることで、LINEトークでのURLをブラウザで開くようにしています。　PC(Windows)の場合不要ですが、スマホ(IPhone)の場合はLINE内でURLを開いてしまいます。(Macbook　Androidは分かりません)

### なぜこのような処理にしたか
実施したこと
1. webhookアクションにてcurrent_userを使う
最初はcurrent_userを使いました。
ログインしている状態で、公式LINEを追加することでUsersテーブルのline_user_idに保存するようにしましたが、ログにてcurrent_userが引っかかりエラーとなりました。
2.QRコード作成 (rqrcode)
Usersテーブルに新たに、"ine_user_id_token"カラムを追加し、User作成時にSecureRandomnにてランダムな値を保存しました。
そのline_user_id_tokenの値をQRコードに埋め込もうとしましたが、LINEそのものが送られてきたデーターを取得し、webhookにて返すということは出来ませんでした。
↓
そこで、webhookイベントにてSecureRandomnにてランダムな値とLINEIDを別のテーブルに保存し、トーク画面のURLからLINEIDを取得する処理にすることにしました。

## (3)webhookイベント後の処理
URL先はLineBotsのnewアクションとしていますので、
```
  def new
    if current_user.line_user_id.present?
      flash[:notice] = "LINE連携が完了しました"
      redirect_to posts_path
    else
      @line_user_id_token = params[:state]
    end
  end
```
このように書きました。params[:state]にてSecureRandomnのランダムな値を取得します。
```
line_bots/new.html.erb

<div class="container">
  <div class="row">
    <div class="text-center p-3 m-3">
      <%= link_to "LINE連携する", line_bots_path(line_user_id_token:  @line_user_id_token), data: { turbo_method: :post }, class: "btn btn-outline-success" %>
    </div>
  </div>
</div>
```
line_bots/new.html.erbにてボタンを作成し、
```
def create
    @line_bot_token = LineBotToken.find_by(line_user_id_token: params[:line_user_id_token])
    current_user.update(line_user_id: @line_bot_token.line_user_id)
    @line_bot_token.destroy
    flash[:notice] = "LINE連携が完了しました"
    redirect_to posts_path
  end
```
createアクションにてUsersテーブルのline_user_idへ保存し、LineBotTokenのデータは不要の為削除します。

以上になります。