0%

前言

在第 Day 13 Python SDK 試玩的時候使用過 get_profile,當時使用時就是在每次訊息來的時候才去 SQL 儲存使用者資訊,若只是單純想要使用者資訊的話這樣用好像有點多餘 😓,以下就帶各位簡單的實作。

實作

我們要將上篇的前端頁面(line_login_auth.html)改造一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>LINE Login</title>
</head>
<body></body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.js"></script>
<script>
$.ajax({
url: "https://YOUR_SLS_DOMAIN.us-east-1.amazonaws.com/dev/line/auth",
method: "POST",
success: function(data) {
window.location.replace(data.result);
}
});
</script>
</html>

這邊我將 button 的 onclick 事件以及 button 本身移除掉,目的是要讓使用者一到這個頁面之後就直接透過 AJAX call api

到了這裡應該有感應到什麼了吧!接著就是把這個 html 檔的 S3 路徑網址使用 QR code 包起來,讓他看起來像是 LINE bot 的 QR code

到了這邊基本上已經快完成了,參考 LINE 的好友說明或下圖

接著到機器人的開發者頁面,並且往下滑會看到自己機器人的 QR code 以及 Bot 的 ID,把他複製下來並製作機器人的加好友網址

像是這樣子

1
https://line.me/R/@1234

接著到 controller/line_login_controller.pydef get(self)最後一行,原本我們這邊做的是回傳 JSON

1
2
if dt:
return {'result': dt}, 200

改用

1
2
3
from flask import redirect
if dt:
return redirect("https://line.me/R/ti/p//@127ojvgz", code=302)

如此一來在登入驗證完之後就會把使用者導向到加好友的頁面,在手機上若已經加過好友則會直接導到機器人對話窗上,如此一來就可以讓使用者在加入好友的同時也綁定完帳號,在未來使用的時候 api 這邊也比較好控管使用者的權限問題。

結論

像是我自己做的健身記錄機器人,就是以這個方法下去實作,有聽過一些朋友跟我反應這樣的方法多此一舉,但我認為若是有網頁的話也是套用 LINE SSO 來實作,就可以讓兩個入口去統一,在狀態流上就能比較好控制了。

前言

現在有規模的平台提供商基本上都會提供 Single Sign On (SSO)的登入機制,藉由他們的平台讓一般小網站可以讓使用者免密碼登入,雖然有很多種 Oauth2 的套件,不過我還是喜歡自己打 🤣,下面就開始嚕!

實作

views/建立一個line_login_auth.html,填入以下內容,主要是實作一個按鈕並透過 javascript 來互叫 api 並導向到 LINE 頁面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>LINE Login</title>
</head>
<body>
<button class="auth" onclick="auth()">LINE LOGIN</button>
</body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.js"></script>
<script>
function auth() {
$.ajax({
url: 'https://YOUR_SERVERLESS_DOMAIN.amazonaws.com/dev/line/auth',
method: 'POST',
success: function(data) {
window.location.replace(data.result);
},
});
}
</script>
</html>

接著我們在.env加入 LINE Login 的環境變數並輸入對應的參數

1
2
3
LINE_LOGIN_CLIENT_ID=
LINE_LOGIN_SECRET=
LINE_LOGIN_URI=https://YOUR_SERVERLESS_DOMAIN.amazonaws.com/dev/line/auth

最後就是我們最重要的部分了,實作 GET a& POST 的 api,這邊我先從 POST 講起,這邊主要是把環境變數組在網址中回傳給前端,這邊需要注意的是state他是在回傳回來的 JWT 需要解碼時所使用的,這邊我簡單打個字串,然而實際上會隨機產個變數,確保解碼的安全。

GET 則是負責處理從 LINE 回來的參數們,這邊我只用回傳回來的stateid_token解開,拿到裡面的使用者的參數,後續可以參照應用把資訊存下來,放入 session 並告知前端已登入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
from flask import request
from flask_restful import Resource
import requests
import webbrowser
import os
import jwt
import json


