標籤

LINE SDK

2020 一、二月的學習紀錄

learn records

前言

礙於最近做事有點手忙腳亂,沒有很有效的管理時間,記錄下來讓往後能有個經驗可循並增加自我管理能力。

實作項目

Docker & Docker-Compose

我記得上次自己弄 docker container 已經是一年多前,當時還結合 Drone CI 來跑(好久了 👀),對於 Dockerfile 使用方法依稀有點印象,而最近因為看了許多 open source 後對於 Dockerfile 的內容了解度也提升了許多(實在是個不錯的方法 👍),會這樣子弄的原因是我認為 Serverless 的架構很容易被 AWS 綁架,我認為 API 這類可拆出來的功能應該要包成 container 增加彈性不讓平台限制,被這個想法 trigger 後就來好好練習 Docker 了 🏋️‍♂️。

這次使用 Twitch-Bot 做我的練習對象,裡面主要用了 node 以及 mongo 兩個服務,在此同時如果各別起 docker 挺麻煩,因此就需要使用 docker-compose 幫忙啟動,而在這過程中學到了:

  • 把 Dockerfile 遷移到 docker-compose 上 (如何複製別人的檔案(大誤))。
  • 藉由 docker-compose 裡面的 network 連接兩個容器的使用方法。
  • Google container-registry 上傳以及 gcloud 過期的 docker 使用方法(參考),同時也修正後可以使用 docker 的原生指令將 container 送上 Google。

接下來就是趁六月 credit 過期前趕快來玩玩 Google Kubernetes 🚀

AWS IoT Job

最近在公司實作 AWS IoT Job 部分的功能(以下稱為 Job),因為 Job 每次執行收費約 1 塊台幣,之所以會用它是因為在 IoT 執行時不是只會單純發送訊號出來讓後台收訊號做事,而是需要跟後台經過一連串的溝通來達成功能(例如 韌體更新),當然還有很多更複雜的功能,這邊只列舉一個小部分。

從這段時間的實作上學到的部分:

裝置工作列表

當後台的 Queue 同時塞進很多工作時要如何管理並通知裝置此時的工作有哪些(All Job)以及要做哪件事(Next Job),實作這功能時不能只考慮到一個裝置,而是考慮到 N 個裝置時要如何過濾並管理狀態機,限制裝置要依照狀態執行避免系統錯亂。

平台化

之前主要功能都是介接 AWS IoT,而這次是要實作一個類似的功能提供給其他使用者,不能只開一個 API 就想要功能都包好,而是將功能明確劃分,讓使用者可以有彈性的去組合,只是在此時我沒有處理得很好,之後的 Task 需要更注意。

LINE SDK - Narrowcast Client API

參考 Pull Request: Add narrowcast client api #237

工廠模式

功能主要是讓開發者可以透過 API 來做分眾的推播,在實作這個 issue 時讓我原本模糊的工廠模式清晰了許多,主要是 SDK 使用了 Python Class 來擔任 Model 的角色,讓進來資料可以經過 model 去轉換成對應的 key-value,所有的資料都分類進去對應的工廠處理完再回傳出來。

在還沒開始看 code 前只想著要寫什麼邏輯去處理,其實使用 Model 的方法就處理掉大部分資料格式的問題,很多 LINE 文件已經清楚定義的格式只要照著填就解決了,只是在這個 Task 遇到了Python 保留字的問題,而經過各種 google 後只好自己寫邏輯去判斷掉這件事,之後只能繼續看有沒有更好的寫法可以取代掉這個保留字的問題。

Lint

官方 Github 有寫要 Contribute 時需要執行的內容(參考),lint 則是遵照flake8,而執行tox後會對每個版本的 python 去做測試,但像我電腦只有 3.7 版 tox 就只會針對這個版本做測試。

  • 在這件事上學到的 tab空白鍵(space) 的差異,PEP-08 上也告訴建議使用空白鍵,而 python 3 之後也不允許兩個混用,畢盡 python 是很注重縮排,既然官網都寫了哪有不遵守的道理呢?😃

Lint 也不會無緣無故來刁難開發者 XD

  • 內部函式(Class、Function)都需要再第一行下註解,而會這樣用的因素可能在 python 上並沒有 publicprivate 的觀念,透過這樣的方法來表示它(Class、Function)是一個內部函式,藉由 Lint 讓開發者可以知道這個功能是內部還外部,只是在 python 只能使用這個 lint 讓大家遵守這規則,如果沒有 CI 要硬上的話其實也管不住,畢竟不像 golang 這類語言會編譯(汗顏)。

總之使用 lint 的好處就是開發風格可以統一,能夠盡量避免不必要的 programing 麻煩,在寫任何語言都推薦使用各個語言的 lint,雖然一開始會很討厭它,但它真的是在幫助你 😆。

Netflix

這時間與家人一起訂閱了一年份的會員,而我的初衷就是想把英文學好,而我也花了時間好好的看完一部全英文影集(發英與字幕),第一次嘗試這樣看全英文,並且邊看邊 Google 翻譯不懂的單字同時口述跟著念一遍增加記憶力,隨著工作量減輕後下班也可以再安排時間繼續看下個影集學了~

然後迷上了愛的迫降這個韓劇,推薦喜歡看愛情片的朋友去看 ❤️

武漢肺炎

最近剛好因為這傳染病盛行,除了防疫之餘,g0v 也提供 API 讓開發者可以拿去介接,隨著大家各種開發前端 web 以及 chatbot ...的應用後本來在想有個什麼想法可以參與這個全民黑客松,只是我認為用途實在重複性太高,而在大家開發應用時我又剛好在寫 LINE SDK,錯失了第一時間發佈口罩相關的服務建立時間,本來想到 LIFF v2 + LINE Notify,但只是在使用上很容易吵到用戶進而封鎖它(LINE Notify),因此要考慮好功能再來看會不會有比較好的使用情境。

出入公共場所記得戴口罩,有帶隨身瓶酒精的記得消毒!😷

結論

有時候在思考與規劃的過程中如果經驗不夠時真的很難馬上到位,不過任何事情都一樣,若這件事可以輕鬆且又得心應手,代表它可能已經是你熟悉的領域或是過於簡單,就像我一開始處理社群一樣,若是沒有前輩們帶著走的話我跟本無法有效地處理事情,總之凡是起頭難,即早加入即早熟悉各個領域才是王道 😆

參考

AWS IoT 觸發後可以執行的功能 Job Events Tab 與 space 的差異(PEP-08)

繼續閱讀

Day15 - 串接 LINE Richmenu API

前言

之前我在使用時都是直接在 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

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

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

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

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

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

繼續閱讀

Day13 - 試玩 python SDK

前言

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

get_profile

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

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 並逐一推送訊息。

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 的範例來用,若是有在做商家功能的機器人可以使用這個方式讓使用者接收到商家訊息~

self.line_bot_api.reply_message(token, LocationSendMessage(
    title='my location',
    address='Tokyo',
    latitude=35.65910807942215,
    longitude=139.70372892916203
))

StickerSendMessage

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

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

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

Button Template

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

繼續閱讀