Laravel 如何清理快取

有時候更新了程式碼,但是沒辦法即時看到結果,這是由於該應用程式被快取所導致。這裡記錄了一些清理快取的方法

  1. 清理應用程式快取  :php artisan cache:clear
  1. 清理路由快取:php artisan route:clear
  1. 清理設定快取:php artisan config:clear
  1. 清理編譯的視圖文件:php artisan view:clear
 

Laravel 使用 env 函式讀取環境變數為 null 的問題

在 Laravel 專案中,如果執行了 php artisan config:cache 命令把配置檔案快取起來後,在使用 env 函式讀取環境變數的值,會變成 null,但是執行 php artisan config:clear,清除配置快取後,又可以讀取了,就覺得很奇怪

看了一下,得知在 Laravel 中,如果執行 php aritisan config:cache 命令後,Laravel 就會把 app/config 目錄下的所有配置檔案快取到 bootstrap/cache/config.php 裡面。正因為有了這個快取配置檔案,在其他地方使用 env 函式,就會讀取不到環境變數,所以返回 null.

接著看一下 Illuminate/Foundation/Bootstrap/DetectEnvironment.php 的這段程式碼:

 

public function bootstrap(Application $app) {
    if (! $app->configurationIsCached()) {
        $this->checkForSpecificEnvironmentFile($app);
        try {
            (new Dotenv($app->environmentPath(), $app->environmentFile()))->load();
        } catch (InvalidPathException $e) {
            //
        }
    }
}

 

這個方法在框架啟動後就會執行,這段程式碼說明了如果存在快取配置檔案,就不會去設定環境變數了

,配置都讀快取配置檔案,而不會再讀環境變數了。因此,如果做快取,一旦執行 php artisan config:cache 之後,env 函式就不起作用了

LINE 聊天機器人: Hello world!

前面講了這麼多,終於要進入重點了。現在來寫一個會回應的 LINE 聊天機器人吧!

無論你是透過 Laravel 安裝工具 的 laravel new 或是 composer create-project 的方式 都可以,反正就是新安裝一份 Laravel 專案吧!

在你安裝完 Laravel 後,首先需要做的事情就是設定一個隨機字串到應用程式金鑰。如果這金鑰沒有被設定的話,你的使用者 sessions 和其他的加密資料都會不安全,因此我們需要來設定一下應用程式金鑰。

假設你是透過 Composer 安裝 Laravel,這個金鑰有可能已經透過 key:generate 指令幫你設定完成,如果沒有的話,試著輸入 php artisan key:generate 產生應用程式金鑰吧

首先,我們來設定 `route` 的部分,讓 LINE 可以打入我們設定好的 webhook ,打開 routes/web.php

插入一行

Route::post('/callback', '[email protected]');

此時我們只要去 LINE Developer 後台的 webhooks 網址添加 /callback (如下圖),每當使用者跟機器人講話的時候,就會觸發 LineController 裡面的 webhook 這個方法

img

剛剛我們有提到 LineController 裡面的 webhook 方法,所以我們來建立一個,輸入 php artisan make:controller LineController ,此時 app/Http/Controllers/ 裡面會多了一新的 controller,我們在裡面增加一點東西,讓他可以接受 LINE 的訊息,然後回應

接著我們需要安裝 LINE 提供的 SDK ,所以下個指令 composer require linecorp/line-bot-sdk ,來安裝 line-bot-sdk ,安裝完成後就可以開始寫 code 囉!

我們先建立一個 app\Http\Controller\LineController

<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use LINE\LINEBot;
use LINE\LINEBot\Event\MessageEvent;
use LINE\LINEBot\HTTPClient\CurlHTTPClient;

class LineController extends Controller
{
    private $client;
    private $bot;
    private $channel_access_token;
    private $channel_secret;

    public function __construct()
    {
        $this->channel_access_token = env('CHANNEL_ACCESS_TOKEN');
        $this->channel_secret = env('CHANNEL_SECRET');

        $httpClient = new CurlHTTPClient($this->channel_access_token);
        $this->bot = new LINEBot($httpClient, ['channelSecret' => $this->channel_secret]);
        $this->client = $httpClient;
    }