class LineLoginController(Resource):
def get(self):
r = requests.post(
"https://api.line.me/oauth2/v2.1/token",
data={
"grant_type": "authorization_code",
"code": request.args.get('CODE'),
"redirect_uri": os.environ.get('LINE_LOGIN_URI'),
"client_id": os.environ.get('LINE_LOGIN_CLIENT_ID'),
"client_secret": os.environ.get('LINE_LOGIN_SECRET'),
}, headers={"Content-Type": "application/x-www-form-urlencoded"})
payload = json.loads(r.text)
print(payload)
token = payload.get("id_token")
if token is None:
return {'result': payload['error_description']}, 400

state = request.args.get('state')
token = token.encode()
dt = jwt.decode(token, state, None, algorithms=['HS256'])
if dt:
return {'result': dt}, 200

def post(self):
r_uri = os.environ.get("LINE_LOGIN_URI")
client = os.environ.get("LINE_LOGIN_CLIENT_ID")
state = "nostate" # it will be random value
uri = f"https://access.line.me/oauth2/v2.1/authorize?response_type=code&client_id={client}&redirect_uri={r_uri}&scope=profile%20openid%20email&state={state}"
return {'result': uri}

JWT 解開的參數如下,sub就是 LINE 的唯一值 ID,一般拿到這個就可以知道是來自 LINE 的使用者了。

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"result": {
"iss": "https://access.line.me",
"sub": "Ud6ab866a67xxxxxxxxb12b0baffb8ac",
"aud": "1622939248",
"exp": 1569925548,
"iat": 1569921948,
"amr": ["linesso"],
"name": "NiJia Lin",
"picture": "https://profile.line-scdn.net/0hKvTocMuLFFlpFj9HpKdrDlVTGjxxxxxxxxx1YHACMOPRtEHmkWJVIKUyUJNkQWGT0X",
"email": "loxxxxx09@gmail.com"
}
}

最後別忘了要在api.py加入路由才會被導出去ㄛ

1
2
from controller.line_login_controller import LineLoginController
api.add_resource(LineLoginController, '/line/auth')

結論

在實作的時候不知道為什麼一直不能讓 python 去幫我把用戶導去 LINE 認證的頁面,我記得之前寫 Ruby 可以啊(?),最後只好讓前端去幫我把使用者導出去,這邊就帶大家做到這嚕!

前言

上篇介紹了好多了,這邊就不廢話了,直接就帶大家申請一個 LINE Login 的服務。

實作

首先到自己的開發者頁面按+,並選擇最左邊的選項

接著輸入App name(服務名稱)、App description(服務簡介),因為我們是實作 API ,所以選擇Use WEB的選項,接著再輸入email

把該填的填完之後按下送出就會看到有一個新的服務嚕!

點進去後往下滑,可以看到有個 link to this channel 的選項,這邊選擇一開始建立的那隻機器人,綁定之後就變它的形狀啦(?)

接著再往下會看到 Email 的狀態是 Unapplied,然後按下右邊的Submit

會看到這樣子的一個框框,把兩個框框打勾並選擇一張圖片上傳,這部分就算是申請帳號下面的同意服務條款一樣,圖片就上傳自己希望當使用者到你頁面的時候想看到的圖片 🤣,送出之後就會看到狀態改成 Applied

結論

很多時候實作對我們來說已經不是什麼問題了,重點是在找不到服務要怎麼去申請 😓,這篇也算是記錄一下申請的步驟細節,接下來會實作個簡單的認證頁面以及 API,打完收工嚕!

前言

LINE 中文版也上線啦 URL
去某些網站登入你可能會有像下面這樣

或者是你有玩過 LINE Rangers (怎麼感覺我好老),就會看到他們登入畫面有個登入鈕,按下去按確認後就登入,使用者不用輸入帳號密碼。

這個機制被稱為 SSO (single sign-on),它是透過 OAuth2 的機制來做認證,因為不儲存密碼除了可以降低儲存第三方網站的風險,以及使用者不需要記憶各種不同的密碼所帶來困擾,像是 Facebook、Google、LINE…都有相關的機制可以串連 SSO 的服務。

