0%

前言

本篇會介紹 npm 套件庫裡的 Serverless-plugin-warmup,裡面介紹了很多參數可以使用,以下會簡介如何引入它。

實作

透過npm來安裝套件

1
npm install --save-dev serverless-plugin-warmup

serverless.ymlplugin加入套件

1
2
plugins:
- serverless-plugin-warmup

custom加入warm up的設定參數

1
2
3
4
5
6
7
8
9
custom:
warmup:
enabled: true
cleanFolder: false
memorySize: 128
name: "LINE-warmup-pop"
events:
- schedule: "cron(0/5 0-12 ? * MON-FRI *)"
timeout: 10

.gitignore加入以下兩個_warmup/以及.requirements.zip,他在部署前建立一個_warp的資料夾並放一個 Lambda 來打已建立的 api 以及 Lambda 們,部署後會建立.requirements.zip,所以這邊就加入這兩行來防止被 git 推上去。

或許會有疑問不是cleanFolder設定 true 就可以,這邊我自己實測時在部署會因為 Size 過大而無法上傳,把這參數設定掉他才會乖乖的上傳成功,但這部分有待驗證。

加入 decorator

這邊我在lib/下新增一個decorator.py,加入以下的 code

1
2
3
4
5
6
7
8
9
10
11
12
from functools import wraps
from logging import Logger

def lambda_warm_up(func):
@wraps(func)
def wrapper(*args, **kwargs):
source = args[0].get('source')
if (source == 'serverless-plugin-warmup'):
Logger.info('WarmUp - Lambda is warm!')
return {}
return func(*args, **kwargs)
return wrapper

主要功能是透過 python decorator 的特性讓我們在 function 之前用個前綴符號就可以引用它,很像 po 文在 tag 別人一樣 🤣
warm-up 建立的 Lambda 會固定對其他的 Lambda 打serverless-plugin-warmup的字串,這邊就寫個判斷式判斷掉後直接 return

你以為這樣就結束了?還有 IAM role 的問題呢,詳細問題已不可考,想試看看的可以把 IAM 拔掉再部署一次,問題就會出現了,以下為設定檔:

1
2
3
4
5
6
7
8
9
10
11
iamRoleStatements:
- Effect: "Allow"
Action:
- "lambda:InvokeFunction"
Resource:
- Fn::Join:
- ":"
- - arn:aws:lambda
- Ref: AWS::Region
- Ref: AWS::AccountId
- function:*

如果你的目錄結構跟我一樣,或是跟到今天的實作,加入以下的 code,這邊主要是告訴你的 Lambda 要在每次的 warm-up 字串來的時候呼叫這個 decorator 來幫忙處理一下

1
2
3
4
5
from lib.decorator import lambda_warm_up

@lambda_warm_up
def xxx():
pass

我的 Lambda 都加了,那原本的 flask 建立的 api 我要加在哪?

看看這個網址,serverless-wsgi 在實作的時候已經有考慮到這件事了,所以 api 這邊就不用管他,他自己會去把它處理掉!

結論

Warm-up 的機制就是透過建立一個 Lambda 來打一個 flag 到其他 Lambda 來他們別進入睡眠模式,但總是會有人考慮費用的問題,可以參考下圖,若是你建立了一個簡單的服務然後上線了,AWS 給你了一百萬個請求的量,我覺得一個月的請求可以到 100 萬應該是服務有點規模的情況,一個月能到這個量相信你也有一定的能力養它 🤣,爾後每一百萬個請求才六塊台幣,可能比 GCP 還貴,但其實以我來說這樣已經很夠用,而且還扛得住

參考

serverless-wsgi Warm-up code
Serverless-plugin-warmup

前言

以下就為大家帶來簡單的實作以及如何去使用它。

實作

views/下新增一個liff_test.html並加入以下內容

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
46
47
48
49
50
51
52
53
54
55
<html>
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
/>
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.js"></script>
<script src="https://d.line-scdn.net/liff/1.0/sdk.js"></script>
<script>
function initializeApp(data) {
$("#userid").val(data.context.userId);
}

