PHP實作Line bot

因工作需要,接觸到有趣的Line Bot,以文章記錄一下基礎開發流程

 

註冊Line API

首先,進入Line開發者介面(https://developers.line.biz/en/),以Line的帳號登入

登入後,即可看到Porvider list,或是透過網址進入(https://developers.line.biz/console/),接著新增一組Porvider

 

Porvider建立沒什麼難度,只要設定好名稱即可

 

完成後,進入Porvider,並選擇建立一個Message API的Channel

 

新增Channel也沒有什麼難度,名稱所屬產業填一填即可

 

主要需要注意的是,如果選擇Developer方案的話,好友上限最多50人,且未來是無法轉成其他方案的,如果有需要用於正式環境的話,建議直接選擇Free方案,方案限制可以參考這裡 https://at.line.me/tw/plan

 

Line更新了API方案,原本的Developer trial 方案沒了,2019/4/18後申請的bot 直接升級成”輕用量”方案,每個月上限500則推播,變更內容懶人包一下:

  1. 過去只能 bot 或 LINE@ 二選一,現在合併了所以 bot 也能到後台一對一聊天或群發,可以在後台切換
  2. LINE 計價方式由原本的好友&訊息計價,改為訊息計價(好友無上限)
  3. Multicast 、Push 都會被算到訊息額度(Reply不會),如果過去的 bot 訊息量比較大有機會超標

詳細資訊可以參考這裡 http://at-blog.line.me/tw/archives/LINEOA2.0FAQ.html

 

完成後,清單上即可看到剛剛新增的Channel

 

接著點選Channel,進入詳細的設定畫面,有幾個地方要特別設定:

1. Channel access token (long-lived)
這裡開啟token,並設定token的使用時限,設定0為永久。取得token等於取得權限,token是絕對不可以公布給其他人的!

2. 開啟 Use Webhooks

3. 設定 Webhook URL並驗證
這裡輸入一個URL,指的是當Bot收到訊息後,Bot會通知哪隻網頁程式,網域必須是https開頭

 

一切就緒後即可轉戰PHP的部分

 

撰寫PHP

我們可以使用官方SDK來開發,或者自己實作訊息處理;因為僅實作簡單的訊息回覆,本文使用自己處理訊息的方式實作

首先我們建立Webhooks觸發的程式,取名為CallBack.php,程式碼如下

<?php 

//設定Token 
$ChannelSecret = 'ChannelSecret'; 
$ChannelAccessToken = 'ChannelAccessToken'; 

//讀取資訊 
$HttpRequestBody = file_get_contents('php://input'); 
$HeaderSignature = $_SERVER['HTTP_X_LINE_SIGNATURE']; 

//驗證來源是否是LINE官方伺服器 
$Hash = hash_hmac('sha256', $HttpRequestBody, $ChannelSecret, true); 
$HashSignature = base64_encode($Hash); 
if($HashSignature != $HeaderSignature) 
{ 
    die('hash error!'); 
} 

//輸出 
file_put_contents('log.txt', $HttpRequestBody); 

?>

其中ChannelSecret、ChannelAccessToken請修改為自己的ChannelSecret與AccessToken,兩項資訊都在剛剛的Channel Setting頁面中

首先我們使用file_get_contents()的方式取得訊息內容,接著使用sha256與base64編碼並驗證是否與header中HTTP_X_LINE_SIGNATURE的內容相符(驗證資料是否來自Line官方伺服器),最後我們將收到的訊息輸出至log.txt

完成後我們掃描QR code加機器人好友

 

沒意外的話log.txt會存放Line伺服器發送過來的follow事件

{
  "events" : [
    {
      "type" : "follow",
      "replyToken" : "9cfa97373e624b4d85ee89faecee1dfd",
      "source" : {
        "userId" : "U97f699d56851a7bf725c1283772106bd",
        "type" : "user"
      },
      "timestamp" : 1551514544448
    }
  ],
  "destination" : "U9206f4b863b5497478a9752dac21ce99"
}

 

接著我們傳送訊息給Bot

 

我們也能在log.txt看到Message事件,且得到使用者的訊息內容

{
  "events":[
    {
      "type":"message",
      "replyToken":"121a260a7d224d14949d9aa9c4f6aa95",
      "source":{
        "userId":"U97f699d56851a7bf725c1283772106bd",
        "type":"user"
      },
      "timestamp":1551515007430,
      "message":{
        "type":"text",
        "id":"9443953102066",
        "text":"你好"
      }
    }
  ], 
  "destination":"U9206f4b863b5497478a9752dac21ce99"
} 

 

由官方文件所示,事件訊息都會以JSON格式發送,我們只需要解析其內容即可實作相對應的反應

值得要注意的是發送過來的訊息當中,事件(events)是以陣列的方式傳送,不一定只有一個事件,所以我們必須使用迴圈逐一反應

每個事件都會取得一個replyToken,我們可以利用replyToken回覆訊息給該使用者

 

現在已經能正確接收訊息了,接下來實作回覆訊息,我們稍微修改一下程式碼


//解析
$DataBody=json_decode($HttpRequestBody, true);

//逐一執行事件
foreach($DataBody['events'] as $Event)
{
    //當bot收到任何訊息
    if($Event['type'] == 'message')
    {
        $Payload = [
            'replyToken' => $Event['replyToken'],
            'messages' => [
                [
                    'type' => 'text',
                    'text' => '我收到你的訊息了'
                ]
            ]
        ];

        // 傳送訊息
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, 'https://api.line.me/v2/bot/message/reply');
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($Payload));
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Content-Type: application/json',
            'Authorization: Bearer ' . $ChannelAccessToken
        ]);
        $Result = curl_exec($ch);
        curl_close($ch);
    }
}

 

