最近小摸了一下 line bot 的技術,老實說還算簡單好玩,但是感覺雷有點多,就乾脆自己寫了一篇簡單的初階教學。(x)

簡單的部分我跳過了,不然字數會太多,主要會圍繞在 line bot 官方文件的解讀上,由於這種文章都會有時效性,如果不能照著做的話請通知我,雖然我也不一定會更新就是了,也歡迎直接觀看 document。

首先,來看看 Line 給開發者進行 API 對接的模式。

從上圖中,可以很清楚的看到 line bot 中伺服器如何與 line server 進行對接和保障對話的安全性。

首先,大家請到 line的開發者平台 上面驗證和註冊。

資料隨便填,註冊過程歡迎觀看 [30 天教你如何玩弄 Line bot API] 第 1 天:玩弄 Line bot API 第一步](https://ithelp.ithome.com.tw/articles/10215656)。

註冊完全不是問題點,創建好 line bot 之後會給你必要的 token 等等,如果需要另外創建的話,可以另外使用 Assertion Signing Key ,生成可以到 https://mkjwk.org/ 此網站進行。

按照這樣子選完之後將 public key 複製到 Assertion Signing Key 的申請欄位就好。

或者是可以在 Chrome 內的 js 控制台中輸入以下 code 並運行來生成。

(async () => {
const pair = await crypto.subtle.generateKey(
{
name: 'RSASSA-PKCS1-v1_5',
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: 'SHA-256'
},
true,
['sign', 'verify']
);

console.log('=== private key ===');
console.log(JSON.stringify(await crypto.subtle.exportKey('jwk', pair.privateKey), null, ' '));

console.log('=== public key ===');
console.log(JSON.stringify(await crypto.subtle.exportKey('jwk', pair.publicKey), null, ' '));
})();

以下是本地端伺服器跟 line server 進行交互的架構圖。

不過這是非必要的啦,一般來說採用系統給的 channel-access-token 就行。

我一開始是用 js 摸的,後來覺得 python 比較熟一點就改用 python 了,接下來會講到如何在本地端搭起一個 server 來回應傳來的資訊。

我先介紹一下 python 要做哪些動作才能進行自動回覆訊息的功能。

先建立一個 python 的虛擬環境

我是用 pychorm IDE 的自動建置虛擬環境,比較簡單一點,如果你是新手,也歡迎直接使用 pychorm,建置版本要求 Python >= 2.7 or >= 3.4,為求穩定和後續開發的間容性我使用3.7。

下載 line-bot-sdk 。

pip install line-bot-sdk

輸入程式碼

以下由 line bot 的官方說明文件範例進行小改寫後講解,先隨便建立一個.py檔案將以下程式碼輸入。

官方 github

from __future__ import unicode_literals

import os
import sys
from argparse import ArgumentParser

from flask import Flask, request, abort
from linebot import (
LineBotApi, WebhookParser
)
from linebot.exceptions import (
InvalidSignatureError
)
from linebot.models import (
MessageEvent, TextMessage, TextSendMessage,
)

app = Flask(__name__)

# get channel_secret and channel_access_token from your environment variable
channel_secret = os.getenv('LINE_CHANNEL_SECRET', '你的CHANNEL_SECRET')
channel_access_token = os.getenv('LINE_CHANNEL_ACCESS_TOKEN', '你的CHANNEL_ACCESS_TOKEN')

if channel_secret is '你的CHANNEL_SECRET':
print('Specify LINE_CHANNEL_SECRET as environment variable.')
sys.exit(1)
if channel_access_token is '你的CHANNEL_ACCESS_TOKEN':
print('Specify LINE_CHANNEL_ACCESS_TOKEN as environment variable.')
sys.exit(1)

line_bot_api = LineBotApi(channel_access_token)
parser = WebhookParser(channel_secret)


@app.route("/callback", methods=['POST'])
def callback():
signature = request.headers['X-Line-Signature']

# get request body as text
body = request.get_data(as_text=True)
app.logger.info("Request body: " + body)

# parse webhook body
try:
events = parser.parse(body, signature)
except InvalidSignatureError:
abort(400)

# if event is MessageEvent and message is TextMessage, then echo text
for event in events:
if not isinstance(event, MessageEvent):
continue
if not isinstance(event.message, TextMessage):
continue

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

return 'OK'


if __name__ == "__main__":
arg_parser = ArgumentParser(
usage='Usage: python ' + __file__ + ' [--port <port>] [--help]'
)
arg_parser.add_argument('-p', '--port', type=int, default=8000, help='port')
arg_parser.add_argument('-d', '--debug', default=False, help='debug')
options = arg_parser.parse_args()

app.run(debug=options.debug, port=options.port)

這邊在後面要重填一下在註冊好的 https://developers.line.biz/console 當中 Basic settings 內的 Channel secret 以及 Messaging API settings 中的 Channel access token (long-lived) 項目。

channel_secret = os.getenv('LINE_CHANNEL_SECRET', '你的 Channel secret')
channel_access_token = os.getenv('LINE_CHANNEL_ACCESS_TOKEN', '你的 Channel access token')

沒有改直接跑的話,程式碼會出現下面字樣來提醒。

Specify LINE_CHANNEL_SECRET as environment variable.
or
Specify LINE_CHANNEL_ACCESS_TOKEN as environment variable.

不需要下載其他 lib 這樣就可以跑了。

讓程式可以跟外網溝通

但是我們會遇到下一個問題,跑完之後他會連到的位置是 http://localhost:8000,而 line 的伺服器無法從外網送資料進入內網給程式,也拿不到要傳給使用者的內容。

這個時候就需要到 ngrok 下載處把他下載下來,ngrok 可以方便的將內網轉換成外網的一個隨機網址。

然後下面的運行部分是在 linux 環境下的指令,所以 windows 會出現找不到指令的情形,這個時候就到下載好解壓縮完的 ngrok.exe 所在資料夾,記下路徑然後在虛擬環境內新開一個 terminal (終端),將路徑導向程式所在資料夾。

cd C:\path to file

接下來輸入下面的指令。

windows

ngrok.exe http 8000

linux

./ngrok http 80

就可以看到類似以下的介面。

ngrok by @inconshreveable                                                                                                                                                                                              (Ctrl+C to quit)

Session Status online
Session Expires 1 hour, 8 minutes
Version 2.3.40
Region United States (us)
Web Interface http://127.0.0.1:4041
Forwarding http://3327-218-166-17-89.ngrok.io -> http://localhost:8000
Forwarding https://3327-218-166-17-89.ngrok.io -> http://localhost:8000

Connections ttl opn rt1 rt5 p50 p90
13 0 0.00 0.00 0.32 0.94

HTTP Requests

https://3327-218-166-17-89.ngrok.io 就是 ngrok 幫我建立在外網的一個隨機網址,接下來將此網址複製,並在後面加上 /callback (仔細看程式,程式會在這個網址回應)貼到 https://developers.line.biz/console 中 Messaging API settings 內的 Webhook URL,貼完按下 verify 基本上就可以驗證成功了。

想要開啟 ngrok 提供的 Dashboard 的話請按下 Ctrl + 點選 Web Interface後面的網址,例如 http://127.0.0.1:4041

如果需要多個 callback URL 的話,原先官方給的資料是換行填寫,但是我實測他換行功能好像鎖起來了,或許可以發一下 issue 詢問。

接下來用自己的手機將創建出來的機器人加為好友之後就可以開始玩了。

該從哪裡開始下手去對於程式碼做出改變

找到callback()裡面的這個然後就可以先試著做更改了

for event in events:

if not isinstance(event, MessageEvent):
continue
if not isinstance(event.message, TextMessage):
continue

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

在加入好友之後,可以對機器人進行對話。

例如對它說一個 hi。

接下來我幫大家捕捉了程式碼當中的 event 查看內部到底有什麼呢 ?

event 的格式如下:

{
"message": {
"id": "14651735376424",
"text": "hi",
"type": "text"
},
"mode": "active",
"replyToken": "48d30e0ca8594d5fb1f9d956f561597e",
"source": {
"type": "user",
"userId": "Ud3903825f232e3d357c074ece1efd5dd"
},
"timestamp": 1630174218140,
"type": "message"
}

可以從上很明顯的看出,使用者說了一個 hi,如果呼叫此函式。

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

機器人就會回應使用者一個 hi。

所以可以直接拿這個框架,配合簡單的字串處理找出關鍵字,回復使用者一些訊息。

基本上做到這裡就差不多完成了,但是 line bot 回復的時候還會多加上一段話。

感謝您的訊息!

很抱歉,本帳號無法個別回覆用戶的訊息。
敬請期待我們下次發送的內容喔(content)

這是因為在 Messaging API settings 中的 Auto-reply messages 有預設自動回復,點擊 edit 選項之後,將在進階設定內的自動回應訊息設定關閉就好了。

好累喔,不知不覺六千多字,圖片等資料傳送和處裡以及 line 的一些有趣功能下次再寫,寫部落格比直接實作難多了。