以下會介紹 LINE Login 的流程。

流程

這裡我使用 LINE Login

  1. 服務要引導使用者到 LINE 認證頁面並帶著必填的欄位。
  2. LINE 會在瀏覽器上打開認證頁面,使用者必須按下同意才會進行認證。
  3. LINE 會透過設定過的 redirect_uri 還給你一個 authorization code 以及 state 在 Query string 上。
  4. 你的服務需要將 authorization code 打到https://api.line.me/oauth2/v2.1/token
  5. LINE 確認完之後就會送你一個 access token 嚕!

    可以拿透過 social API 去拿到相關資訊

結論

下一篇再帶著大家申請一個 LINE Login 的服務並建立一個簡單的註冊頁面~

參考

OAuth wiki
LINE overview
LINE 中文文件

前言

過往寫了這麼多的 api,總是會有特定的功能會需要輪詢資料庫或是監聽某些事件,以前還要到弄個虛擬機寫腳本用 crontab,這時候 Lambda 的用處就來啦!不只可以處理 api 的事情,還可以設定排程做事,太舒服啦,以下就來介紹一下~

實作

首先在 consumer 資料夾下line_bot_alert.py,再寫一段需要定時跑的程式,這邊我去抓中央氣象局的資料,大概簡單判斷一下區域以及算出他的範圍:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import json
import requests

def alert_handler(event, context):
air = requests.get(
'http://opendata.epa.gov.tw/webapi/Data/REWIQA/?$orderby=SiteName&$skip=0&$top=1000&format=json')
air_body = json.loads(air.text)
air_status = ["良好", "普通", "對敏感族群不健康", "對所有族群不健康", "非常不健康", "危害", "資料有誤"]
total, count = 0, 0
for fetch in air_body:
if (fetch['County'] == "臺中市" and (fetch['SiteName'] == "西屯" or fetch['SiteName'] == "沙鹿")):
count += 1
if fetch['AQI'] != 0:
total = total + int(fetch['AQI'])
switch = 0
pm = int(total / count)
print(pm)
if pm in range(0, 50):
switch = 0
elif pm in range(51, 100):
switch = 1
elif pm in range(101, 150):
switch = 2
elif pm in range(151, 200):
switch = 3
elif pm in range(201, 250):
switch = 4
elif pm in range(251, 300):
switch = 5
else:
switch = 6
payload = f"\n空氣品質: {air_status[switch]}"
r = requests.post('https://1uv2o723o4.execute-api.us-east-1.amazonaws.com/dev/notify/sqs',
data={'message': payload})
print(r)

serverless.yml的 function 下面加入 Lambda,handler 就是 資料夾/檔案.類別,設定事件(event)排程一分鐘一次

1
2
3
4
5
function:
line_bot_alert:
handler: consumer/line_bot_alert_handler.alert_handler
events:
- schedule: rate(1 minute)

AWS Lambda 有兩種設定排程的方法,rate以及cron,若是比較固定時建議用rate,比較複雜的排程就可以讓cron來,下面兩張圖分別是來自 AWS >


sls deploy部署之後就會看到剛剛做line_bot_alertfunction,他每分鐘都在跑,若玩完之後不想用了可以下sls remove把上面有的東西刪掉嚕!

結論

其實還有很多應用可以實作排程,像是有朋友就用 GCP 的來定時發文 🤣,而且可以把腳本寫得像是 api 一樣,讓整個靈活性都增加了不少,下一篇就帶大家來做 LINE Login 嚕!

參考

AWS document

前言

用 Serverless 用得這麼爽,也是時候來看一下他的資源在哪,距離我上次自己手動上傳 python 檔已經是 N 個月之前的事了(遠望),當時也沒用過什麼Api Gateway相關的服務,以下就帶大家來介紹一下嚕!

介紹

CloudFormation

CloudFormation 是一個 AWS 所提供的 JSON-base 服務,主要是用來讓使用者能夠快速建立 AWS 的服務。

這邊也不用擔心設定的問題,Serverless 再 deploy 的過程中會幫忙 CloudFormation 的 json 檔


