iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 18
0
Modern Web

從LINE BOT到資料視覺化:賴田捕手系列 第 18

第 18 天:Heroku Postgres:連接 LINE 聊天機器人(二)

  • 分享至 

  • xImage
  •  

第 18 天:Heroku Postgres:連接 LINE 聊天機器人(二)

  昨天我們實作出了一個 LINE 儲存資料的小幫手,藉由輸入特定格式的指令,LINE 聊天機器人就會將訊息轉換為符合 Heroku Postgres 表格的資料,並透過 psycopg2 下達 SQL 指令,將資料存入表格中。
  既然存入資料都可以請 LINE 聊天機器人幫忙了,我們是否也可以請 LINE 聊天機器人幫我們從 Heroku Postgres 的表格中讀出特定的資料呢?今天我們就來試著讓 LINE 聊天機器人幫我們查詢表格中的資料吧。

請 LINE 聊天機器人讀取資料

  我們先來寫一段讀取資料的程式碼看看。經過這幾天浸淫在滿滿的 psycopg2 跟 SQL 指令的氛圍當中,相信大家對於該怎麼做,心裡應該有點概念了。我就先野人獻曝一番,讓大家看看我寫的程式碼:

import os
import psycopg2

def line_select_overall(fetchnumber):
    DATABASE_URL = os.environ['DATABASE_URL']

    conn = psycopg2.connect(DATABASE_URL, sslmode='require')
    cursor = conn.cursor()
    
    postgres_select_query = f"""SELECT * FROM alpaca_training ORDER BY record_no DESC;"""
    
    cursor.execute(postgres_select_query)
    raw = cursor.fetchmany(int(fetchnumber))
    message = []
    
    for i in raw:
        message.append((i[0], i[1], i[2], str(i[3])[:-3], str(i[4])))
    
    cursor.close()
    conn.close()
    
    return message

  上面那段程式碼,我首先引進需要用到的套件,並定義了一個函數line_select_overall(),以及操作這個函數時需要用到的參數fetchnumber。接著,嘗試連接到 Heroku Postgres 提供的資料庫。一樣的,再提醒大家一次,在 Heroku 的環境中,取得資料庫的位置只需要用 DATABASE_URL = os.environ['DATABASE_URL']就可以了,這也是 Heroku 建議的做法。
  而這個函數當中,我下達的 SQL 指令是這樣的:

SELECT * 
FROM alpaca_training 
ORDER BY record_no DESC;
  • 第一行:SELECT *
      代表選取所有欄位。
  • 第二行:FROM alpaca_training
      從我們 Heroku Postgres 資料庫當中的alpaca_training表單提取資料。
  • 第三行:ORDER BY record_no DESC
      提取資料的時候,根據欄位record_no的順序,由大到小(DESC, descending)依序將資料提取出來。

  為什麼這麼做呢?因為我想看最新的資料嘛。
  接著我用cursor.fetchmany(int(fetchnumber)),以參數fetchnumber為依據,傳回特定數量的資料。剩下的程式碼就只是在做一些讓資料更利於閱讀的文字操作而已。
  所以這整段程式碼在做的,就是從alpaca_training表單中,傳回一共fetchnumber筆最新的(最後進入表單的)訓練資料。這樣就結束了。沒錯,就跟大家想的一樣,並不困難,沒枉費我們前幾天學 psycopg2 跟 SQL 指令學的那麼認真。但這樣今天的內容也太沒挑戰性了?所以我們要來試著做點好玩的東西。
  首先我們來模擬一下情境:

我:「我想查詢alpaca_training裡面的訓練資料。」

LINE 聊天機器人:「您想查詢所有(Overall)資料、還是要根據草泥馬的名字(alpaca_name)、還是要根據草泥馬的訓練內容(training)來做特定的查詢呢?」

我:「所有資料,謝謝」
因為所有資料真的太多了,一次傳過來我可能會看得頭昏眼花,貼心的 LINE 聊天機器人於是又發問了。