發送訊息就會收到我們設定的「我收到你的訊息了」內容,由於我們沒有關閉預設罐頭訊息,所以使用者會看到罐頭,可以去Channel Setting頁面中關閉

 

完整程式碼如下:

<?php 

//設定Token 
$ChannelSecret = 'ChannelSecret'; 
$ChannelAccessToken = 'ChannelAccessToken'; 

//讀取資訊 
$HttpRequestBody = file_get_contents('php://input'); 
$HeaderSignature = $_SERVER['HTTP_X_LINE_SIGNATURE']; 

//驗證來源是否是LINE官方伺服器 
$Hash = hash_hmac('sha256', $HttpRequestBody, $ChannelSecret, true); 
$HashSignature = base64_encode($Hash); 
if($HashSignature != $HeaderSignature) 
{ 
    die('hash error!'); 
} 

//解析 
$DataBody=json_decode($HttpRequestBody, true); 

//逐一執行事件 
foreach($DataBody['events'] as $Event) 
{ 
    //當bot收到任何訊息 
    if($Event['type'] == 'message') 
    { 
        $Payload = [ 
            'replyToken' => $Event['replyToken'],
            'messages' => [
                [
                    'type' => 'text',
                    'text' => '我收到你的訊息了'
                ]
            ]
        ];

        // 傳送訊息
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, 'https://api.line.me/v2/bot/message/reply');
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($Payload));
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Content-Type: application/json',
            'Authorization: Bearer ' . $ChannelAccessToken
        ]);
        $Result = curl_exec($ch);
        curl_close($ch);
    }
}

?>

 

本文僅實作簡單的回覆,未來還可以依照訊息內容或關鍵字作不同的反應

 

參考資料


2 則迴響

  • Denny

    2019/05/29

    謝謝您的資訊,但我用您的這段 code
    使用 postman 工具來模擬 post request 可以取到 log
    但在 Line 上發出訊息時,一直沒有寫入 log

    有什麼我該注意的地方嗎,謝謝

    $ChannelSecret = ‘XXXXX’;
    $ChannelAccessToken = ‘XXXXXX’;
    $HttpRequestBody = file_get_contents(‘php://input’);
    file_put_contents(‘log.txt’, $HttpRequestBody);

    回復
    • wwwang

      2019/06/15

      您好,若希望取得Line bot收到的訊息內容,必須綁定Line API的Webhook,且必須是可以被Line連接的公開對象(固定IP或是網址),您可以先嘗試用postman連接看看Webhook的URL是否可以被外連

Denny 發表迴響 取消回覆