LINE WORKS Bot メッセージ送受信機能（Laravel, Line works API 2.0)

## 始めに
LINE WORKS Botチャット機能とwebアプリケーションを連携し、
webアプリケーション側からの送信機能、コールバック値を取得する機能

機能としては、大きく2つ
1、メッセージ通知（送信）
　　　チャットボットを利用しLINE WORKS上のトークルームにメッセージ送信（ボタン付き）
2、コールバック取得
　　　トークルームのチャット画面にて、送信されたボタン押下後に、
　　　値がwebアプリケーション側にコールバックされる

## 環境
| 開発環境    |バージョン |
|-----------|------------|
| __LINE WORKS API__ | 2.0 |
| __Laravel__ | 8.60.0 |
| __PHP__     | 7.3.10  |

## 初期設定
1、LINE WORKS Developer consoleでアプリを作成する
2、アプリの OAuth Scopes を bot に設定する
3、Bot を作成する
4、Bot の API Interface を API 2.0に設定する
5、Bot の Callback URL を追加しておく（Botに送信可能なタイプ：テキスト）
6、チャット画面でトークルームを作成する
7、トークルームに 2、で作成した Bot を招待する

## メッセージ通知に必要な情報を取得しておく
パラメータに必要な情報（5つ）
 ( ↓ Developer consoleのアプリ内 ↓ )
　・Client ID      
　・Client Secret
　・Service Account
　・Private Key
 ( ↓ Developer consoleのBot内 ↓ ) 
　・bot ID
( ↓  チャット画面の指定したいトークルームから取得可 ↓ ) 
　・channel ID

取得方法の参考URL
　https://qiita.com/kunihiros/items/54853e910ed1dff9ebdc

## メッセージ通知（送信）

```PHP:LineWorksController.php

    // LINE WORKSにメッセージ送信
    public function sendLineWorks(Request $request)
    {
        Log::debug(__FUNCTION__)

        try {

            // 初期設定
            $client_id          = 'Client ID';
            $client_secret      = 'Client Secret';
            $service_account_id = 'Service Account';
            $private_key        = 'Private Key'
                    // ------------------------------------------------------------------------
                                    <<< EOM
                                    -----BEGIN PRIVATE KEY-----
                                    // ... 略
                                    -----END PRIVATE KEY-----
                                    EOM;
                                    // ↑ Laravelの場合は、"<<< EOM", "EOM" をつける必要があった
                                    //   生PHの場合は、"<<< EOM", "EOM" または、ダブルクオーテーション
                                    //   keyファイルを直接読み込むことも可能
                    // ------------------------------------------------------------------------
            $bot_id              = "bot ID";
            $channel_id          = "channel ID";

            // トークンを取得する
            $token = $this->service->lw_get_accesstoken($client_id, $client_secret, $service_account_id, $private_key);

            // トークンが存在しない時
            if(!$token) {
                throw new Exception('トークンを取得できませんでした。');
            }

            // webhookURLを作成する（LINE WORKS トークルーム向け）
            $user_message_api = 'https://www.worksapis.com/v1.0/bots/'.$bot_id.'/channels/'.urlencode($channel_id).'/messages'; // チャンネル用

            // ヘッダー情報を配列に格納する
            $headers = ['Content-Type'  => 'application/json; charset=UTF-8',
                        'Authorization' => 'Bearer '.$token->access_token
                        ];

            // ペイロード情報を配列に格納する
            $payload = [
                "content"=> [
                    "type"=> "button_template",
                    "contentText"=> "あなたは、どちらに旅行したい？",
                    "actions"=> [[
                        // ボタン（上部）
                        "type"=> "message"
                        "label"=> "北海道"
                        ], [
                        // ボタン（下部）
                        "type"=> "message",
                        "label"=> "沖縄"
                    ]]
                ]
            ];

            // HTTPリクエストを送信する
            $response = \Http::withHeaders($headers)->post($user_message_api, $payload);

            // レスポンスが成功した時
            if($response->successful()) {
                // 送信成功
                Log::error(get_class($this).':'.__FUNCTION__);

            // レスポンスが失敗した時
            } else {
                // エラー処理
                Log::error(get_class($this).':'.__FUNCTION__);
            }

        } catch (Exception $e) {

            // エラー処理
            Log::error(get_class($this).':'.__FUNCTION__);
            Log::error($e->getMessage());
        }
    }
```

