iT邦幫忙

2024 iThome 鐵人賽

DAY 11
0
Software Development

做一支專屬自己學校的課程評價 LINE Bot 吧!系列 第 11

[Day 11] 以 PostbackEvent 觸發事件,讓使用者輸入老師名就能跑出課程列表

  • 分享至 

  • xImage
  •  

釐清觸發事件 MessageEvent 及 PostbackEvent

MessageEvent

還記得前面學習 ImageSendMessageTextSendMessage,以及 FlexSendMessage 的時候,我們都是用什麼樣的事件觸發嗎?

# 處理訊息的事件
@parser.add(MessageEvent, message=TextMessage)
def handle_message(event):

答案是 MessageEvent,指的是使用者發送了文字、圖片、影片、貼圖等訊息,就會觸發 MessageEvent,而 TextMessage 則是限定這個函數只處理文字訊息。

無論在 Day 06 歸剛回貓貓機器人Day 09 陽春麵版查評價關鍵字 以及 Day 10 金箔陽春麵版查評價關鍵字,都是藉由使用者輸入任意文字訊息,去觸發 handle_message 函式。

PostbackEvent

而如果今天想要讓使用者按我發送的 FlexSendMessage 裡頭的按鈕來做回覆呢?畢竟懶得打字可說是大家的共識,能用選的就用選的~
https://ithelp.ithome.com.tw/upload/images/20240925/20151510ke5BI5UFb4.png

