2024年1月30日 星期二

Python 學習筆記 : LINE Bot 開發 (三) 在本機佈署 Echo Bot 聊天機器人

在前一篇測試中已經建立 Line 聊天機器人的官方帳號, 並將自己加入為好友. 接下來要測試 Line Bot 最重要的部分 : 設定 Webhook URL, 這樣才能將 Line Bot 伺服器串接到我們的後台網頁伺服器, 讓裡面的 Python 程式來處理所接收到的訊息並決定如何回應.

本系列之前的文章參考 :


教學文章參考 :



Line 聊天機器人其實就是安裝在開發者的後台網頁伺服器中的網頁程式 (可用 Python 的 Flask 或 Django 等實作), 使用者傳送的訊息會透過 Line 的訊息伺服器 (Messaging Server) 作為介接, 利用 Webhook (採用 RESTful 的一個 URL) 以 HTTPS 向後台網頁伺服器提出 POST 請求, 我們的 Line Bot 程式須根據收到的訊息決定如何回應, 處理架構與流程如下圖所示 :




Line 聊天機器人的開發程序如下 :
  1. 到 Line 開發者網站將自己的 Line 帳號註冊為開發者帳號 (Business ID).
  2. 建立供應商 (Provider) 名稱, 相當於開發者的公司招牌.
  3. 建立頻道 (Channel) 名稱, 一個頻道代表一個聊天機器人 (會有一個 @ 開頭的 Line 官方帳號), 一個供應商可以建立多個頻道. 
  4. 到 Line 官方帳號管理頁面進行此頻道之回應設定並將此官方帳號加為好友.
  5. 安裝 line-bot-sdk 套件以便撰寫聊天機器人網頁程式, 並佈署到自己的網頁伺服器 (取得HTTPS 網址). 
  6. 將聊天機器人的網址設為此頻道的 Webhook URL 並進行測試. 
在前兩篇測試中已完成步驟 1~4, 本篇將以簡單的 Echo Bot (鸚鵡機器人, 會將收到的訊息值接回傳) 為例完成步驟 5~6. 參考官方教學文件 :


首先我們必須來完成後台網頁伺服器的架設, 首先用 Flask 寫一個運行於本機的網頁伺服器, 然後再用 Ngrok 程式讓其擁有 HTTPS 網域功能 (Line 訊息伺服器的 Webhook 必須是 HTTPS 域名網址, 不可以使用 IP). 


一. 取得頻道密鑰 (Channel secret) 與頻道存取權杖 (Channel access token) : 

先開啟 Line 開發者首頁 : 





按左邊的 "Console" 進入控制台, 或直接拜訪下列網址 :


控制台會顯示所有已建立之頻道 (即聊天機器人, 目前只有一個) : 




點選要設定的頻道進入基本設定頁面 : 




拉到頁面最底下可以看到 Channel secret, 如果沒有就按 "Issue" 鈕產生一個 : 




按複製鈕先將其貼到記事本保存, 例如 :

secret="abcdefbd24f2f1eabd274024catony1966"   (此為範例)

然後切換到 Messaging API 頁籤, 上面是官方帳號與其 QR code (可以讓測試者加入好友) : 




往下拉到底可以看到 Channel access token 欄位, 因尚未發行過權杖目前是空的 :




按 "Issue" 鈕新增一個 : 




按複製鈕將其貼到記事本保存, 例如 : 

token="tony1966/pOjFWcV+AJ2qQTcGSQXvvDdoRa98W6W44fC/vwCG5B+Pg03dPP/gHVuUpRcJGZ6glj5aA1PEoN9VlCIBDgFW5/wRGMP+/hvfscg3P2M1jMCNLv8pjIV53DIyT9WpY4MPQYMWgAKbdV3GySwdB04t89/1O/w1cDabcdef="  (此為範例)

此 Messaging API 設定頁面還有一個 Webhook 欄位要設定, 等網頁伺服器架設好再來填. 

參考 :



二. 用 Flask 在本機架設一個網頁伺服器 : 