LINE 聊天機器人:「您想要看最新的一筆(Last 1)資料呢,還是最新的十筆(Last 10)資料呢?」

我:「最新的十筆,謝謝。」

於是乎,按著我的回答,LINE 聊天機器人傳給了我最新的十筆資料。

這樣子的對話邏輯,有可能在 LINE 聊天機器人裡面被實現嗎?當然是可能的。我們今天就來試著用 LINE 提供的新玩具:FlexMessage去建立我們想要的對話模式吧!

不囉嗦,先來看看實作出來的結果:

https://ithelp.ithome.com.tw/upload/images/20190926/20120178CN6H5vHlGg.png
圖一、FlexMessage 一問一答

  FlexMessage 的其中一個功能,讓 LINE 聊天機器人可以提供類似問卷一樣的選擇情境,而根據使用者的回答或選擇,進一步觸發 LINE 聊天機器人去執行特定指令。

FlexMessage 基本架構

  LINE 所提供的 FlexMessage 其架構有點像是一個網頁,可以放入的元素相當豐富,因此能夠做出各式各樣美輪美奐的 FlexMessage。
  FlexMessage 的基本架構如官方網頁內容所示➂。

  若用 Python 的語法來寫,則是一個超大的字典物件:

contents = {'type': 'bubble',
            'herder':,
            'hero':,
            'body':,
            'footer':}

  而'herder''hero''body''footer'又可以繼續填入如'box''image''text''seperator''button'等等的物件➂。

contents = {'type':'box',
            'layout':,
            'contents':}
contents = {'type':'image',
            'url':,
            'size':,
            'aspect_ratio':,
            'aspect_mode':,
            'action':}
contents = {'type':'text',
            'text':,
            'size':,
            'align':,
            'color':}
contents = {'type':'seperator',
            'type':}
contents = {'type':'button',
            'type':,
            'style':,
            'color':,
            'action':}

  當然上面只是舉幾個常用於 FlexMessage 的元素,以及這些元素常用的參數。詳細的內容可以參考 LINE 提供的應用程式編程介面➃。不過如果大家是以 Python 來寫 LINE 聊天機器人的時候,在看官方提供的文件時,有件事得先謹記在心:官方文件提供的變數名稱都是以首字母小寫的駝峰式命名來呈現➄,也就是 JavaScript 習慣使用的命名方式。但是但是,在 LINE 提供給 Python 的官方套件 line-bot-sdk-python 中,各個變數卻是以小寫字母加底線的方式命名。大家還記得 LINE 的 MessageEvent 的結構嗎?

MessageEvent = {'reply_token':,
                'type':,
                'timestamp':,
                'source': {'type':, 'user_id':},
                'message':{'id':, 'type':, 'text':}}

  不知道大家有沒有很好奇:我們在 LINE 官方提供的文件裡面,看到的格式明明是

MessageEvent = {'replyToken':,
                'type':,
                'timestamp':,
                'source': {'type':, 'userId':},
                'message':{'id':, 'type':, 'text':}}

  官方文件給的明明是'replyToken''userId'啊?但我們卻用'reply_token''user_id',而且不這樣用還不行呢。沒錯我在說的就是這件事。因此如果你是用 Python 來寫 LINE 的聊天機器人,記得,在官方文件看到關鍵字像是'aspectRatio',你要改成'aspect_ratio',line-bot-sdk-python 才看得懂你在寫什麼。

