Laravel透過Line Message API實做echo bot
建立Provider
點選New Provider
接著選擇Create Message API Channel
設定好一些Bot的基本資料
像是名稱、大頭貼、Channel名稱、敘述等
設定Webhook
因為Webhook URL必須是對外網址而且是HTTPS
如果在local開發可使用ngrok
將Line Developer Console的Channel Access Token及Channel Secret保留下來
先測試Webhook是否能通過驗證
可以先開一支api回傳http status code 200
並從Line Developer Console上點選Webhook Verify按鈕
確認此api是否能通
另外可以先log出請求的格式先看個大概
<?php
namespace Modules\Line\Http\Controllers;
use Illuminate\Http\Request;
use Modules\Base\Http\Controllers\BaseController;
class LineHookController extends BaseController
{
public function hooks(Request $request)
{
$params = $request->all();
logger(json_encode($params, JSON_UNESCAPED_UNICODE));
return response('hello world', 200);
}
}
log出來的請求參數
可以看到就是長的像官方文件的Webhook event object這樣
驗證請求
到目前為止
這隻接收webhook的api只要成功發佈出去
已經人讓任何人來call了
因此我們必須依照文件的signature validation建議來驗證所有來的請求
上面也有各種語言的範例
不過因為我是使用Line官方的PHP SDK line-bot-sdk-php
SDK驗證的部份都包好而且還有範例
所以可以很簡單的來做驗證
<?php
namespace Modules\Line\Http\Controllers;
use Illuminate\Http\Request;
use LINE\LINEBot;
use Modules\Base\Http\Controllers\BaseController;
use LINE\LINEBot\HTTPClient\CurlHTTPClient;
use Modules\Line\Constant\LineHookHttpResponse;
class LineHookController extends BaseController
{
public function hooks(Request $request)
{
$httpClient = new CurlHTTPClient(env('LINE_CHANNEL_ACCESS_TOKEN'));
$bot = new LINEBot($httpClient, [
'channelSecret' => env('LINE_CHANNEL_SECRET')
]);
$signature = $request->header(LINEBot\Constant\HTTPHeader::LINE_SIGNATURE);
if(!$signature) {
return $this->http403(LineHookHttpResponse::SIGNATURE_INVALID);
}
try {
$bot->parseEventRequest($request->getContent(), $signature);
} catch (LINEBot\Exception\InvalidSignatureException $exception) {
return $this->http403(LineHookHttpResponse::SIGNATURE_INVALID);
} catch (LINEBot\Exception\InvalidEventRequestException $exception) {
return $this->http403(LineHookHttpResponse::EVENTS_INVALID);
}
$events = $request->events;
foreach ($events as $event) {
logger(json_encode($event));
}
return $this->http200('anchor');
}
}
驗證這段寫完之後
記得要再點選Webhook Verify按鈕確認Code是否有問題
實做接收訊息並回覆功能
到目前為止我們已經能夠安全的接收到訊息了
因此我們可以將Webhook event object物件加以利用
這時候可以透過Message event的replyToken
丟給透過的SDK建立出來的$bot物件來發送訊息
先設定一個假設情境
當我們收到訊息內容有"台灣"的時候
系統要自動回覆"南波萬"
<?php
namespace Modules\Line\Http\Controllers;
use Illuminate\Http\Request;
use LINE\LINEBot;
use Modules\Base\Http\Controllers\BaseController;
use LINE\LINEBot\HTTPClient\CurlHTTPClient;
use Modules\Line\Constant\LineHookHttpResponse;
class LineHookController extends BaseController
{
public function hooks(Request $request)
{
$httpClient = new CurlHTTPClient(env('LINE_CHANNEL_ACCESS_TOKEN'));
$bot = new LINEBot($httpClient, [
'channelSecret' => env('LINE_CHANNEL_SECRET')
]);
$signature = $request->header(LINEBot\Constant\HTTPHeader::LINE_SIGNATURE);
if(!$signature) {
return $this->http403(LineHookHttpResponse::SIGNATURE_INVALID);
}
try {
$bot->parseEventRequest($request->getContent(), $signature);
} catch (LINEBot\Exception\InvalidSignatureException $exception) {
return $this->http403(LineHookHttpResponse::SIGNATURE_INVALID);
} catch (LINEBot\Exception\InvalidEventRequestException $exception) {
return $this->http403(LineHookHttpResponse::EVENTS_INVALID);
}
$events = $request->events;
foreach ($events as $event) {
// 不是訊息的event先不處理
if($event['type'] != 'message') continue;
$messageType = $event['message']['type'];
$message = $event['message']['text'];
// 不是文字訊息的類型先不處理
if($messageType != 'text') continue;
$match = preg_match('/台灣|臺灣|Taiwan|taiwan/', $message);
if(!$match) continue;
$response = $bot->replyText($event['replyToken'], '南波萬');
if ($response->isSucceeded()) {
logger('reply successfully');
return;
}
}
return $this->http200('anchor');
}
}
測試一下
上面這段示範先用SDK的簡單的文字訊息來回應($bot->replyText)
如果要回覆其他類型的訊息(可參考文件的Message Object)
像是影片、地標、貼圖
透過SDK的各種MessageBuilder也能做到
做更複雜的訊息回覆
如果透過上述的一般MessageBuilder Class
一次只能回覆一筆訊息
當你回覆第二次會發現Line Server告訴你replayToken已經失效
因此如果我們希望系統能夠一次回覆多個訊息
可使用LINEBot\MessageBuilder\MultiMessageBuilder這個Class
透過它的add Method可以一次塞入多個MessageBuilder Class
最後將再將MutliMessageBuilder Class丟給$bot->replyMessagemethod
<?php
namespace Modules\Line\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use LINE\LINEBot;
use Modules\Base\Http\Controllers\BaseController;
use LINE\LINEBot\HTTPClient\CurlHTTPClient;
use Modules\Line\Constant\LineHookHttpResponse;
class LineHookController extends BaseController
{
public function hooks(Request $request)
{
$httpClient = new CurlHTTPClient(env('LINE_CHANNEL_ACCESS_TOKEN'));
$bot = new LINEBot($httpClient, [
'channelSecret' => env('LINE_CHANNEL_SECRET')
]);
$signature = $request->header(LINEBot\Constant\HTTPHeader::LINE_SIGNATURE);
if(!$signature) {
return $this->http403(LineHookHttpResponse::SIGNATURE_INVALID);
}
try {
$bot->parseEventRequest($request->getContent(), $signature);
} catch (LINEBot\Exception\InvalidSignatureException $exception) {
return $this->http403(LineHookHttpResponse::SIGNATURE_INVALID);
} catch (LINEBot\Exception\InvalidEventRequestException $exception) {
return $this->http403(LineHookHttpResponse::EVENTS_INVALID);
}
$events = $request->events;
foreach ($events as $event) {
// 不是訊息的event先不處理
if($event['type'] != 'message') continue;
$messageType = $event['message']['type'];
$message = $event['message']['text'];
// 不是文字訊息的類型先不處理
if($messageType != 'text') continue;
$match = preg_match('/台灣|臺灣|Taiwan|taiwan/', $message);
if(!$match) continue;
$messageBuilder = new \LINE\LINEBot\MessageBuilder\MultiMessageBuilder();
// 回覆文字
$text = new LINEBot\MessageBuilder\TextMessageBuilder('南波萬');
$messageBuilder->add($text);
// 回覆貼圖
$sticker = new LINEBot\MessageBuilder\StickerMessageBuilder('11537', '52002734');
$messageBuilder->add($sticker);
// 回覆地標
$location = new LINEBot\MessageBuilder\LocationMessageBuilder('台灣南波萬', '哇呆灣郎啦', '24.147666', '120.673552');
$messageBuilder->add($location);
// 回覆相片訊息
$image = new LINEBot\MessageBuilder\ImageMessageBuilder(
'https://foobar.png',
'https://foobar.tiny.png'
);
$messageBuilder->add($image);
$response = $bot->replyMessage($event['replyToken'], $messageBuilder);
if ($response->isSucceeded()) {
logger('reply sticker successfully');
}
else {
Log::warning($response->getRawBody());
Log::warning('reply sticker failure');
}
}
return $this->http200('anchor');
}
}
測試結果