透過 CloudFormation 建立 JSON-based 的 template 你可以快速建立所需要的資源堆疊(stack)。


按下Template後如下圖所示,他會顯示你已經有建立的服務 Map,整個線牽起來看起來就很爽 XD

Api Gateway

Serverless 建立過程中也會透過 CloudFormation 幫忙建立 Api Gateway,因為我們是用 WSGI,所以只會看到兩個ANY的路由。
點進去後就可以看到 Client 是怎麼進出的,最右邊是打到 AWS Lambda 上,可以點選上面的字進入 Lambda 所在地。

Lambda

點選進來之後可以看到 Lambda 接了什麼服務,以我到目前的程式就會有API GatewaySQS以及預設的CloudWatch(都是透過這個來看 log)

接著往下會看到之前設定的環境變數以及STAGE,這些都是托Serverless的福免去這些麻煩的設定

S3

Serverless 在過程中會在 S3 幫忙建立一個 Bucket,並在後面加個 hash key 以免 bucket 重複。

Cloud Watch Log

在開始測試後 AWS 會幫忙在這個地方輸出 Log,

Serverless deploy 紀錄

這邊使用 Serverless deploy --verbose 就可以看到所有部署中的基本資訊了

首先因為我們有使用.env,所以在一開始時會先設定環境變數,接著就是把相依套件的東西都放進去,再去啟動 CloudFormation


接著會建立一個 S3 的 Bucket,將程式透過 CloudFormation 處理成 zip的檔案格式並上傳到 S3 上面


接著就會部署到 Lambda 以及 Api Gateway 上,並做對應 CloudFormation JSON 檔的設定


在最後部署完之後就可以看到所有的基本資訊,物件名稱、部署階段、區域、網域以及 Function 們(因為 Lambda 是 Function-base 的服務)等等的基本資訊都會在此

結論

以目前的架構基本上到這麼基本資訊都顯示出來了,若是有使用像是Route 53CloudFront等等的服務它就會顯示在這邊了。雖然說 CloudFormation 在設定上已經很方便了,但不得不說 Serverless 真的幫忙做了很多事,讓開發者又更能專注在打 Code 上了~

參考

使用 CloudFormation 快速部署 AWS 資源

前言

看著很多人的機器人都有上下頁的功能就心癢癢,一直沒有實際去操作,雖然看起來好像不難,但身為一個肥宅就是只有懶,到了現在才把他實作出來(羞),以下就帶大家來做嚕!

事前準備

首先我做了兩個超級陽春的圖片,來展示一下上下一頁的樣子(超弱)
(1)


(2)

實作

我把上一篇的 richmenu_api_controller.py 的 create 部分改編了一下,
增加了讀 state Query string 的一個參數,用up&down來簡單判斷上傳的內容要傳什麼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
if usage == 'create':
rich_menu_to_create = None
if request.args.get('state') == 'up':
rich_menu_to_create = RichMenu(
size=RichMenuSize(width=2500, height=843),
selected=False,
name="Nice richmenu", # display name
chat_bar_text="我是第一頁",
areas=[RichMenuArea( # 這邊是陣列的格式,可以動態設定自己要的區域想要有什麼功能
bounds=RichMenuBounds(
x=0, y=0, width=2500, height=843),
action=MessageAction(
label='message',
text='下一頁'
))]
)
elif request.args.get('state') == 'down':
rich_menu_to_create = RichMenu(
size=RichMenuSize(width=2500, height=843),
selected=False,
name="Nice richmenu", # display name
chat_bar_text="我是第二頁",
areas=[RichMenuArea(
bounds=RichMenuBounds(
x=0, y=0, width=2500, height=843),
action=MessageAction(
label='message',
text='上一頁'
))]
)
else:
rich_menu_to_create = RichMenu(
size=RichMenuSize(width=2500, height=843),
selected=False,
name="Nice richmenu", # display name
chat_bar_text="我是測試使用",
areas=[RichMenuArea( # 這邊是陣列的格式,可以動態設定自己要的區域想要有什麼功能
bounds=RichMenuBounds(
x=0, y=0, width=2500, height=843),
action=URIAction(label='Go to line.me', uri='https://line.me'))]
)
rich_menu_id = self.line_bot_api.create_rich_menu(
rich_menu=rich_menu_to_create)
print(rich_menu_id)
return {'id': rich_menu_id}, 200