FlexMessage 模擬器(Flex Message Simulator)

  由於 FlexMessage 的架構實在是太大了,'body'裡面可以有'box''box'裡面又可以繼續放入'box''text'或是'seperator'。有些時候我們就會在無限的字典迴圈裡面迷路,而不知道我們會造出怎麼樣的 FlexMessage。於是乎,LINE 官方很貼心的提供了一個 FlexMessage 模擬器。先登入 LINE Developers,進到 Provider List 的頁面,就可以看到最左邊有一排 Providers、Tools、Support。點擊 Tools,就會看到如圖三 FlexMessage 模擬器(Flex Message Simulator)出現在我們面前啦。
  選擇 Flex Message Simulator 之後,會跳出一個新的分頁,右上角有一個 + 的符號,如圖四。點下去之後會出現 LINE 提供的一些 FlexMessage 模板,選擇一個接近我們需要的模板,就可以快速地開始修改、編輯囉!

https://ithelp.ithome.com.tw/upload/images/20190926/20120178cJeFBVKTD8.png
圖二、選擇 Flex Message Simulator

https://ithelp.ithome.com.tw/upload/images/20190926/20120178qCyiVwnREd.png
圖三、選擇適合模板

圖四、開始編輯囉!

回發事件(PostbackEvent)

  我做出來的模板大概是長這樣:

圖六、透過 Flex Message Simulator 創造出的 FlexMessage

  紅色框框處是一個一個的按鈕(button),按下去等於是告訴 LINE 聊天機器人我們的回答,而我希望 LINE 聊天機器人能根據我們的回答作出相應的反應。這時候就需要用到'button'裡面的'action'這個項目。
  LINE 提供的'action'有兩種。一種可以帶你上網,另一種則可以回傳資料給 LINE 聊天機器人,藉此告訴 LINE 聊天機器人我們的選擇。

{'type':'uri',
 'label':,
 'uri':}

  上面那種就是帶你上網的,'label'用來設定'button'上顯示的字串。

{'type':'postback',
 'label':,
 'data':}

  而這邊第二種,是我今天想用的,可以製造出 PostbackEvent 的。讓我們來看一看 PostbackEvent:

{"type":"postback",
 "reply_token":,
 "source":{"user_id":, "type":},
 "timestamp":,
 "postback":{"data":,"params":{"datetime":}}}

  裡面資料有很多,但真正重要的是"postback"裡面的"data",這正是我們靠'action'傳送過去的"data"

實際的來試一試

  當我們從 FlexMessage 模擬器寫出想要的 FlexMessage 之後,要怎麼交給 LINE 聊天機器人呢?
  溫故知新一下,大家還記得我們的TextSendMessage()嗎?

line_bot_api.reply_message(
    event.reply_token,
    TextSendMessage(text=reply)
)

  好的,那麼FlexSendMessage()就是這樣用的:

FlexSendMessage(
    alt_text = '說明文字',
    contents = 你在Flex Message Simulator寫出來的程式碼
)

  alt_text不重要,但是一定要有。contents則是我們在 FlexMessage 模擬器中創造出來的一大串程式碼。好的,我們實際來試一試:

def index_flex(event):
    
    if '查詢訓練紀錄' in event.message.text:
        
        line_bot_api.reply_message(
            event.reply_token,
            FlexSendMessage(
                alt_text = 'index',
                contents = index
            )
        )

  我先定義一個用來回傳 FlexMessage 的函數,啟動的條件是,對 LINE 機器人說出'查詢訓練紀錄'contents = index中的index,則是我在 FlexMessage 模擬器寫出來的程式碼。大家想看嗎?好喔:

