2025年11月2日 星期日

LINE Bot 學習筆記 : LINE 訊息解析

撰寫 LINE Bot 聊天機器人須對 LINE 的訊息結構有深入了解, 本篇旨在利用我在 render.com 上架設的 serverless 函式執行平台 (類似 GCF 或 AWS Lambda) 上撰寫的測試函式來了解 LINE 訊息結構, 關於利用 render.com 免費帳戶執行 Python web app 的方法參考 :


關於 serverless 函式執行平台參考 :


render.com 是透過寄存於 GitHub 的 repo 來佈署 web app 的, 最新的 serverless repo 網址如下 :


在 Serverless 平台上新增的函式模組必須定義一個 main() 函式, 並傳入 request 物件與關鍵字參數 kwargs, 程式結構如下 : 

# func_module.py
def main(request, **kwargs):
    # 取得主程式 serverless 傳遞的環境變數 (存於 ~/.env)
    # config=kwargs.get('config', {})  # 預設傳回空 dict
    # telegram_token=config.get('TELEGRAM_TOKEN')
    # telegram_id=config.get('TELEGRAM_ID')
    result='do something'
    return result

注意, **kwargs 參數是必須的, 主要用來接收從主程式傳來的 API key/token 參數 (例如 Telegram 權杖與聊天室 ID, 或 OpenAI API 金鑰等), 即使此函式用不到也必須接收. 

主程式 serverless 已經匯入如下套件模組

from flask import Flask, request, jsonify, session, redirect
import importlib.util
import os
import logging
from dotenv import dotenv_values
import sqlite3

函式模組會被動態地載入主程式, 因此若有用到上面這些模組就直接使用, 毋須再匯入. 本篇測試旨在利用 LINE App 輸入聊天訊息, LINE 訊息伺服器收到後會傳送給放在 render.com 的 serverless 平台上的 Webhook 程式處理, 此程式會把 LINE 請求訊息傳回 LINE 訊息伺服器回覆提問者, 此 Webhook 程式碼如下 :

# line_message_1.py
import json
from linebot import LineBotApi, WebhookHandler
from linebot.models import TextSendMessage

def main(request, **kwargs):
    # 取得主程式傳入的 LINE secret & token
    config=kwargs.get('config', {})
    secret=config.get('LINE_CHANNEL_SECRET')
    token=config.get('LINE_CHANNEL_ACCESS_TOKEN')
    # 檢查必要參數
    if not secret or not token:
        return {'error': 'Missing LINE or Gemini credentials'}    
    try:
        # 初始化 LINE Bot API 與 Webhook Handler 物件
        line_bot_api=LineBotApi(token)
        handler=WebhookHandler(secret)
        # 取得請求內容
        body=request.get_data(as_text=True)
        # 取得簽章
        signature=request.headers.get('X-Line-Signature', '')
        # 驗證簽章
        try:
            handler.parser.parse(body, signature)
        except InvalidSignatureError:
            return {'error': 'Invalid signature'}
        # 將請求內容轉成 JSON 格式
        json_data=json.loads(body)
        events=json_data.get('events', [])
        # 若為 verify webhook 測試回傳 ok 即可 (沒有事件) 
        if not events:
            return {'status': 'ok'}        
        reply_token=events[0]['replyToken']
        # 設定回傳訊息為請求內容
        text_message=TextSendMessage(text=json.dumps(json_data, ensure_ascii=False))
        # 回傳訊息
        line_bot_api.reply_message(reply_token,text_message)
    except Exception as e:
        return {'error': str(e)}
    return {'status': 'ok'}

此程式需匯入 LineBotApi, WebhookHandler, 與 TextSendMessage 類別, 在 main() 函式中先用關鍵字參數字典取得主程式 serverless.py 傳來的 LINE 訊息通道的 Secret 與 Access token, 以便初始化 LINE Bot API 與 Webhook Handler 物件. 然後用 Flask.request 物件取得 LINE 訊息伺服器傳來的請求內容並利用它驗證數位簽章 (確認此請求來自 LINE), 接著將請求內容轉成字典, 利用有無包含 events 鍵 (值為串列) 來判別是否為 Webhook Verify (無 events 鍵) 還是聊天訊息 (有 events 鍵), 若為 Verify 動作就直接回 200 OK 表示 verify Success; 否則就從 events 中取出 replyToken, 呼叫 LINE Bot API 物件的 reply_message() 方法將請求內容字串傳回. 

先在 render.com 的 serverless 平台新增函式 line_message_1.py :




然後將滑鼠移到函式列表 line_message_1 的執行欄按右鍵, 點選 "複製連結網址" :




