0%

前言

用 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

實作

新增兩個 LINE Channel key 至 .env

1
2
LINE_CHANNEL_TOKEN=
LINE_CHANNEL_SECRET_KEY=

接著加入 LINE 的 python SDK 至 requirements.txt

1
line-bot-sdk==1.12.1

透過 pip 安裝一下

1
pip install -r requirements.txt --user

在 controller/ 底下新增一個 message_api_controller.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
36
37
38
39
40
from flask import Flask, request, abort
from flask_restful import Resource
import json
import os
from linebot import (
LineBotApi, WebhookHandler
)
from linebot.exceptions import (
InvalidSignatureError
)
from linebot.models import (
MessageEvent, TextMessage, TextSendMessage,
)


class LineMessageApiWebhookController(Resource):
def post(self):
line_bot_api = LineBotApi(os.getenv('LINE_CHANNEL_TOKEN'))
handler = WebhookHandler(os.getenv('LINE_CHANNEL_SECRET_KEY'))

# get X-Line-Signature header value
signature = request.headers['X-Line-Signature']

body = request.get_data(as_text=True)
event = json.loads(body)
print(event)
try:
handler.handle(body, signature)
except InvalidSignatureError:
print(
"Invalid signature. Please check your channel access token/channel secret.")
abort(400)

token = event['events'][0]['replyToken']
if token == "00000000000000000000000000000000":
pass
else:
line_bot_api.reply_message(token, TextSendMessage(
text=event['events'][0]['message']['text']))
return 'OK'

用任何語言都會看到回傳會有 200, ‘OK’…回傳的成功訊息,在內部系統的部分在處理時要考慮到回傳時間的問題,一般來說如果回傳的時間大於 1 秒的話就有可能失效,或許有些朋友有測試過大於 1 秒也可以,但最好還是於時間內回覆哦!

以下圖片來自 https://engineering.linecorp.com/zh-hant/blog/line-device-10/

https://engineering.linecorp.com/zh-hant/blog/line-device-10/

接著在 api.py 加入下面兩段 code,新增一個webhook的路由

1
2
from controller.message_api_controller import LineMessageApiWebhookController
api.add_resource(LineMessageApiWebhookController, '/webhook')

這邊可以使用sls wsgi serve + ngrok去搭配做測試

接著就部署啦

1
sls deploy

現在在接這種第三方的 API 時最好都使用 https,而使用 Serverless 的好處就是 AWS 在部署完之後會送你一個含有 SSL 的網址~

把網址貼到 webhook URL 上並在尾端加上 /webhook

測試結果如下

結論

這次整合了以前寫的 Repo,加入了.env讓我在抓下來使用時也更方便,也整合成 Restful 格式讓之後有需要的人在看 code 時可以更容易理解了~

專案也會持續更新,更多詳情可以 follow 我的專案 aws-python-line-api

前言

今天會帶大家如何一步一步的建立 Message api 以及需要注意設定的地方。

實作

  1. 首先先到 https://developers.line.biz/zh-hant/ ,點選右上角的大頭貼並選擇 Add new provider
  2. 接著在 Provider name 填上自己的想要的名稱
  3. 再次確認
  4. 選擇想要使用那種服務,這邊我們就選擇Message API並按下Create Channel
  5. 接著就開始填寫自己的機器人(channel)所需要的資訊嚕!
  6. 這邊我們需要填寫的內容為
    6.1. 大頭貼(option)
    6.2. 名稱 App name (required)
    6.3. 描述 app description (required)
    6.4. 類別與子類別 Catagory (required)
    6.5. 信箱 Email address (required)
    6.6. 政策網址 Policy URL (option)
    6.7. 服務條款網址 Term of use URL (option)



  1. 當填寫完之後的寫一步最後會需要勾兩個服務說明確認的選項並按下 Create
  2. 如此一來就看到自己百般折騰建立出來的 Message api 啦
  3. 滑到最下面,首先掃描 QR code 加入自己機器人的好友讓接下來好測試,接著按下圖篇右手邊的 Set message 進入調整頁面
  4. 進入之後到下面,將歡迎訊息 & 自動回應 停用,並將 Webhook 打開,讓之後的串接可以透過 Webhook 打到 Serverless 上,若是前兩個回應的選項沒有關的話即使 Webhook 有啟動也會因為優先權問題被擋下來哦!

簡介

LINE 在 2016 年 9 月底發表了 Messaging API 功能,用戶可以透過 Messaging API 將自家的服務內容串聯到 LINE@ 上。

接著在 2019/04/18 之後 LINE@ 2.0 正式上線,在 5/22 後有升級的選項並會陸續更新,升級完的官方帳號也可以申請專屬 ID、進行帳號認證。

我認為比前一版本的最大的好處是在好友無上限的這部分,以前只能有 50 位好友,若一般開發者的機器人好不容易上線了並開放測試時被限制住,除了再開一隻機器人或是其他方法就超麻煩,現在好友無上限了,就不會有上述的問題了。

若您以前有開發過的機器人到現在還沒升級的話,可以參考 -> URL