index = {
  "type": "bubble",
  "hero": {
    "type": "image",
    "url": "https://scdn.line-apps.com/n/channel_devcenter/img/fx/01_2_restaurant.png",
    "size": "full",
    "aspect_ratio": "20:13",
    "aspect_mode": "cover"
  },
  "body": {
    "type": "box",
    "layout": "vertical",
    "spacing": "md",
    "contents": [
      {
        "type": "text",
        "text": "Into the alpaca_training",
        "size": "xl",
        "weight": "bold"
      },
      {
        "type": "separator",
        "margin": "xxl"
      },
      {
        "type": "box",
        "layout": "vertical",
        "spacing": "sm",
        "contents": [
          {
            "type": "box",
            "layout": "baseline",
            "contents": [
              {
                "type": "text",
                "text": "Overall",
                "weight": "bold",
                "margin": "sm",
                "flex": 0
              }
            ]
          },
          {
            "type": "box",
            "layout": "baseline",
            "contents": [
              {
                "type": "text",
                "text": "# 查詢所有 alpaca_training 中的資料",
                "size": "sm",
                "color": "#aaaaaa"
              }
            ]
          },
          {
            "type": "box",
            "layout": "baseline",
            "contents": [
              {
                "type": "text",
                "text": "alpaca_name",
                "weight": "bold",
                "margin": "sm",
                "flex": 0
              }
            ]
          },
          {
            "type": "box",
            "layout": "baseline",
            "contents": [
              {
                "type": "text",
                "text": "# 依照 alpaca_name 查詢",
                "size": "sm",
                "color": "#aaaaaa"
              }
            ]
          },
          {
            "type": "box",
            "layout": "baseline",
            "contents": [
              {
                "type": "text",
                "text": "training",
                "weight": "bold",
                "margin": "sm",
                "flex": 0
              }
            ]
          },
          {
            "type": "box",
            "layout": "baseline",
            "contents": [
              {
                "type": "text",
                "text": "# 依照 training 查詢",
                "size": "sm",
                "color": "#aaaaaa"
              }
            ]
          }
        ]
      },
      {
        "type": "separator",
        "margin": "xxl"
      }
    ]
  },
  "footer": {
    "type": "box",
    "layout": "vertical",
    "contents": [
      {
        "type": "button",
        "style": "link",
        "color": "#1DB446",
        "action": {
          "type": "postback",
          "label": "Overall",
          "data": "Overall"
        }
      },
      {
        "type": "button",
        "style": "link",
        "color": "#1DB446",
        "action": {
          "type": "postback",
          "label": "alpaca_name",
          "data": "alpaca_name"
        }
      },
      {
        "type": "button",
        "style": "link",
        "color": "#1DB446",
        "action": {
          "type": "postback",
          "label": "training",
          "data": "training"
        }
      }
    ]
  }
}

  暈頭轉向吧?

  接著再用去對PostbackEvent作反應:

@handler.add(PostbackEvent)
def reply_postback(event):
    report_record(event)

  這樣子我們就可以用 FlexMessage 跟 LINE 聊天機器人對話了!

  今天的內容有點亂。總而言之,我們的目標是透過 FlexMessage 一問一答的形式,告訴 LINE 聊天機器人我們想要依照什麼條件去抓取需要的資料。今天算是對 FlexMessage 以及 PostbackEvent 有的初步的了解,明天會繼續說明如何使用 FlexMessage 作出一個 LINE 查詢資料小幫手。相關的程式碼我也會在明天放到 Github 上,今天的內容若有不清楚的地方,歡迎直接在下面留言,我會盡可能地回覆大家的。謝謝大家!

參考資料

➀ Psycopg2 使用教學
➁ PostgreSQL 使用教學
➂ FlexMessage 基本架構
➃ FlexMessage 應用程式編程介面
➄ 駝峰式命名 wiki

註:對於此系列文有興趣的讀者,歡迎參考由此系列文擴編成書的 LINE Bot by Python,以及最新的系列文《賴田捕手:追加篇》
第 31 天 初始化 LINE BOT on Heroku
第 32 天 快速回覆 QuickReply 介紹
第 33 天 妥善運用 Heroku APP 暫存空間
第 34 天 妥善運用 LINE Notify 免費推播
第 35 天 製造 Deploy to Heroku 按鈕


上一篇
第 17 天:Heroku Postgres:連接 LINE 聊天機器人
下一篇
第 19 天:LINE BOT SDK:FlexMessage
系列文
從LINE BOT到資料視覺化:賴田捕手30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言