接下來要登入 LINE 開發者網站 : 


切到 Messaging API 頁籤, 按 Webhook URL 項目的 Edit 鈕, 將上面 line_message_1.py 函式的網址貼上儲存 : 




按 Verify 驗證網址顯示 Success 即完成 Webhook 設定, 可以用 LINE App 來測試是否能收到 LINE 訊息伺服器傳送給 Webhook 程式的請求內容. 


1. 傳送文字訊息 : 
 
在 LINE 聊天室中切換到 LINE Bot 的官方帳號 (此處為小狐狸事務所機器人), 傳送文字訊息例如 '你好', LINE 訊息伺服器會向上面的 Webhook 程式 line_message_1.py 發出 HTTP 請求, Webhook 程式會將此請求訊息回傳, LINE 訊息伺服器收到後就傳送到手機 LINE App 的 LINE Bot 官方帳號聊天室, 結果如下 : 




收到的 JSON 回覆內容 :

{"destination": "Ufaec4230b58e2d1449895879a-------", "events": [{"type": "message", "message": {"type": "text", "id": "585818269928128938", "quoteToken": "iNYTI1QbQh8SEfdtrEmF3w1NgfWEeDKKZuSIy_qfwvkg3OIZ_MqaDE5vWri4zCpFd0RileKFfof8YzXwkjGBWIDNh6ZFxJ2AbsVY5FbKewiwkj6XZ5qwSHShKv3WfT5bvkwSUI1ZwgmQCQDb3LS-Hw", "text": "你好"}, "webhookEventId": "01K8ZW1YQJSHCXJ9QRF6ZAXJSJ", "deliveryContext": {"isRedelivery": false}, "timestamp": 1762006202834, "source": {"type": "user", "userId": "使用者 ID"}, "replyToken": "a61c2f85aaa64bd99ba534992d10b99a", "mode": "active"}]}

可見 LINE 訊息伺服器發給 Webhook 程式的請求訊息是一個只有 destination 與 events 屬性的 JSON 格式資訊, destination 表示我 LINE 官方帳號 (LINE Bo) 的 User ID, 用來標示這是給哪個 Bot (哪個 LINE channel) 的事件, 而 events 則為訊息事件之內容, 其值為單元素串列, 內容是一個物件, 其中 type, message, timeStamp, source, replyToken, mode 等均為 LINE 訊息的通用屬性, 不管訊息是文字, 圖片, 音訊, 視訊等都有這些屬性, 裡面的 replyToken 最重要, 因為 Webhook 程式在呼叫 reply_message() 時需要用它來指定回覆對象. 

可以將此 JSON 字串貼到 JSON Viewer 網站來觀察其資料結構 : 





2. 傳送 Sticker (表情貼圖) 訊息 : 

按 LINE 聊天輸入框右邊的笑臉, 點選任一表情貼圖傳送給 LINE Bot :




這時會收到 Webhook 程式回傳的請求訊息 : 




回覆內容如下 :

{"destination": "Ufaec4230b58e2d1449895879a-------", "events": [{"type": "message", "message": {"type": "sticker", "id": "585828359058489380", "quoteToken": "v4wMDoLMbMtNnOsw7yeZjnN6-xx8d0MQ5h7HNMj_N7c2GfTdLL2Q_YfUaYNNa5BoDhnwdGB2YFe_BzQOaN65cdXz1b3K4gZ4Ge455XakD_GWKlEPjV183ZuDgMdZUJlrl8Q7g5eydK5OBJqjzJRlRg", "stickerId": "51626501", "packageId": "11538", "stickerResourceType": "ANIMATION", "keywords": ["alright", "super", "roger!", "goodidea", "good", "great", "okie", "OK", "line", "moon", "thumbsup", "got it", "of course", "very good", "Affirmative"]}, "webhookEventId": "01K901SFJVF4J9MFH734BMGQ74", "deliveryContext": {"isRedelivery": false}, "timestamp": 1762012216419, "source": {"type": "user", "userId": "使用者ID"}, "replyToken": "e1f6811d581b488690dca89e0ef0413b", "mode": "active"}]}

可見傳送表情貼時, message 屬性值為 sticker, 後面有 stickerId 與 packageId 等與表情貼圖相關之屬性, 其餘為所有訊息共通之屬性. 

但如果要用這方法在 LINE App 上顯示傳送圖片時的請求訊息將不會獲得回應, 因為圖片的訊息量太大, 會超過 LINE 的限制而無法處理, 必須在本機用 ngrok 模擬才行, 參考下面這本書做法 :


此書作者 OXXO 將原始碼放在 GitHub, 關於訊息解析的程式碼網址如下 :


沒有留言 :