LINE BOT API + Laravel5(PHP)でのリッチメッセージ送信方法

# 概要
Laravel5でLINE BOTのリッチメッセージを送信する方法をまとめた。

※[BOT API Trial Account](https://business.line.me/ja/products/4/introduction)の取得方法や設定方法については、
既に沢山記事があるのでそちらを参照のこと。
（個人的には[こちら](http://qiita.com/srea/items/58ba0f7d870a6ee3da2a)の記事を参考にさせていただきました）


# 使うもの
* Laravel5
* [line-bot-sdk-php](https://github.com/line/line-bot-sdk-php)
    * BOTのメッセージ送信全般の実装が楽になる。特にリッチメッセージのマークアップ部分。
* [Intervention Image](http://image.intervention.io/)
    * 画像の生成・加工が簡単に行えるライブラリ。今回はリッチメッセージ全体画像の生成に利用。

# リッチメッセージとは

[line-bot-api-v1ドキュメント](http://line.github.io/line-bot-api-doc/ja/richmessage.html)にある通り、全体が画像となっているメッセージ。
画像の任意領域にタップ時のアクション（webリンク or メッセージ送信）を設定できる。

今回は、下記のようなリッチメッセージを送信する方法の例を紹介。
（実際には画像中の文字はもっと小さくなります。。）

![Slack for iOS Upload-2.png.png](https://qiita-image-store.s3.amazonaws.com/0/57617/724d2990-4bb0-5cf8-3e49-06caee6dde40.png)
・ユーザーが送信したメッセージを白領域に書く。
・白領域をタップするとwebページが開く（webアクション）。
・黒領域をタップすると'user message'というメッセージをユーザーに送信させる（sendMessageアクション）。


# リッチメッセージのデータの流れ

リッチメッセージでは下記の図のようなリクエストが発生する。

![Qiita投稿用2 (1).png](https://qiita-image-store.s3.amazonaws.com/0/57617/90c26e84-579b-6d24-ea20-16eb5cf1c127.png)

今回は便宜上、3の画像リクエストの度に画像を生成・出力する構成で実装。
（本運用するなら1の時点で画像生成・ストレージ保存、とか、、？）


# line-bot-sdk-phpの導入

READMEに書いてある通り、下記コマンドでLaravelにインストール。

`composer require linecorp/line-bot-sdk`

# Intervention Imageの導入

Installationに書いてある通り、下記コマンドでインストール。

`composer require intervention/image`
`php artisan vendor:publish --provider="Intervention\Image\ImageServiceProviderLaravel5"`

app.phpにも下記を追記。

```php:config/app.php
'providers' => array(
    // ...
    'Intervention\Image\ImageServiceProvider',
)

...

'aliases' => array(
    // ...
    'Image'     => 'Intervention\Image\Facades\Image',
)
```

# SDKを利用してリッチメッセージ送信
line-bot-sdk-phpのREADME[「LINEBot#sendRichMessage」](https://github.com/line/line-bot-sdk-php#linebotsendrichmessagemid-imageurl-alttext-markup-markup)を参考にしながら実装。

```php:routes.php
// LINEから飛んでくるリクエストはpost
Route::post('richmessage', 'RichMessageController@index');
```

```php:RichMessageController.php
use LINE\LINEBot\HTTPClient\GuzzleHTTPClient;
use LINE\LINEBot\Message\RichMessage\Markup;
use Request;

class RichMessageController extends Controller
{
    public function index()
    {
        // ユーザーから送信された情報の中から"from"を取得
        $request = Request::all();
        $from = $request['result'][0]['content']['from'];

        // チャネル等の設定
        $config = [
                'channelId' => 'アカウント管理画面から取得',
                'channelSecret' => 'アカウント管理画面から取得',
    	        'channelMid' => 'アカウント管理画面から取得',
        ];
        $bot = new LINEBot($config, new GuzzleHTTPClient($config));

        // リッチメッセージのマークアップ
        $markup = (new Markup(1040))
            // リッチメッセージの上半分がタップされた時はexample.comを開く
            ->setAction('OpenExampleCom', 'openexamplecom', 'https://example.com/')
            ->addListener('OpenExampleCom', 0, 0, 1040, 520)
            // リッチメッセージの下半分がタップされた時はユーザーに'user message'というメッセージを送信させる
            ->setAction('GetUserToSendMessage', 'user message', '', 'sendMessage')
            ->addListener('GetUserToSendMessage', 0, 520, 1040, 1040);

        // 画像のベースURLを動的に生成
        $img_base_url = 'https://myapp.com/img/' . urlencode($request['result'][0]['content']['text']);
        // リッチメッセージ送信
        $bot->sendRichMessage($from, $img_base_url, 'Alt text', $markup);
    }
}

```

※ユーザーにメッセージを送信させる場合、setActionの第4引数に'sendMessage'を指定するが、
webリンクの場合はデフォルト値で'web'が入るため指定しなくてOK.

詳細はSDKの下記ソースを参照。
https://github.com/line/line-bot-sdk-php/blob/master/src/LINEBot/Message/RichMessage/Markup.php#L80

# リッチメッセージ全体画像を動的に生成

sendRichMessage第2引数はリッチメッセージ全体画像のベースURLであり、
今回の場合、LINE側から下記のようにリクエストされる。

https://myapp.com/img/{ユーザーが送信したメッセージ}/1040 →アプリ側で幅1040pxの画像を出力
https://myapp.com/img/{ユーザーが送信したメッセージ}/700 →アプリ側で幅700pxの画像を出力
（他サイズも同様）
※ベースURL = https://myapp.com/img/{ユーザーが送信したメッセージ}

参考：[line-bot-api-v1ドキュメント](http://line.github.io/line-bot-api-doc/ja/richmessage.html)
>`全体画像のURL = <ベースURL>/<横幅(1040|700|460|300|240)>`


実装は下記のような感じ。

```php:routes.php
// URL末尾が数字
Route::get('img/{message}/{img_width}', 'ImgController@index')->where('img_width', '[0-9]+');
```

```php:ImageController.php
use Intervention\Image\ImageManagerStatic as Image;

class ImgController extends Controller
{
    public function index($message, $img_width)
    {
        // ベースの白い全体画像を生成（今回は正方形とする）
        $base_img = Image::canvas($img_width, $img_width, '#ffffff');
        // 全体画像にユーザーが送信したメッセージを挿入
        $base_img->text($message, $img_width/2, $img_width/4, function($font) {
                $font->color('#000000');
                $font->align('center');
        });

        // 黒領域の画像を生成
        $black_img = Image::canvas($img_width, $img_width / 2, '#000000');
        // 黒領域の画像に'SendMessage'という文字を挿入
        $black_img->text('SendMessage', $img_width/2, $img_width/4, function($font) {
                $font->color('#ffffff');
                $font->align('center');
        });
        // 全体画像の下部中央に貼り付け
        $base_img->insert($black_img, 'bottom');

        // 全体画像を出力
        return $base_img->response();
    }
}

```

※Intervention Imageのcanvas()やtext()の使い方は[ドキュメント](http://image.intervention.io/)参照。

# 動作確認
ここまで実装したら動くはず。
LINEアプリからBOTに適当なメッセージを送信してみて、リッチメッセージが返信されれば成功！

# 注意事項
* LINE側でリッチメッセージ全体画像をキャッシュしている模様（URL単位？）。
    * キャッシュクリア方法は調査中。。
* 全体的にメッセージはURLエンコードしないと文字化けする模様。
* LaravelのMiddlewareのVerifyCsrfTokenを外さないと、LINEからpostされた時に"500 Internal Server Error"となってしまう。

```php:app/Http/Kernel.php
protected $middleware = [
    // ...
    'App\Http\Middleware\VerifyCsrfToken', // ←この行は削除
];
```

# 参考
https://speakerdeck.com/yusukebe/sumahoshi-dai-falsebotapurifalsetukurifang
（かなり参考にさせていただきました）