Flask 是一個 Python 輕量級的 Web 框架套件, Flask 應用程式執行時會在本機建立一個網頁伺服器 (localhost 或 127.0.0.1, 預設埠口為 5000), 因語法簡單常被用來快速建立 Web 網站雛形以驗證 RESTful 服務, 關於 Flask 用法參考 : 



1. 安裝 flask 套件 : 

使用 pip install 來安裝 flask 套件 :

pip install flask  


2. 匯入 Flask 類別建立 app : 

從 flask 匯入 Flask() 類別, 然後呼叫其建構子並傳入 __name__ 即可建立一個網頁應用程式 (即傳回之 Flask 物件) :

>>> from flask import Flask    
>>> app=Flask(__name__)    
>>> type(app)   
<class 'flask.app.Flask'>   


3. 用裝飾器定義路由與處理函式 : 

接下來是定義路由 (routes), 即 URL 路徑與所對應之處理函式, Flask 使用 app.route() 函式的裝飾器來定義 URL 路徑, 放在所對應之處理函式前面, 作法簡單直觀, 語法如下 :

@app.route("路徑")
def handler([參數序列]):
    ,,,
    return 回應訊息      # HTML 格式字串

URL 路徑其實就是所謂的 Web API, 這是一種採用 RESTful 規範的 GET 請求方式, 它以斜線做區隔的層級路徑來區別要執行網站伺服器裡的哪一個資源 (程式), "/" 表示網站首頁, 亦即根目錄, 斜線底下是資源名稱, 例如 "/hello" 表示存取 hello 資源, 也可以傳遞多個參數, 語法如下 :

/資源名稱/參數1/<值1>/參數2/<值2> ....

此 RESTful 的 GET 請求路徑相當下列的一般 GET 路徑語法 :

資源名稱?參數1=值1&參數2=值2 ....  

如果有傳入參數的話, 要依序傳入處理函式 :

def handler(參數1, 參數2, ...): 

參考 :


例如可為上面建立的 app 建立三個路由 :

"/" : 請求首頁, 回應 "<h1>歡迎光臨我的網站</h1>"
"/ok" : 請求 ok 資源, 回應 "<p>OK</p>" 
"/hello/<name>" : 請求 hello 資源傳入 name 參數, 回應 "<h3>Hello, {name}!"

例如 :  

>>> @app.route('/')  
def main():  
    return '<h1>歡迎光臨我的網站</h1>'    
>>> @app.route('/ok')   
def ok():   
    return '<h1>OK</h1>'   
>>> @app.route('/hello/<name>')   
def hello(name):   
    return f'<h1>Hello, {name}!</h1>'   

這裡的路徑有分大小寫 (因為 Python 字串是區分大小寫的), 寫錯會回應 not found, 但最後面的參數則無關大小寫 (參數字串會被忠實傳送). 例如請求 /hello/TONY 會回應 "Hello, TONY"; 請求 /hello/tony 會回應 "Hello, tony"; 但請求 /HELLO/TONY 則是回應網頁不存在. 

注意, 在 Python 命令列執行上面指令時, 裝飾器必須與其所修飾的函式一起輸入後再按 Enter, 若只輸入 @app.route() 就按 Enter 會出現 SyntaxError. 


4. 呼叫 app.run() 建立網站 :  

定義好路由後只要呼叫 app.run() 就會將此網站架起來了 :

>>> app.run(debug=True)   
 * Serving Flask app '__main__'
 * Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5000   
Press CTRL+C to quit
 * Restarting with watchdog (windowsapi)
OK

可見此 Flask 應用程式預設監聽本機 (127.0.0.1 或 localhost) 的 5000 埠請求, 注意, 此處 debug=True 表示會顯示呼叫 app.run() 時也傳入 host 與 port 等參數, 例如 (先按 Ctrl + C 停止上面已啟動之開發伺服器) : 

>>> app.run(host='0.0.0.0', port=8080, debug=True)   
 * Serving Flask app 'flask_app'
 * Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:8080   
 * Running on http://192.168.2.128:8080   
Press CTRL+C to quit
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 260-650-195