```PHP:LineWorksService.php
    use Firebase\JWT\JWT;

    // アクセストークン取得
    public function lw_get_accesstoken($client_id, $client_secret, $service_account_id, $private_key)
    {
        // 初期設定
        $lw_auth_url = "https://auth.worksmobile.com/oauth2/v2.0/token";

        // トークンを取得する
        $token = $this->jwt_encode($client_id, $service_account_id, $private_key);

        // ヘッダー情報を配列に格納する
        $headers = ['Content-Type' => 'application/x-www-form-urlencoded'];

        $params = array(
            'assertion'     => $token,
            'grant_type'    => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
            'client_id'     => $client_id,
            'client_secret' => $client_secret,
            'scope'         => 'bot',
        );

        try {

            $client = new \GuzzleHttp\Client;

            // HTTPリクエストを送信する
            // レスポンスを取得する
            $response = $client->post($lw_auth_url, [$headers, 'form_params' => $params]);

            // ステータスコードを取得する
            $status_code = $response->getStatusCode();

            // 通信が成功した時
            if($status_code == 200) {

                return json_decode($response->getBody());

            // 通信が失敗した時
            } else {
                // エラー処理
                return false;
            }
        }
        catch (\Exception $e) {
            // エラー処理
            Log::error(get_class($this).':'.__FUNCTION__);
            Log::error($e->getMessage());
        }
    }

    // エンコード処理
    private function jwt_encode($client_id, $service_account_id, $private_key)
    {
        try{
            $payload = array(
                'iss' => $client_id,
                'sub' => $service_account_id,
                'iat' => time(),
                'exp' => time() + 3600
            );

            // エンコード
            $encodedInfo = JWT::encode($payload, $private_key,'RS256');

            // エンコードされた値を返す
            return $encodedInfo;

        } catch (\Exception $e) {
            // ログ出力
            Log::error(get_class($this).':'.__FUNCTION__);
            Log::error($e->getMessage());
        }
    }
```

firebase/php-jwtがないよ、みたいなエラーが出てきたので、下記のコマンドで解決。
 （JWT のライブラリとして firebase/php-jwt がLaravelに入っていない場合）
```
composer require firebase/php-jwt
```

## コールバック取得

```PHP:LineWorksCallbackController.php

    // LINE WORKS からのコールバック値を取得
    public function update(Request $request)
    {
        Log::debug(__FUNCTION__);

        $callbackInfo = $request->all();
        
        Log::debug($callbackInfo);
    }
```

コールバックが取得できなくてはまりました。
CSRFトークンのチェックが原因でした。💦
VerifyCsrfToken.phpにCSRF保護から除外したいURIを記載するだけ！

参考URL　LaravelでCSRF保護からURIを除外する
https://curecode.jp/tech/laravel-csrf-check-except-with-specified-referer-uri/


## 終わりに
参考URLの作成者様方に助けられて、本当に感謝です。ありがとうございます。
ご指摘、ご意見等ありましたら、メッセージいただけますとありがたいです。
よろしくお願いします。

## LINE WORKS 参考資料まとめ

   ・公式ドキュメント
    　https://developers.worksmobile.com/jp/document?lang=ja

   ・API認証情報の取得方法まとめ
    　https://qiita.com/kunihiros/items/54853e910ed1dff9ebdc

   ・【Line works API 2.0】LaravelでBotからユーザーにメッセージを送信する
    　https://qiita.com/flatwhite/items/261b395adea70defe952