$(function() {
liff.init(function(data) {
initializeApp(data);
});

$("#ButtonGetProfile").click(function() {
liff.getProfile().then(profile => {
$("#UserInfo").val(profile.displayName);
alert("done");
});
});

$("#ButtonSendMsg").click(function() {
liff
.sendMessages([
{
type: "text",
text: $("#msg").val()
}
])
.then(() => {
alert("done");
liff.closeWindow(); // 關掉頁面
});
});
});
</script>
</head>
<body>
<button class="liff_btn">Get user profile</button>
<label>user id:</label>
<input class="form-control" type="text" id="userid" /> <br />
<button class="btn btn-primary" id="ButtonGetProfile">Get Profile</button>
<input class="form-control" type="text" id="UserInfo" /><br />
<label>要傳送的訊息:</label>
<input class="form-control" type="text" id="msg" value="測試" /><br />
<button class="btn btn-primary" id="ButtonSendMsg">要傳送的訊息</button>
</body>
</html>

以上參考來自董老師的部落格

詳細的 LIFF API 文件可以參考

接下來按著之前做過的一樣把這個檔案送到S3裡,接著它的網址複製起來

接著來去之前建立機器人的開發者頁面裡按下最右邊的LIFF

按下去之後呢,再來按下add

跑出這個頁面後,Name好辨認為主、Size選擇滿版(Full)、Endpoint URL就把剛剛複製起來的 S3 網址放進去後按Confirm

大概內容會長得像這樣

建立成功後往下滑會看到 LINE 給你一個屬於他們的網址,接著可以把它貼到你的機器人上(或者任何聊天室)

點下去之後就會看到來自 LIFF 的 Webview 囉!

接下來就可以開始玩裡面的功能了 🤣

結論

LIFF 整合的功能讓開發者在 Bot 裡不好實現的功能透過 Webview 的方式讓使用者可以到頁面上做邏輯處理,透過兩種的結合讓整體的應用就有更多的變化,這邊若要給使用者個話就把 line://xxxoxx 製作成 flex message 或者乾脆傳給使用者,引導他點選,如此就能讓使用者到自己設計的頁面上了。

這邊額外補充一點,有看到程式碼裡面可以拉到Query string嗎?事實上可以透過Query string讓使用者帶些參數來做更多的判斷,像是卡米哥做的Kamiliff就是讓使用者在點選後判斷這個 LIFF 的 Size,在導去對應的路由去做處理,這樣子使用者在建立 LIFF 的時候就不用一個頁面弄一個,否則這樣在 dev 轉 prod 的時候應該會弄到很生氣 🤣。

介紹

LIFF (LINE Front-end Framework)顧名思義就是 LINE 開發在 LINE 這個通訊軟體裡面的 Web view(好饒舌),他是一個很精簡的內建瀏覽器,主要是能夠讓開發者可以使用 HTML & Javascript 可以去處理一些在 bot 不好實現的功能,也讓開發者可以透過使用 JS SDK 去拉使用者的資訊,藉此可以做出更多種的應用。

網址的樣式都像是這樣line://app/ZZZZZ-AAAAAAA,透過 deep link 的方法讓 LINE 本身可以認得這個藉此去導向設定對應的網站,畢竟做平台的公司內部要互相溝通也是,就透過這種方法去讓不同部門的工程師可以互相使用好像也是不錯(?)

它也可以搭配在之前做的 LINE 服務上,Notify、Message api 上都可以串上去搭配不同種應用,而這個簡易的 Web view 有下面三種的顯示格式可以使用,開發者可以搭配不同應用場景去做搭配。

一開始都只能使用 curl 來增加項目,使用方法可以參考這篇,現在可以直接在 LINE Developer 頁面線上新增,像下圖片在這個頁面就可以新增了

1
2
3
4
5
6
7
8
9
10
curl -X POST \
-H "Authorization: Bearer YOUR_CHANNEL_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"view": {
"type": "LIFF_SIZE",
"url": "URL_OF_YOUR_APPLICATION"
}
}' \
https://api.line.me/liff/v1/apps

這邊推薦一個 kamiliff,卡米哥就用一個路由帶著 query string 來判斷,再轉打去其他 controller,如此一來就不需要為了每個頁面去獨立建立 LIFF,如此一來測試跟上線都很好管理,推薦大家去參考卡米哥的 kamiliff 的實作方法~

這個功能最大的好處就是可以在 LINE 裡面就可以直接用,若是把使用者導出頁面基本上轉換率以及使用者體驗一定會變很差,所以若是有在開發 LINE Bot 的話很推薦使用這個。

結論

以前我曾嘗試過使用 LIFF 使用 websocket 去跟 MQTT 溝通( IoT 裝置常常使用的通訊格式),只是他的 websocket 似乎有開,但是總是沒有收到數值,這部分實作也是年初時使用,現在這時候就不知道能不能用了 🤣,若不行就期待哪天他能提供出這個服務了~

參考

LIFF overview

前言

在第 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,到這打完收工,繼續期待下一篇~