接著回到 message_api_controller.py 把前幾篇的 reply SDK code 先註解掉,並加入以下的 code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
message = event['events'][0]['message']['text']
if message == "上一頁" or message == "下一頁":
try:
rich_menu_id = self.line_bot_api.get_rich_menu_id_of_user(id)
except:
# link default rich menu
self.line_bot_api.link_rich_menu_to_user(id, "richmenu-269cc28b8e8497d76c2df062b274a2ce")

if rich_menu_id == "richmenu-269cc28b8e8497d76c2df062b274a2ce":
self.line_bot_api.link_rich_menu_to_user(id, "richmenu-e31be74ad7e577b4752ab70c9c2a3fba")
else:
self.line_bot_api.link_rich_menu_to_user(id, "richmenu-269cc28b8e8497d76c2df062b274a2ce")
else:
self.line_bot_api.reply_message(token, TextSendMessage(text=message))

這邊簡單判斷讀取使用者回應來進入控制的程式碼,透過get_rich_menu_id_of_user來抓取當前使用者的 rich menu,但因為他是會返回 Error response,所以這邊就使用try except來包
except 部分就是透過link_rich_menu_to_user來設定當前使用者為第一張圖片的 richmenu id,下面的判斷式就是讓他互換成對應的 rich menu id 來換頁。

Richmenu 那邊事實上是要用 postback,因為要 demo 的關係我就弄成一般的文字格式,因為這個上下頁字串的理論上是不該讓使用者看到的,只要用圖片來引導使用者就好。

Demo


結論

透過這次練習大概把 LINE SDK 玩得差不多了(message api & richmenu api),寫到了今天真的是苦讀 LINE 的文件啊,都要以防萬一我這狗眼沒有看錯而寫出錯誤的程式碼 XD,到這打完收工,繼續期待下一篇~

前言

之前我在使用時都是直接在 Postman 上直接打 API,本來是打算自己刻 接 Richmenu API 的,但剛剛看到Python SDK 很佛心的也提供 Richmenu 相關的 SDK,如此一來就不用自己包一包了。

注意 Richmenu 圖片的方向是 由上到下、由左到右 哦!算位置記得別算錯XD

實作

設定座標

首先先使用 create_rich_menu(self, rich_menu, timeout=None)告訴 LINE 說你的 Richmenu 的基本設定是什麼,格式區域什麼個別要做什麼對應的事情,

Creates a rich menu. You must upload a rich menu image and link the rich menu to a user for the rich menu to be displayed. You can create up to 10 rich menus for one bot.

https://developers.line.biz/en/reference/messaging-api/#create-rich-menu

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if usage == 'create':
rich_menu_to_create = RichMenu(
size=RichMenuSize(width=2500, height=843),
selected=False,
name="Nice richmenu", # display name
chat_bar_text="我是測試使用",
areas=[RichMenuArea( # 這邊是陣列的格式,可以動態設定自己要的區域想要有什麼功能
bounds=RichMenuBounds(x=0, y=0, width=2500, height=843),
action=URIAction(label='Go to line.me', uri='https://line.me'))]
)
rich_menu_id = self.line_bot_api.create_rich_menu(
rich_menu=rich_menu_to_create)
print(rich_menu_id)
return {'id': rich_menu_id}, 200

set_rich_menu_image(self, rich_menu_id, content_type, content, timeout=None)

上面設定完圖片裡每個座標區間要幹嘛了,接著就可以自己弄圖片或是下載官網提供的圖片

MAC 用戶的小技巧

這邊教大家一個小技巧,可以點選想使用的照片,在預覽程式裡的選擇工具->調整大小

接著看到這畫面,把依比例縮放的勾勾取消,然後把長寬調到對應的大小後就完成圖片大小的設定嚕!