像是醬子,透過 FlexSendMessage 按鈕讓使用者不用再同時輸入課名與老師名,這設計可真是趕時間跑加簽的福音(?

有別於前面提到的 MessageEventPostbackEvent 處理的就是使用者點擊含有回傳資料的按鈕

定義需求

  • 使用者輸入老師名時,會跳出課程列表選項,點選即可跳出對應評價
  • 選項與回覆皆以 FlexSendMessage 方式回覆

編寫邏輯

  1. 建立一個 JSON 檔案: 先將課程列表回覆模板 JSON 的內容放到一個新檔案:
    hulolo > chatbot > reply_course_list.json

  2. 引入 PostbackEvent 套件:

    # hulolo > chatbot > views.py
    from linebot.models import MessageEvent, PostbackEvent, TextMessage, 
        TextSendMessage, ImageSendMessage, FlexSendMessage
    
  3. 使用者輸入老師名的處理邏輯: 重新寫一個觸發事件的函式 handle_teacher_name_msg,用來處理使用者輸入老師名。邏輯是:

    • 先去查詢資料表中有沒有老師開的課
    • 取得帶著課程們的陣列
    • teacher_lists_flex_message_package 套漂亮的模板
    • 回覆給使用者
    # hulolo > chatbot > views.py
    @parser.add(MessageEvent, message=TextMessage)
    def handle_teacher_name_msg(event):    
        user_message = event.message.text  # 取得使用者發送的文字
        filtered_teacher = Course.objects.filter(teacher_name=user_message)
        if filtered_teacher.exists():
            teacher_name = user_message
    
            # values_list 是以陣列裝課程名稱
            # distinct 可以把可能重複的老師名做過濾
            candidate_courses = filtered_teacher.values_list(
            'course_name', flat=True).distinct()
    
            # 稍後會製作這個漂亮的函式,先呼叫它
            flex_message = teacher_lists_flex_message_package(
            teacher_name, candidate_courses)
    
            message = FlexSendMessage(
                                alt_text=f"{teacher_name} 老師的課程",
                                contents=flex_message
                            )
            line_bot_api.reply_message(
                        event.reply_token,
                        message)
        else:
            print("Empty")   
    
  4. 處理漂亮的課程列表 Flex message 回覆: 新增 teacher_lists_flex_message_package 函式處理漂亮的課程列表 Flex message 回覆,這邊要特別介紹 Flex message 若有按鈕,是需要在裡面加上按鈕按下去會傳遞的資料,也就是註解中按鈕背後藏著真正的資料 (詳見程式碼內的註解)。

    # hulolo > chatbot > views.py
    def teacher_lists_flex_message_package(teacher_name, candidate_courses):
        # 引入 JSON 檔案
        json_path = os.path.join(BASE_DIR, 
            'chatbot', 'reply_course_list.json')
        flex = json.load(open(json_path, 'r', encoding='utf-8'))    
    
        # 設定標題為老師名稱
        flex['body']['contents'][0]['text'] = f"{teacher_name}老師的哪堂課?"
    
        # 課程列表的選項用了十個顏色!
        colors = ['#F0C29E', '#A1DE95', '#F5C578', '#91D9C2', '#DFC493', 
                '#F0C29E', '#A1DE95', '#F5C578', '#91D9C2', '#DFC493']
    
        # 根據上述顏色動態生成課程按鈕,而且是根據幾堂課就產生幾個按鈕~
        for i, course in enumerate(candidate_courses, start=1):
            button = {
                'type': 'box',
                'layout': 'vertical',
                'spacing': 'none',
                'contents': [{
                    'type': 'button',
                    'style': 'primary',
                    'action': {
                        'type': 'postback',
                        'label': course,
                        #注意! 這很重要,此處代表按鈕背後真正的資料
                        'data': f"{teacher_name}-{course}" 
                    },
                    'color': colors[i-1],
                    'margin': 'xs',
                    'offsetTop': 'none',
                    'height': 'sm'
                }],
                'flex': 0,
                'borderWidth': 'medium',
                'cornerRadius': 'xxl',
                'offsetTop': 'none',
                'margin': 'md',
                'backgroundColor': colors[i-1]
            }
            flex['body']['contents'].append(button)
    
        return flex
    
  5. 使用者按下後的 PostbackEvent 處理: 新增一個函式 handle_postback,這邊就會使用到 PostbackEvent,用來觸發與分析剛剛按鈕內的資料,並進行查詢回覆後告知評價內容,讀者可以觀察其與 handle_message 函式的差異,會發現幾乎相同哦~

    # hulolo > chatbot > views.py
    @parser.add(PostbackEvent)
    def handle_postback(event):
        # 取得使用者點按鈕時回傳的資料
        postback_data = event.postback.data
    
        # 還記得前面提及按鈕背後的資料嗎? 在這!
        # postback_data 格式為 "課程名稱-老師名稱"
        if "-" in postback_data:
            teacher_name = postback_data.split("-")[0]
            course_name = postback_data.split("-")[1]
    
            filtered_courses = Course.objects.filter(
                teacher_name=teacher_name, course_name=course_name)[:5]
            if filtered_courses.exists():
                messages = []
    
                # 使用 enumerate 給每個課程加上編號
                for idx, course in enumerate(filtered_courses, start=1):  
    
                    # 準備課程資料以傳入 flex_message_package 函式
                    course_info = {
                        'course_name': course.course_name,
                        'teacher_name': course.teacher_name,
                        'course_type': course.course_type,
                        'feedback_content': course.feedback_content,
                        'evaluation_semester': course.evaluation_semester,
                        'submitter_name': course.submitter_name,
                        'number': str(idx),  # 這裡的 idx 是第幾則的意思
                        }
    
                    # 呼叫 flex_message_package 函式來產生 Flex Message
                    flex_message = flex_message_package(course_info)
    
                    # 使用 FlexSendMessage 回傳 Flex Message
                    messages.append(FlexSendMessage(
                            alt_text=f"課程評價:{course.course_name}",
                            contents=flex_message
                        ))
    
                line_bot_api.reply_message(
                    event.reply_token,
                    messages  # 回傳 Flex Message 列表
                )         
            else:
                print("Empty")
        else:
            print("Empty")
    

結果

當輸入 吳小峰 就會得到一筆訊息回覆,包含一個 特殊教育學生評量 的課程~可以到後台試著增加同老師不同課程,就會有多的按鈕出現 (在這邊教學方便我們上限設為 10 種不同課程):
https://ithelp.ithome.com.tw/upload/images/20240925/20151510LVihPYC88q.png

再往前一點點

  • 當函式變多的時候,對於命名的整理就很重要了,如果對於自己命名沒有信心,可以問問隔壁飽讀程式詩書的 ChatGPT,會給予很不錯的命名建議~
  • 發現 handle_messagehandle_postback 內容很相近時,可以考慮將其回覆評價內容的邏輯,獨立一個函式,就能避免重複造輪子的問題。

覆盤

在這篇文章中,我們學會了:

  • 利用 PostbackEvent 觸發事件,在這邊是按下 Flex message 裡的按鈕觸發
  • 改良後的老師關鍵字搜尋流程:
    • 解讀老師名訊息
    • 資料庫篩選
    • 列出課程名
    • 提供列表回覆訊息
    • 收到使用者按鈕的回覆
    • 回覆訊息給使用者評價訊息
  • Day 11 原始碼

上一篇
[Day 10] 使用 FlexSendMessage 讓回覆訊息變成-金箔陽春麵版
下一篇
[Day 12] 函式的可重複利用性: 無論輸入課名或老師名都可以跳出選項
系列文
做一支專屬自己學校的課程評價 LINE Bot 吧!20
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言