這邊需要注意 1 on 1 聊天與 Messaging API 是無法同時使用的,但可以在後台切換看要使用哪種模式與顧客互動。對話記錄也會保存在各自的模式下,若要查看完整的對話記錄的話會建議以固定模式與顧客進行互動哦!

主要的功能環繞在以下兩點

  1. Push mode: 主動推播訊息給用戶,2.0 每個機器人都有 500 則免費推播則數,超過則數的話可以參考以下的價目表:

  1. Reply mode: 這個模式是接受使用者的訊息並透過 webhook 打到伺服器上讓伺服器去做對應訊息的處理,重點是它是是免費、免費、免費的!所以若能引導使用者去輸入訊息的話用這個模式是不用錢的哦 🎉

Message API 支援以下格式

  • Text message
  • Sticker message
  • Image message
  • Video message
  • Audio message
  • Location message
  • Imagemap message
  • Template message
  • Flex message

詳細內容可以參考 LINE doc

line sample 1
line sample 2

回傳格式

Message API 回傳格式都是一個陣列的格式,一開始在接的時候都沒注意到一直瘋狂出錯,事實上這樣陣列對 Server 有個好處就是當一次需要送較多訊息時格式會比較統一,只是對於有潔癖的開發者來說每次都要多打[0]會覺得有點髒 🤣

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
{
'events': [{
'replyToken': '00000000000000000000000000000000',
'type': 'message',
'timestamp': 1568983962754,
'source': {
'type': 'user',
'userId': 'Udeadbeefdaaaaefdeadbeefdeadbeef'
},
'message': {
'id': '100001',
'type': 'text',
'text': 'Hello, world'
}
}, {
'replyToken': 'ffffffffffffffffffffffffffffffff',
'type': 'message',
'timestamp': 1568983962754,
'source': {
'type': 'user',
'userId': 'Udeadbeeaaaaefdeadbeefdeadbeef'
},
'message': {
'id': '100002',
'type': 'sticker',
'packageId': '1',
'stickerId': '1'
}
}]
}

結論

以前剛開始接 LINE Message API 的時候都覺得他的文件不太人性,更新到現在文件內容也越來完整,讓開發者在開發的過程中不用再為了找不到文件而放棄了(我以前就是 🤣),下一篇將會帶來如何申請一隻機器人到如何建立一個 webhook API 到 AWS 上

參考

功能介紹】Messaging API
LINE message template

首先先建立一個consumer/資料夾,新增__init__.py以及notify_handler.py

notify_handler.py輸入以下程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import json
import requests


def line_notify_handler(event, context):
print(event)
body = json.loads(event['Records'][0]['body'])
print(body)
headers = {
'Authorization': f"Bearer {body['token']}",
'Content-Type': 'application/x-www-form-urlencoded'
}
r = requests.post('https://notify-api.line.me/api/notify',
headers=headers,
data={'message': body['message']})
print(r)

# AWS record sample
# {'Records': [{'messageId': 'fddc42ba-a122-4581-965e-d0144ac8a5ad', 'receiptHandle': 'AQEBjO32gY5pXOfOrmDR0hD4k1av9KyjbHFpc+rIBPV2Brif7Lo+jqnGevSjfFwlICyGf+BhWwKaxFw8XdB3QTzRbw0vnLURjnQeDSBrJHa/S57SRs9TOLRBq38maycAVg69iZbetg9VhLMBCcLtOtPHTzKkmo+/Sosm51WA5CzXK7A0rteikx6nxS1CUIpq6MAujodupP0Hgr5RjK5nH/nmxA4Db0leWEmLokalZbtlx4W14tp7PZxPOrQOLDaGrH//p4h32tY8IN3MkCqi+gyNT7kCU4KwCGOIrybb07ZWyKBTKw+KOMNr/Ykj4z2N1qxIvTM55UY9d8V29YsH32OjrZTei5P7Nke/51E2tWkmkqoFAlqzxDjQPvpP+Pvvr8aazeeZ6opkr59UefAiiyM71Q==', 'body': 'hi', 'attributes': {'ApproximateReceiveCount': '9', 'SentTimestamp': '1566621263072', 'SenderId': '901588721449', 'ApproximateFirstReceiveTimestamp': '1566621263072'}, 'messageAttributes': {}, 'md5OfBody': '49f68a5c8493ec2c0bf489821c21fc3b', 'eventSource': 'aws:sqs', 'eventSourceARN': 'arn:aws:sqs:us-east-1:901588721449:LINE_notify_consumer', 'awsRegion': 'us-east-1'}]}

接著在requirements.txt加入boto3,他是一個使用 python 介接 AWS 的套件

1
boto3==1.9.189

加入SQS_URL以及SQS_ARN.env裡面

1
2
SQS_URL=sqs url
SQS_ARN=your sqs arn

add controller/notify_sqs_controller.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
36
37
38
39
40
41
42
43
from flask_restful import Resource, reqparse
import json
from lib.db import Database
import psycopg2.extras
import os
import boto3


def send_message(url, attr, body, delay=0):
cli.send_message(
QueueUrl=url,
DelaySeconds=0,
MessageAttributes=attr,
MessageBody=body,
)