此處 host='0.0.0.0' 表示 Flask 開發伺服器監聽對本機任何 IPv4 位址的請求, 可見除了 127.0.0.1 外, 還多了一個 192.168.2.128, 這是本機從區網 DHCP 獲得之網址. port 參數則是開發伺服器要監聽的埠號. debug=True 表示要顯示偵錯訊息. 關於 0.0.0.0 與 127.0.0.0 參考 :


網站架起來後即可開啟瀏覽器拜訪 http://127.0.0.1:8080 (或此處本機獲得之區網網址 192.168.2.128:8080), 基於安全會出現防火牆警告視窗 :




按 "允許" 後會出現首頁頁面 : 



在網址列輸入 127.0.0.1/ok 會回應 OK 網頁 : 




在網址列輸入 127.0.0.1/hello/tony 則會回應 Hello, tony 網頁 : 



可見這個 Flasp 網站已能正常運作, 我們的聊天機器人運作模式也是如此, Line Messaging Server 會依照 Webhook URL 網址對我們的後台網頁伺服器提出請求, 然後網頁伺服器依據請求做出是當的回應. 

完整程式碼如下 :

app.py
from flask import Flask

app=Flask(__name__)

@app.route('/')
def main():
    return '<h1>歡迎光臨我的網站</h1>'

@app.route('/ok')
def ok():
    return '<h1>OK</h1>'

@app.route('/hello/<name>')
def hello(name):
    return f'<h1>Hello, {name}!</h1>'

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080, debug=True)
    
注意, 在本機執行 Flask 應用程式建立網站時, 建議使用 flask run 指令於命令列執行, 且應用程式檔案名稱要取名為 app.py, 指令語法如下 :

flask run -h localhost -p 埠號   

或 

flask run -h 127.0.0.1 -p 埠號

如果用 python 指令執行或在 Thonny 中按 Play 鍵執行, 在有些電腦可能會因 "網站無回應" 而無法顯示網頁, 參考 : 


不過這個網站只能在本機存取, 外網無法連線此網站. 若要用本機網站充當聊天機器人的後台伺服器, 必須讓此網站具有 HTTPS 域名網址 (這是 Line 訊息伺服器對 Webhook URL 的要求), 這可以透過 Ngrok 代理伺服器來達成, 它會將我們在本機指定埠號上執行的網頁伺服器綁定一個 HTTPS 域名網址. 參考 : 



5. 下載代理伺服器 Ngrok :   

上面用 Flask 在本機所架設的 localhost 網站只支援 HTTP 協定, 且只有本機區域 IP 網段能存, 外網無法連線此網站, 解決辦法是使用 Ngrok 代理伺服器, 它可以為 localhost 本機伺服器上的網站提供一個 HTTP 與 HTTPS 網域網址 (不是 IP), 讓外網能存取此本機網站, 其 HTTPS 網址就能符合 Line Bot 的 webhook URL 要求. 參考 :


Ngrok 可在官網下載 zip 檔 (v3 約 9MB 左右), 毋須安裝, 解開即可使用 : 





ngrok-v3-stable-windows-amd64.zip 解開後得到一個單一的 ngrok.exe, 不過執行時需要輸入認證用的權杖 (token), 故須先按上圖右上角的 "Sign up" 鈕申請帳號 (可以使用 Email 或 GitHub 帳號), 然後點左方導覽列 "Getting Started" 項下的 "Your Authtoken" 會在右方顯示權杖 :




按 "Copy" 鈕將權杖複製到記事本儲存. 取得權杖後開啟一個命令提示字元視窗, 用下列指令進行認證 (注意, token 不可用括號括起來) :

ngrok authtoken 權杖 

例如 :

D:\python\test>ngrok authtoken tonybyjkVWZ1RXPyZVSQ1nKz8Mn_3kbM1966At62W7RGhsx1L   (此為範例權杖) 
Authtoken saved to configuration file: C:\Users\tony1\AppData\Local/ngrok/ngrok.yml   

如看到 "Authtoken saved to configuration file ... " 表示認證 OK, 然後就可以用下列指令讓 Ngrok 為目前監聽本機 8080 埠的網站伺服器產生一個 HTTPS 網址 :