    public function webhook(Request $request)
    {
        $bot = $this->bot;
        $signature = $request->header(\LINE\LINEBot\Constant\HTTPHeader::LINE_SIGNATURE);
        $body = $request->getContent();

        try
        {
            $events = $bot->parseEventRequest($body, $signature);
        }
        catch(\Exception $e)
        {
            Log::error($e->getMessage());
        }

        foreach ($events as $event)
        {
            $replyToken = $event->getReplyToken();
            if ($event instanceof MessageEvent)
            {
                $message_type = $event->getMessageType();
                $text = $event->getText();
                switch ($message_type)
                {
                    case 'text':
                        $bot->replyText($replyToken, 'Hello world!');
                    break;
                }
            }
        }
    }
}

 

先來講講 __construct 。這個其實很簡單,就是拿著你的 .env 裡面的 Channel secret 和 Channel access token 去建立一個 LINEBot 物件。

緊接著是 函式 webhook 中

$bot = $this->bot;
$signature = $request->header(\LINE\LINEBot\Constant\HTTPHeader::LINE_SIGNATURE);
$body = $request->getContent();

try {
    $events = $bot->parseEventRequest($body, $signature);
} catch (\Exception $e) {
    Log::error($e->getMessage());
}

 

我們先取除剛剛建構的 LINEBot 物件,然後去接收 $request ,接著 透過 try... catch 去看看這個 $request 能不能夠正確被處理,如果可以的話,就可以開始做事情啦!

接著是最後一段,也是最核心的部分

foreach ($events as $event) {
    $replyToken = $event->getReplyToken();
    if ($event instanceof MessageEvent) {
        $message_type = $event->getMessageType();
        $text = $event->getText();
        switch ($message_type) {
            case 'text':
            $bot->replyText($replyToken, 'Hello world!');
            break;
        }
    }
}

 

如果能夠正常處理請求,過來的東西會是一個陣列,裡頭有許許多多的 event ,而這個 event 裡頭有許多資訊供我們使用,首先我透過 $event->getReplyToken() 去取得 ReplyToken ,等等回覆給使用者的時候要用這個 token ,這組 token 存活時間非常短暫,所以我們接收到使用者的訊息,必須馬上把它用掉

接著我用 if ($event instanceof MessageEvent) 去確認事件類型是 MessageEvent 。至於這是什麼東西呢?先別急,之後我會介紹更多的事件類型,緊接著 if 裡面的第一行,我們先透過 getMessageType 取得訊息類型,還有 getText() 取得文字內容,再透過 switch 的方式判斷 訊息類型 是不是 text

最後,我們透過剛剛建立的 $bot 這個 LINEBot 物件中的 replyMessage 來回覆訊息給使用者吧!這樣核心的程式就完成的差不多了

然後剛剛有提到 env ,所以我們來去 .env 添加兩個環境變數吧!其中一個是 CHANNEL_ACCESS_TOKEN 另一個是 CHANNEL_SECRET ,還記得我在 先有一個 Line Messaging API account 這個章節一直提到的 Channel secret 和 Channel access token 嗎?這時候終於派得上用場囉!把他填上去就對了

CHANNEL_ACCESS_TOKEN={{ YOUR_CHANNEL_ACCESS_TOKEN }}
CHANNEL_SECRET={{ YOUR_CHANNEL_SECRET }}

感覺好像都可以了,試著去和 chatbot 聊天的時候,會發現他還是都不理人!!!

沒錯!因為 Laravel 這邊有個小小的坑,就是 Laravel 很貼心地做了 Csrf Token 驗證,所以 LINE 打過來的資料被視為非法的,所以被拒絕了!因此我們需要讓 LINE 打過來的這個 webhook 是暢通的。

先打開 app/Middleware/VerifyCsrfToken.php ,修改內容

<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
    /**
     * Indicates whether the XSRF-TOKEN cookie should be set on the response.
     *
     * @var bool
     */
    protected $addHttpCookie = true;
    /**
     * The URIs that should be excluded from CSRF verification.
     *
     * @var array
     */
    protected $except = ['/callback'
    // 添加這行
    ];
}

 

這樣就會告訴 Laravel 說 /callback 這個路徑就不需要做驗證囉!這樣再去找聊天機器人聊天,應該就會 Hello world 了喔!

Webhook 資料格式

看到這邊,應該會好奇,當使用者每次和機器人聊天的時候,LINE 也會打到我們提供的 webhook URL ,那 LINE 究竟給了我們什麼呢?

我們先試著在上一篇建立的 app\Http\Controller\LineController 中修改一些東西,印出來看看