https://developers.line.biz/en/reference/messaging-api/#upload-rich-menu-image

1
2
3
4
5
6
7
8
9
10
11
elif usage == 'upload':
file = request.files['the_file'].read()
rich_menu_id = request.form['richmenu_id']
content_type = "image/png"
try:
self.line_bot_api.set_rich_menu_image(
rich_menu_id, content_type, file)
except Exception as e:
print("===Upload Exception===")
raise BadRequest(e)
return {'result': 'upload ok'}, 200

set_default_rich_menu(self, rich_menu_id, timeout=None)

設定完了之後要幹嘛?就是要把他設定default啦!
不囉唆, show you the code.

Sets the default rich menu.
https://developers.line.biz/en/reference/messaging-api/#set-default-rich-menu

1
2
3
4
5
6
7
elif usage == 'set':
rich_menu_id = request.form['richmenu_id']
try:
self.line_bot_api.set_default_rich_menu(rich_menu_id)
except:
raise BadRequest("Maybe your richmenu id error.")
return {'result': 'set default ok!'}, 200

get_default_rich_menu(self, timeout=None)

糟糕,我好像瘋狂建立卻不知道有幾組?用這個就對了!

Gets the ID of the default rich menu set with the Messaging API.
https://developers.line.biz/en/reference/messaging-api/#get-default-rich-menu-id

1
2
3
4
5
6
elif usage == 'get':
rich_menu_list = self.line_bot_api.get_rich_menu_list()
total = []
for rich_menu in rich_menu_list:
total.append(rich_menu.rich_menu_id)
return {'result': total}, 200

delete_rich_menu(rich_menu_id)

不行,我弄的座標什麼全錯怎麼辦?有時候上傳的圖片可能不是自己想的那樣?,透過這個方法來刪掉它

Deletes a rich menu.
https://developers.line.biz/en/reference/messaging-api/#delete-rich-menu

1
2
3
4
5
6
7
elif usage == 'delete':
rich_menu_id = request.form['richmenu_id']
try:
self.line_bot_api.delete_rich_menu(rich_menu_id)
except:
raise BadRequest("Maybe your richmenu id error.")
return {'result': f'{rich_menu_id} is delete!'}, 200

結論

我都把 raise 打成 rails (大誤),LINE 幫忙做的 SDK 真的是好用,透過這次練習我也多學了好多不一樣的用法,順便把自己的腦袋也整理一下 😃,程式碼同步在這邊哦 URL

Rich menu 是一個 LINE 提供給 chatbot 的圖文選單,可以再上面設定很多各式各樣的功能,下圖為一個範例

優點

  • 擁有長板 & 短板的圖片支援,讓一張圖片不再是一張圖片,機器人看起來可以更有品牌風格,根據 User 喜好、訂閱功能或是下一頁 Menu(即時切換)等等不同創意巧思,且可以客製化圖片讓機器人看起來有更不一樣的特色,也讓對話是互動並不限於對話,而圖片來讓使用者可以更直觀地去使用機器人。
  • 一個 Chatbot 可以設定 1000 組的功能選單
  • 針對不同 user ID 顯示不同功能選單
    Rich menu 可以自己設計圖片並上傳,再透過 JSON 的樣式去設定圖片區間要進行各種用途,這邊使用 Youbike 小幫手為範例,設定很多相關的設計,並設計上下頁的選單提供像是 APP 的操作。

限制

  • 圖片格式: JPEG、PNG
  • 圖片尺寸: 2500x1686, 2500x843, 1200x810, 1200x405, 800x540, 800x270
  • 圖片大小: 1 MB

步驟

  1. 先準備一個圖片
  2. 對這個 API 下圖片每個位置要做什麼事 https://api.line.me/v2/bot/richmenu
  3. 接著上傳圖片給 LINE https://api.line.me/v2/bot/richmenu/richmenu-{id}/content
  4. 設定預設 https://api.line.me/v2/bot/user/all/richmenu/richmenu-{id}

結論

有很多的文章都有介紹過類似的功能,這邊簡介一下限制以及下篇要做的步驟~