ngrok http 8080   

此處 8080 為目前本機網頁伺服器的埠號. 注意, 新版 ngrok 程式已取消用來指定伺服器所在區域的 -r 參數 (會自動選擇較近之主機). 執行後就會出現所綁定的 HTTPS 網址 : 




複製此 HTTPS 網址 https://b55d-220-133-183-132.ngrok-free.app/ 貼到瀏覽器即可顯示本機網站內容了 (不須指定埠號) :





可見任何對 https://b55d-220-133-183-132.ngrok-free.app 的連線請求都會被轉發到本機的區網位址 127.0.0.1:8080 來, 從而使外網也可以看得到位於內網的網站. 只要沒按 CTRL + C 終止這個轉發程序, 這個 HTTPS 網址就會一直存在. 注意, 結束程序再次執行時會綁定不同的 HTTPS 網址. 


三. 安裝 line-bot-sdk 套件與撰寫聊天機器人程式 :     

LINE 公司為 Python 聊天機器人開發者提供了一個開源套件 line-bot-sdk, 其原始碼與範例檔寄存於 GitHub :



1. 安裝 line-bot-sdk 套件 : 

撰寫 Line 聊天機器人程式需要安裝 Line 提供的開發套件 line-bot-sdk : 

pip install line-bot-sdk 

如果使用 Thonny 編輯器可在 "工具/管理套件" 搜尋 "line-bot-sdk" 後安裝 :




Line Bot 開發套件安裝完成後, 即可開始聊天機器人程式, 最簡單的便是將收到的訊息回傳的 Echo Bot 鸚鵡聊天機器人.

參考 :



2. 撰寫 LINE 聊天機器人程式 : 

在 line-bot-sdk 套件的 GitHub 寄存庫中有提供 Echo Bot 的範例程式, 參考 : 


複製此範例程式碼到 Thonny 或其他編輯器中, 稍微修改一下, 加入存取根目錄 "/" 的路由為顯示歡迎訊息, 方便檢查網站是否正常運行 : 

from flask import Flask, request, abort
from linebot.v3 import WebhookHandler
from linebot.v3.exceptions import InvalidSignatureError
from linebot.v3.messaging import (
    Configuration,
    ApiClient,
    MessagingApi,
    ReplyMessageRequest,
    TextMessage
    )
from linebot.v3.webhooks import (
    MessageEvent,
    TextMessageContent
    )

secret='abcdefbd24f2f1eabd274024catony1966'        # 此為範例密鑰
token='tony1966/pOjFWcV+AJ2qQTcGSQXvvDdoRa98W6W44fC/vwCG5B+Pg03dPP/gHVuUpRcJGZ6glj5aA1PEoN9VlCIBDgFW5/wRGMP+/hvfscg3P2M1jMCNLv8pjIV53DIyT9WpY4MPQYMWgAKbdV3GySwdB04t89/1O/w1cDabcdef='                # 此為範例權杖
configuration=Configuration(access_token=token)    # 設定權杖 
handler=WebhookHandler(secret)                               # 建立處理 Webhook 的物件

app=Flask(__name__)      # 建立 Web app

@app.route('/')                  # 測試網站是否正常運行的路由
def main():
    return '<h1>歡迎光臨我的網站</h1>'

@app.route("/callback", methods=['POST'])     # 聊天機器人頻道之 Webhook 網址
def callback():
    # 從 HTTP 標頭取得數位簽證 X-Line-Signature
    signature=request.headers['X-Line-Signature'] 
    # 取得 HTTP 請求之內容 (BODY)
    body=request.get_data(as_text=True)
    app.logger.info('HTTP 請求內容: ' + body) 
    # 比對數位簽證是否符合, 符合就觸發 MessageEvent 呼叫 handle_message() 
    try:
        handler.handle(body, signature)     # 比對數位簽證是否符合
    except InvalidSignatureError:            # 不符合就拋出例外
        app.logger.info("驗證失敗: 頻道密鑰存取權杖不符合")
        abort(400)  # 回應 400: 中斷連線
    return 'OK'