try {
    $events = $bot->parseEventRequest($body, $signature);
    Log::info($events); // 增加這個 Log,將收到的訊息印出來
} catch (\Exception $e) {
    Log::error($e->getMessage());
}

 

然後對著聊天機器人傳送一則訊息看看,就可以看到這個 $events 裡面到底放了什麼?

array (
0 =>
LINE\LINEBot\Event\MessageEvent\TextMessage::__set_state(
array(
'message' =>
array (
'type' => 'text',
'id' => '10629983864837',
'text' => '這是一段文字',
),
'event' =>
array (
'type' => 'message',
'replyToken' => '773xxxxxx10044c9b52ac0b185145023',
'source' =>
array (
'userId' => 'Ubefxxxxxxbdc59b024344f4ce81e7911',
'type' => 'user',
)
)
'timestamp' => 1569403340191
)
),
)

 

此時 LINE 收到使用者的文字訊息後,會把這則文字訊息傳到我們剛剛給的 webhook 位址。我們把它印出來後,可以看到這個 JSON 是一個 webhook event object,此物件(events)包含 1 ~ n 個 event。先拿一個 event 來說明,從第一個先看:

'message' =>
array (
    'type' => 'text',
    'id' => '10629983864837',
    'text' => '這是一段文字',
),

 

message 這個區塊主要是描述使用者傳來的訊息,我們可以從 type 看出這段訊息的類型; id 可以看出這則訊息的唯一識別碼; text 會有使用者傳過來的訊息。

接著是第二個區塊:

'event' =>
array (
    'type' => 'message',
    'replyToken' => '773xxxxxx10044c9b52ac0b185145023',
    'source' =>
    array (
        'userId' => 'Ubefxxxxxxbdc59b024344f4ce81e7911',
        'type' => 'user',
    ),
)

 

這裡描述著使用者傳過來的事件資訊。 type 是這則訊息的類型。 replyToken 則是在回應此訊息使用,當程式要回應此訊息時須使用此 token,這個 token 的有效時間不一定,但是相當的短,所以要回覆的話要儘快。

再來是, source 的這個部分。這裡會描述 哪種人? 和 誰? 傳過來的訊息。 type 可以得知是群組傳的訊息?還是使用者傳的訊息?所以可能是 user 或者是 group ; userId 則是傳過來的使用者的 id ,假設是群組傳過來的話,將會是 groupId (若使用者有同意 official accounts terms of use ,則會再多一個屬性 userId)。

最後一個 :

'timestamp' => 1569403340191

 

這個很單純就是這則訊息的時間戳,採用 UNIX 時間,也就是從 UTC 1970 年 1 月 1 日 0 時 0 分 0 秒起到現在不考慮閏秒的總秒數。

先有一個 Line Messaging API account

首先我們必須要有一個 LINE 開發人員的帳號,因此我們先進去 LINE Developers 的後台,點選 Log in 即可以進入後台

img

首先,我們要先建立一個 privoder ,點擊 右上或是中下的Create New Provider ,都可以建立

img

進去後,只要填寫 Provider name 就可以了,至於 Provider name 就是 chatbot 供應商的名稱,這應該很好理解的

接著進入剛剛建立的 privoder 點選 Create new channel ,會看到三種 channel ,我們要開發的是 Messaging API ,所以就直接選取中間吧

img

接著就是填寫 chatbot 的基本資訊囉,填寫的資訊依序如下:

  • App icon: 就是機器人的大頭貼
  • App name: 機器人的名稱
  • App description: 簡單敘述一下這隻機器人是在做什麼的
  • Category: 機器人的分類
  • Subcategory: 機器人的子分類
  • Email address: 你的聯繫信箱
  • Privacy Policy URL: 選填項目,如果你有隱私條款的話
  • Terms Of Use URL: 選填項目,如果你有使用條款的話

填寫完成並且確認後,就創立了一隻全新的機器人囉!

接著來看看機器人裡面有什麼資訊

img

Channel ID

這是你這個機器人的 ID ,先記著,等等會用到

Channel secret

這是你這個機器人的 secret,有點像是密碼,也是先記著,等等會用到

Channel access token

也是專屬你的機器人的 token ,也是先記著,等等會用到

Use webhooks