class SendNotifyBySQSController(Resource):
cli = boto3.client("sqs", region_name=os.environ("region"))

def post(self):
parser = reqparse.RequestParser()
parser.add_argument(
'message', required=True, help='message can not be blank!')
args = parser.parse_args()
msg = args['message']
with Database() as db, db.connect() as conn:
with conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cur:
cur.execute(
f"SELECT token FROM notify")
fetch = cur.fetchall()
for f in fetch:
body = {
'token': f"Bearer {f['token']}",
'message': f"Hello everyone, {msg}"
}
cli.send_message(
QueueUrl=os.environ("SQS_URL"),
DelaySeconds=0,
MessageAttributes={},
MessageBody=json.dumps(body),
)
return {'result': 'ok'}, 200

程式寫完了就是要加一條路由/notify/sqs

1
2
from controller.notify_sqs_controller import SendNotifyBySQSController
api.add_resource(SendNotifyBySQSController, '/notify/sqs')

接著透過wsgi在本地起一個 server

1
sls wsgi serve

再搭配 postman 來做測試,測試內容如下

1
2
3
{
"message": "test Content"
}

接著透過sls deploy部署上會遇到一個問題,會有 Access Denied,所以要在serverless.yml加入 IAM role 的設定

add iam in provider

1
2
3
4
5
6
iamRoleStatements:
- Effect: Allow
Action:
- sqs:SendMessage
Resource:
- ${env:SQS_ARN}

測試


結論

使用 SQS 這類服務都會需要透過boto3來幫忙串接,最需要注意的就是 IAM role,因為在本地端的 key 通常權限都是最大的,但上到 AWS 上就會有權限的問題,所以要記得加入 IAM 哦!

Code is here

專案也會持續更新,更多詳情可以 follow 我的專案 aws-python-line-api

詳細介紹

以前自己在架伺服器的時候,提到訊息佇列不外乎都是 RabbitMQ 或是 Kafka,只是現在 AWS、GCP 這類的雲端平台都越來越火紅了,手動按一下服務就建好了(信用卡也在哭泣),省去很多建立服務的時間,只是說一般時候根本找不到項目練習 😓,剛好最近在複習 LINE Notfiy,就趁這個機會順便練習並記錄一下 ✌️

你需要先了解…

當 Http requests 非常大量到一定程度,database 已經跟不上處理的速度,尤其是 relational database,這時就需要 queue 來緩衝;所以社群網站像 Facebook or LinkedIn 都使用大量的 message queue and cache.
By -> Leonard Lee

SQS 就是 managed queue service,主要就是 async, central messaging (對相對應的程式來說,通常處理 api 的會有多個 instances 同時存在,像是 auto-scaling),像是 fb 通知、寄送 email 認證信這種不用即時處理的情況,只要給訊息給 queue 讓其他服務或程式去處理,可以把原本的邏輯簡化(以及責任區分),甚至些事件是預期同時會有多個 listener 會需要處理的情況,在一個 api 裡面去處理這些會讓邏輯變很複雜/不好維護。
By -> Bill Chung

還有就是如果負責處理 request 的 host 有問題,message queue 可以用來短暫儲存還未被處理的 requests 使他們不至於丟失,直到 host 回復正常或是換了一個好的 host 後,message queue 裡面的的 requests 就可以繼續被處理
By -> 盧元駿

以上來自我之前寫過的文章 使用 Serverless 讓 AWS SQS 幫你發送 LINE Notify

今天在需要發個請求去呼叫 LINE API(或是其他服務的 API),都可能會碰到圖片,圖片不管他怎麼壓縮,終究還是比文字肥,在數量多的情況下可能就會發現 API 罷工(就會像被斷詠唱一樣)。雖然平常使用可能不會這麼平凡呼叫,但若在商業用途上使用者多的時候一次呼叫就會有一大筆,這時候用 Queue 讓他們排隊一個一個來就在適合不過了 🎉。

為什麼選擇 AWS SQS

Amazon Simple Queue Service (SQS) 是全受管訊息佇列服務,可讓您分離和擴展微型服務、分散式系統及無伺服器應用程式。SQS 可免除與管理和操作訊息導向中介軟體相關的複雜性及開銷,也可讓開發人員專注在與眾不同的工作上。您可以使用 SQS 在軟體元件之間傳送、存放和接收不限數量的訊息,不會遺失訊息或需要其他服務可用。使用 AWS 主控台、命令列界面或自選的 SDK 以及三個簡單的命令,即可在幾分鐘內開始使用 SQS。(參考 AWS)

SQS 有在免費方案裡面,只要一個月別高於 100 萬個請求就不會被收錢啦 💪

AWS 的免費方案可以參考 -> LINK

建立 SQS

接著就要開始引入 SQS 嚕,首先到 AWS 上的 SQS 頁面,如下


接著選左邊的Standard Queue並填入 name 就好


建立完之後就可以看到下面有建立完的資訊

ARN 以及 URL 接下來都會用到哦

結論

接下來會帶各位使用 Notify 接在 SQS 上,這篇就先帶各位介紹並先手動建立一個 Queue,之後會在大家使用 python 的 boto3 的套件來搭配使用 AWS 服務使用 👏