@handler.add(MessageEvent, message=TextMessageContent)
def handle_message(event):       # 驗證通過時用來處理回應的函式
    with ApiClient(configuration) as api_client:
        line_bot_api=MessagingApi(api_client)
        line_bot_api.reply_message_with_http_info(
            ReplyMessageRequest(
                reply_token=event.reply_token,
                messages=[TextMessage(text=event.message.text)]
            )
        )

if __name__ == "__main__":
    app.run(host="0.0.0.0", debug=True)

此程式的核心是 @app.route() 所修飾的 "/callback" 路由處理函式與 @handler.add() 所修飾的訊息事件處理函式, 這個 "/callback " 是自訂的, 也可以用 "/linebot" 或任意其他名稱 (甚至直接用根目錄 "/" 也行), 它將會是 Webhook URL 的一部分. 

將上面的程式儲存為 app.py (如果要用 flask run 執行 Flask 應用程式一定要用此檔名), 然後在相同目錄下執行 flask run 指令運行本機網頁伺服器 (此處指定伺服器監聽 3000 埠, 其實指定 80, 5000, 或 8080 等埠亦可) : 

D:\python\flask>flask run -h localhost -p 3000    
 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://localhost:3000    
Press CTRL+C to quit

這樣 Flask 網頁伺服器就成功運行了, 用瀏覽器拜訪 http://localhost:3000 或 http://127.0.0.1:3000 就會顯示歡迎網頁, 證明網站運作正常 :

 


接下來執行 ngrok.exe 程式指定上面這個監聽 3000 埠的網站, 讓此區網內的網站擁有一個公網的 HTTPS 網址, 這樣才能讓 Line Messaging Server 可以透過 Webhook URL 將訊息 : 

>>> ngrok http 3000   




這樣此內網的網站便有了如下可讓 Internet 公網存取的 HTTPS 網址 :

https://c9d8-2001-b400-e7ac-be86-5522-c9e6-d934-88d.ngrok-free.app

而所謂的 Line 聊天機器人 Webhook URL 便是在後面加上 "/calback" :

https://c9d8-2001-b400-e7ac-be86-5522-c9e6-d934-88d.ngrok-free.app/callback    

如上所述, 如果直接把處理 Line Messaging Server 傳來的 HTTP 訊息的路由直接做在根目錄 "/" 而非 "/callback" 上, 則 Webhook URL 就直接用 Ngrok 產生的 HTTPS 網址即可. 


四. 設定 Webhook URL :   

透過 Ngrok 為區網網站取得 HTTPS 網址後便要回 Line 開發者控制台設定 Webhook URL :


切到第二個頁籤 "Messaging API", 往下拉到 "Webhook setting" 欄按 "Edit" 鈕, 在 Webhook URL 框中輸入上面的 HTTPS 網址 (後面串接 /callback) :




開啟底下的 "Use webhook" 後按 "Update" 鈕即可 : 



這樣便完成全部設定, 按 "Verify" 鈕驗證 Webhook URL 是否可順利連線後台 Flask 伺服器 : 




出現 Success 表示一切正常, 這時開啟手機 Line App, 點 "好友名單" 再切換到 "官方帳號", 找到上面已加入好友的 "小狐狸事務所聊天機器人", 點進去聊天 (傳送訊息) 就可看到訊息原封不動傳回來了 : 





Bingo! 奮戰了兩個禮拜終於 (才) 搞定這個最簡單的 Line 聊天機器人測試, 到底是難呢還是自己笨都有點搞不清楚了. 總之, 經過這段時間奮鬥算是搞懂整個流程了, 接下去還有更多實務的 Line Bot 要玩, 可參考官方範例 :


2024-02-01 補充 : 

如果用瀏覽器拜訪 Webhook URL 會得到 "Method Not Allowed" 回應 :



 
這是因為此網址必須用 POST 方法提出請求, 而網址列是使用 GET 方法之故. 


2024-03-08 補充 :

看到一篇 iT 邦幫忙好文章 :


沒有留言 :