每當用戶傳訊息給你的機器人的時候,其實都是發送一個請求到你的 webhooks ,所以這個地方一定要選取 Enabled ,機器人才知道要使用webhooks 喔

Webhook URL

如同剛剛說的,每當用戶傳訊息給你的機器人的時候,其實都是發送一個請求到你的 webhooks,所以要提供一個網址讓 LINE 知道這隻機器人的請求要發送到哪裡。還有這個 Webhook URL 一定要有 SSL ,也就是走 https 喔

Allow bot to join group chats

是否讓你的機器人可以被加入群組,如果關掉的話,使用者就不能隨意把你的機器人丟進群組囉

Auto-reply messages

是否要自動回應訊息,這個是 [email protected] 後台可以設定的自動回應訊息,我通常是把它關掉的

Greeting messages

當使用者加機器人好友後的問候訊息,也是可以在 [email protected] 後台做設定,我通常也是把它關掉的

QR code of your bot

就是你的機器人的 QR Code ,掃了可以加她好友

Basic ID

就是你的機器人的 ID ,可以透過這 ID 在 LINE 上面搜尋到你的機器人

Your user ID

對於聊天機器人而言,每個使用者都是獨一無二的,你也不例外,所以這邊會有一組你的 user ID

大概就是這樣

讓 LINE 可以連到你的本地端:ngrok

上一章,我有提到一個表單欄位 Webhook URL ,當我們還在開發的階段時候,還沒有一台主機去放我們的應用程式,也或許你會有,不過這不用擔心,最後的時候,我會提到怎麼部署。

在這,我們只要先可以在本地端開發,並且讓 LINE server 可以打到你的 Webhook URL 就好了,這邊提供一個很多人在使用的服務: ngrok

為什麼選擇用 ngrok 呢?就如同官網所說的

1Spend more time programming. One command for an instant, secure URL to your localhost server through any NAT or firewall.

簡言之就是讓你花更多時間專注在寫程式。ngrok 會為你的本地主機服務器提供一個即時,安全的 URL。

再換句話說,就是把你的 localhost 對應到 https://xxxxx.ngrok.com。(這邊的 xxxxx 會隨著每次打開 ngrok ,隨機配一組給你),這樣子做之後的好處顯而易見,也就是你只要把 webhook URL 填好,就可以在 localhost 上面測試了!

除此之外,使用上也非常的簡單。

  1. 首先先去 ngrok 官網 註冊帳號
  2. 註冊成功,應該會看到這樣的引導說明:
ngrok引導說明
  1. 根據你的作業系統環境下載 ngrok 到根目錄,或是你知道的地方
  2. 打開終端機,輸入 ./ngrok authtoken , 的部分,每個人都不一樣,所以造著 ngrok 的說明複製貼上就可以設定好 auth token
  3. 輸入 ./ngrok http 8000 ,因為 Laravel 預設的 port 是 8000 ,所以我這也就輸入 8000 ,如果你因為其他原因使用了其他的 port 的話,這邊也要記得改成其他的 port 喔
  4. 出現這樣的畫面,就是差不多快成功了
    ngrok引導說明
  5. 將 Forwarding 的網址複製(要複製到有 https 的喔,不然 LINE 會不給過),貼到 LINE Developer 後台的 webhook URL 這個欄位,即可完成!

接著下一章,會來教大家如何做第一個簡單的回覆!

我需要會人工智慧、機器學習才能做嗎?

不用!

一個厲害的聊天機器人會根據不同訊息做出不同的反應,會需要以下元素:

  1. 大數據
  2. 演算法
  3. 文字處理

透過大數據,大量的語料和聊天數據來建置聊天機器人的前置作業,讓聊天機器人會根據不同使用情況,提供不一樣的對應

演算法使聊天機器人普及的關鍵因素,透過演算法分析了解語句間的關聯、來找出相對應關聯性較高的回覆答案,做出相對正確的回應,也因此當語料的情況下,機器人會相對很笨,常常答非所問。

文字處理則是根據不同的自然語言,結構文法不同,所以資料處理的方式更是大相逕庭,相較於英文,中文難上許多

但是很多現有的聊天機器人,都是使用 關鍵字 回應,例如使用者說『早安』,機器人就會回『早安喔』,如果使用者胡亂輸入,機器人可能就會不予回應,或是比較好的 UX 會提示聽不懂。因此要做一個聊天機器人,其實可以不用會人工智慧、機器學習是沒問題的!