前言

以前從花落落的文件中找到一個方法,像是使用 push message

我都會笨笨的 call API 的方法去執行功能,殊不知 SDK 早就包好了,然後邊做邊罵自己為什麼要解這些 JSON (拍桌)
到了現在比較會看文件,才知道原來以前是自己雷自己 (捶心肝)…接下來就試玩幾個我覺得常會用到的 method 好了不廢話了,來簡單介紹一下一些常用的方法 😎

get_profile

回傳使用者的個人狀態,可以透過每次的 reply 去拿到使用者的資訊,一般使用的話都話把它存下來,等之後有介接其他服務時方便使用 userId 去判斷使用者。

1
2
3
4
5
6
7
8
9
10
11
12
13
profile = line_bot_api.get_profile(event['events'][0]['source']['userId'])

try:
with Database() as db, db.connect() as conn, conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cur:
cur.execute(
f"INSERT INTO users(id, name, picture) VALUES ('{id}', '{name}', '{picture}')")
except:
pass # 這邊簡單處理相同 id 的問題(id我有設 Private key 哦!)
state = f"Hello 👉 `{profile.display_name}` 👈"
line_bot_api.reply_message(token, [
TextSendMessage(text=state),
ImageSendMessage(original_content_url=profile.picture_url, preview_image_url=profile.picture_url)
])

這邊搭配 ImageSendMessage 來幫忙發送圖片,他主要是透過 original_content_url 以及 preview_image_url 這兩個參數來幫忙轉成圖片

  • original 就是讓使用者看到的圖片
  • preview 在還沒載入時的樣子

push_message

既然都取到使用者的 id 了,下一步就是推訊息給使用者啦!

建立一個 GET 的路由在 /webhook 上,收到 query string 的 msg 的值並找到所有使用者的 id 並逐一推送訊息。

1
2
3
4
5
6
7
8
9
10
11
12
line_bot_api = LineBotApi(os.getenv('LINE_CHANNEL_TOKEN'))
handler = WebhookHandler(os.getenv('LINE_CHANNEL_SECRET_KEY'))

def get(self):
msg = request.args.get('msg')
with Database() as db, db.connect() as conn, conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cur:
cur.execute("SELECT * FROM users")
fetch = cur.fetchall()
for user in fetch:
self.line_bot_api.push_message(
user['id'], TextSendMessage(text=msg))
return {'message': msg}, 200


Reply Mode

LocationSendMessage

這邊直接拿 SDK 的範例來用,若是有在做商家功能的機器人可以使用這個方式讓使用者接收到商家訊息~

1
2
3
4
5
6
self.line_bot_api.reply_message(token, LocationSendMessage(
title='my location',
address='Tokyo',
latitude=35.65910807942215,
longitude=139.70372892916203
))

StickerSendMessage

這個方法可以搭配使用,讓整個回應看起來跟真實吧!

不知道清單能用什麼可以參考

1
self.line_bot_api.reply_message(token, StickerSendMessage(package_id='1',sticker_id='1'))

Button Template

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
buttons_template_message = TemplateSendMessage(
alt_text='Buttons template',
template=ButtonsTemplate(
thumbnail_image_url=f'{picture}.jpg',
title='Menu',
text='Please select',
actions=[
PostbackAction(
label='postback',
display_text='postback text',
data='action=buy&itemid=1'
),
MessageAction(
label='message',
text='message text'
),
URIAction(
label='uri',
uri='http://example.com/'
)
]
)
)
self.line_bot_api.reply_message(token, buttons_template_message)

結論

簡單玩了幾個 API,大概就這幾個比較常搭配著使用,用過之後才知道其實有好多 API 我都還不是很清楚用法 🤣
而 LINE 最近更新了他們文件的功能,多了一個Try的按鈕提供使用者可以線上直接測 API,只是像是 push messages 這個就剛好沒有 😓,但至少有很多 API 不用通靈去測試,直接拿著 Token 在網頁上測試嚕!
Doc Sample

Code is here

參考

LINE develop page