0%

workload

前言

之前鐵人賽寫了一篇文章有提到 Cold Start 的問題,不過只有粗略介紹,最近看到大家忽然又開始討論這個話題,這次就來好好的搜尋資料介紹一下。

平常在做 open source 時比較常遇到休眠狀態應該就是 Heroku,而開頭要先澄清一下他是屬於 PaaS 的架構,而 AWS、Google、Azure 裡提供的 LambdaCloud RunAzure Function 這些則屬於 FaaS 架構,而最常討論 Cold start 時最常會在 FaaS 上討論,只是剛好 Heroku 使用起來也是一樣,這次文章就把他一起抓進來討論啦~

有很多各式各樣的服務,SaaS、FaaS、KaaS、IaaS… 等各式各樣的架構,每個都是為了解決某個問題所誕生的,至於好或不好其實就是看使用場景而定,所以大家在選用時要注意一下你使用的場景哦!

為什麼會休眠

一般會有休眠機制是因為這些供應商在提供服務都是提供按次計費的方案,讓開發者在用時可以需要才喚醒使用,也就是說這些服務都是 事件驅動(Event-Driven)導向,意指是當前服務若收到訊息後,會呼叫對應的 Function 來處理對應服務所接收到的資料(Queue、Notify …),但相對的就是會有休眠讓服務暫停,進而讓較少使用的服務不會因為掛在線上而一直被收費用,而重新啟動這件事就是本篇要提的Cold start

Cold start 流程

在處理資料之後過一段時間若沒有繼續執行,雲端供應商會將模組暫停,此時 function 會處於 inactive 狀態(cold),而當 function 再度被觸發時(cold start)則會再啟動模組來執行對應 function 來處理事件,在模組與 function 之間的關係可以稱他們為 Function chain

休眠狀態喚醒流程

[參考](https://mikhail.io/2018/08/serverless-cold-start-war/#how-do-languages-compare-)

以 AWS 為例,在喚醒時會到 S3 去取得檔案,接著找到相關的 Lambda 並載入相關模組套件,然後再執行觸發的事件,整理過後的下:

事件處理 ➡️ 過段時間 ➡️ 暫停 function 模組(休眠) ➡️ 觸發 ➡️ 啟動 function 模組 ➡️ 事件處理…

這整個流程就是俗稱的 cold start,因為在這個過程中會花些時間,所以若是拿來處理 api 相關問題的話就會有第一批請求很久,然後之後的請求卻特別快的狀態,請大家莫急莫慌莫害怕呀!

平台比較表

平台 多少時間後暫停
AWS Lambda 10 minutes
Google Cloud Functions 介於 3 minutes 之間 5+ hours 都有
Azure Functions 20 minutes
Heroku 30 minutes

前三個為 FaaS 架構,而 Heroku 則為 PaaS 架構喔!

下圖則來自 2019/09 的一篇文章,較深色的部分則為啟動時間。
FaaS platform cold start time

如何處理或是盡可能避免?

以我熟悉使用 serverless framework 部署到 AWS 來說,他們提供了一個 warm-up 的套件,可以設定排程時間讓這個 function 固定去戳其他 function,避免他們進入休眠的狀態,雖然這樣子就能達到跟一般服務一樣的常駐狀態,不過相對來說就要注意次數的使用問題,若是流量還小的話沒什麼問題,但若有一定的流量就須注意一下帳單,因為這些在互戳的過程中還是有算進費用的喔!使用上還要多注意才行。

另一方面要注意相依套件的問題,引用 google 文件的其中一段:

謹慎使用依附元件,如果您使用動態語言搭配相依的程式庫,例如匯入使用 Node.js 的模型,這些模組的載入時間會增加冷啟動期間的延遲時間。您可以透過以下方式縮短啟動延遲時間:

  • 儘可能減少相依元件的數量和大小以建置精簡的服務。
  • 只有在必要時才載入不常用的程式碼 (如果您使用的語言支援)。
  • 使用程式碼載入的最佳化,例如 PHP 的 composer 自動載入器最佳化。

畢竟在載入模組的時候相依套件也是要一起抓進來,若是程式本身太多依賴的話也是會導致 cold start 的時間變長哦!

最後就在幫大家整理一下三大平台的 warm-up 資源:

結論

為什麼要有 cold start 的機制,以供應商的角度來說他們可以提供一定的量讓使用者免費在平台上先建置服務,但若要免費就是服務會被暫時暫停,畢竟現在流量就是錢嗎 💰,像我常用的 Heroku 就會是這樣,而當你流量大的時候就看你要不要搬家,不過一般應該都懶得搬,就會形成所謂的養、套、殺🤣(離題)。

總而言之,若是需要服務時常活著,就是需要付出點流量的錢 💰,也或許你的作品服務時間可能不用那麼長,只要在服勤時間內長駐活著就可以在省下一些費用,如果有需要的話還是付一些錢給供應商,畢竟人家也幫你保管了服務你說是不是?

參考

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)

前言

我對這錯誤的印象是某次在練習寫測試時裝 jest 後出現的,檢查了package-lock.json後看起來jest-haste-map這個套件有用到 fsevents,而之後使用npm install或者yarn install皆會出現以下這個訊息:
node-gyp rebuild error

環境為 Mac OS Catalina version 10.15.2

看起來是在執行階段時node-gyp出了問題,接著它自己重新執行了node-gyp rebuild後出錯,起初我認為是套件的問題並找到了這個回答並執行:

1
2
sudo npm uninstall node-gyp -g
sudo npm uninstall node-gyp

但從來沒有全域安裝node-gyp過,也有些網友執行node-gyp configure是可以的,而我執行後還是失敗,狀態跟這個 issue 差不多。

後來看到 fsevents 在套件說明裡有寫到:

The FSEvents API in MacOS allows applications to register for notifications of changes to a given directory tree. It is a very fast and lightweight alternative to kqueue.

並且注意到剛剛圖裡面有訊息是跟 xcode 以及 apple 相關,最後找到了這個 issue

我的解決方案

有些人可能是有重灌或環境重設導致沒有安裝 xcode,此時只要執行 xcode-select --install 即可(參考說明)。

這邊我使用的解決方案為這個 issue,執行以下兩個指令來重新安裝 xcode:

1
2
sudo rm -rf $(xcode-select -print-path)
xcode-select --install

原因可能是因為我重舊版本向上更新之後 xcode 沒有相容當前版本,所以我使用以上指令來刪除重新安裝。

不過這個指令很暴力的使用 rm -rf,使用它相對危險,使用前請詳閱公開說明書 😆。

結論

為了練習測試安裝了 jest 卻讓我看著一堆 warning 實在是恨得牙癢癢,所幸目前這樣解決完後是可以用的,就看接下來些新的 side project 後會不會有其他問題再來更新,

另一方面也可能因為我在寫 side project 時安裝了一堆有的沒的東西導致這個結果,看來也得找時間來整理一下環境了~

參考

清除 npm 快取

前言

最近剛好看到許多EKSGKE相關的文章,趁著過年時刻先來補足一下 Docker 這個既熟悉又陌生的技能 😆,之後再接著研究如何串接至 kubernetes 並同時到雲平台上,接下來一起來看我從建立到建置的過程紀錄 🙂。

以下就使用我的 side project - Twitch-Bot 做範例囉!

主要使用的功能為 node 以及 mongo

新增檔案

這邊我使用 vscode 的 Docker extension 來幫助我快速建立 docker 相關的檔案:

  • Dockerfile
  • docer-compose.yml
  • .dockerignore

透過以下的 gif 可以快速建立以上三個檔案

操作方式為 Cmd+Shift+P >docker: add > 選擇語言(Node) > 設定輸出 Port

處理 dockerfile

藉由 extension 來幫我們產生檔案第一步就完成了,接著對 Dockerfile 稍做修改:

1
2
3
4
5
6
7
8
9
FROM node:10.13-alpine
MAINTAINER NiJia <louis70109@gmail.com>
ENV NODE_ENV production
WORKDIR /app
COPY ["package.json", "package-lock.json*", "/app/"]
RUN npm install
COPY . /app/
EXPOSE 5000
CMD ["npm", "start"]
  • FROM: 來自什麼映像檔,這裡使用預設給的 node 版。
  • MAINTAINER: 寫描述的位置,這邊我標註我的名稱與 EMail。
  • ENV: 環境變數寫在這,不過這個專案是放在 Github 上,所以我就沒把 chatbot 平台的密鑰放在這了。
  • WORKDIR: 當前在一個乾淨的環境下,而你預想的工作路目錄要叫什麼就在這設定,同時也會一起幫你建立好 (我用/app這個工作資料夾當作範例)。
  • COPY: 把檔案複製到想要放的地方,這裡為了先讓容器安裝完相依套件,因此先把 package.json 以及 package-lock.json 先複製進 /app/ 這個資料夾內。
  • RUN: 執行終端機指令,在上面檔案複製過來之後,這裡我就來安裝相依套件庫。
  • COPY: 安裝完產生 node_modules 之後把原本的有的東西都複製過來。
    • . = 當前目錄
    • /app/ = 欲複製前往的地方
  • EXPOSE: 容器要打出去的 Port 出口,在前面呼叫 extension 時就已經輸入完這欄了。
  • CMD: 幫我執行指令 (參考: CMD 與 ENTRYPOINT 的差別)

docker-compose.yml

這邊我將預設的修改為:

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
version: '3'

services:
twitch-bot:
image: twitch-bot
restart: always
build: .
environment:
NODE_ENV: production
ports:
- 5000:5000
depends_on:
- mongo
networks:
- chatbot-network
mongo:
image: mongo
restart: always
volumes:
- ./data/db:/data/db
networks:
- chatbot-network

networks:
chatbot-network:
driver: bridge

首先將version改成 3,有向下相容的話當然就是要使用新的囉!

接著會建立兩個 container (services):

  1. twitch-bot
    build 後面接著 . 是指說建立在當前位置,depends_on 則是依賴哪個 container,最後則透過 networks (chatbot-network) 來讓服務互相溝通。
  2. mongo
    這邊則就直接去 dockerhub 裡面抓,restart鐵定是必要設定,不然會沒有資料庫 🤣,volumes則是設定說你想映射的檔案位置在哪,這邊我設定當前目錄的/data/db (./data/db)裡面,對應到映像檔裡面的 /data/db 的位置,然後一樣透過 networks (chatbot-network) 來讓服務溝通。

networks 這邊就有點像主機設定網卡一樣,我設定一個 chatbot-network 的網卡它是使用橋接(bridge)模式來驅動(driver)

這部分是參考知乎,對照改出來的

補給站: 一般看到 5000:5000 或是 ./data/db:/data/db 的寫法,它意思是 本地端位置 對應「:」到 容器的位置,像上述檔案就有 port 以及 資料庫位置,這邊就會因應服務而對應到不同的功能。

修改 .dockerignore

這邊因為我很偷懶不想把.env裡的環境變數寫進dockerfile😆,因此在這就把檔案中的**/.env拔掉方便做事 🤣

測試容器

啟動

1
docker-compose up -d
  • -d 表示讓容器跑在背景,如果沒加的話就會在終端機上看到容器們的行為。
  • 會建立本地端的 container image。

關閉

1
docker-compose down
  • 若是使用 Twitch-bot 來試玩的話,則需要多開一個視窗執行 npx ngrok http 5000 來建立一個暫時含有 SSL 的 URL。

建置(build)

如果是按照原本 docker-compose.yml 的打法:

1
2
3
services:
twitch-bot:
image: twitch-bot

這樣的話 docker 預設會幫忙下一個 latest 版號的 tag。

如果是按造以下做法的話在 build 就會幫忙建立 帳號/容器名,並下一個 0.1 的 tag

1
2
3
services:
twitch-bot:
image: louis70109/twitch-bot:0.1

接下來就執行 docker-compose build 幫忙建立一個有版本號(tag)的容器映像檔(container image)。

補給站:

  • 記得要 docker login 哦!
  • 到 dockerhub 或其他私有倉庫建立 container repository

結碖

趁著春節的零碎時間來跟這個熟悉陌生技能打個招呼 😆,同時也體會到大家說到 docker 好用的地方以及給予的彈性,之前都是有需要測試某些功能才會用到,對於 dockerfile 以及 docker-compose.yml 就甚少了解,藉著年假空檔練習,之後只要在熟悉一下整體的使用方法應該就能好好的~用它來幫忙做事了 🚀。

references

連線 mongo container
docker CMD 使用
Overview of Docker Compose
Twitch-Bot

前言

在去年大約五月的時候寫了一隻 Twitch 的 LINE bot,不過後來似乎 Twitch API 有改版讓機器人壞掉了,最近又有需要一隻機器人幫我查詢,就開始去翻一下 Twitch 官方文件,碰巧逛到這個 Community Resources page,看到了好多資料在裡面,就索性來重寫囉!

因為 Twitch package 都是 Typescript,因此本篇的範例都會是 Typescript 哦!

⭐️ 本專案: Twitch-Bot

加好友

  • LINE

    QRcode
  • Messenger

    QRcode

準備

1
2
3
yarn add twitch
// or...
npm install --save twitch

[2020/06/08 修正] 申請完後將 oauth: 後的 key 透過官方文件的 Validating requests 指令放入 access token 中取得 Client_id,如下:

1
curl -H "Authorization: OAuth <access token>" https://id.twitch.tv/oauth2/validate

本專案的 TWITCH_CLIENT_ID 的環境變數就是這個回傳值中的 client_id

這部分可能會需要去 twitch 設定 -> 連結 去綁定 Oauth 的樣子,再麻煩各位注意一下。

  • 平台

    • 需要有個 Facebook 粉絲頁
    • LINE bot
  • 資料庫

    • MongoDB: Heroku 上為 mLab

實作

Clone 專案下來看 ➡️ https://github.com/louis70109/Twitch-Bot.git
首先把.env.sample改成 .env,並將對應的MessengerLINE以及Twtich key 放置對應的位置

跨平台

跨平台含本地總共會有三個:

  • Console (本地端)
  • Messenger (線上)
  • LINE (線上)

Console 是 bottender 的特色之一,可以在終端機上直接測試 Bot 的行為,會先用此模式先測試路由的回覆是否正常。

接著要讓同樣的 Action 可以導到 Messenger 以及 LINE,bottender 提供了可以設計針對特定平台處理 Action,如下:

1
2
3
4
5
6
7
import { platform } from 'bottender/router';
export default async function App(): Promise<void> {
return await router([
platform('line', LineAction),
platform('messenger', MessengerAction),
]);
}

透過platform來把特定平台的訊息到打對應的 Action (如 LineActionMessengerAction),在傳遞 Action 過程中會有一個 context 的變數,裡頭會有底層幫忙整理好的參數:

1
2
3
4
5
6
7
8
9
10
11
12
ConsoleContext {
...
_session: {
_id: 5e08a45c719a1335aeae39bc,
id: 'console:1',
_state: {},
lastActivity: 1578743328620,
platform: 'console',
user: { id: '1', name: 'you', _updatedAt: '2019-12-29T13:04:25.633Z' }
},
...
}

_session 裡有個 platform 的參數,只要判斷這參數就能知道訊息是來自哪個平台,而user則是被正規化完後的使用者資訊 (這兩個參數為目前為止我最常用的參數)

遇到的問題

由於長期使用 LINE flex message 的 button template (參考),這按鈕事件可以設定按鈕上的文字並設定按下去回覆的字串。

而 Messenger 的按鈕就跟人家不一樣了(參考)!他沒有 LINE 的這個事件,差不多的事件只有 postback,在路由上就得特別處理這個事件,為了讓跨平台所使用的內容要統一,在訊息進 router 時就要處理掉:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { messenger } from 'bottender/router';
import { withProps } from 'bottender';

async function LineAction(context): Promise<void> {
return await router([
...
text(/^[f|F]ind\s*(?<topic>.*)$/, searchGame)
...
]);
}

async function MessengerAction(context): Promise<void> {
const payload = context.event?.postback?.payload;
return await router([
...
messenger.postback(
withProps(searchGame, { match: { groups: { topic: payload } } })
)
...
]);
}

這邊找到 bottender 官方文件中函式專門在處理 Messenger 的 postback 事件,並搭配withProps來幫忙把參數 postback 收到的參數傳進對應 Action。

為什麼會要把傳入的參數設定成巢狀 object -> { match: { groups: { topic: payload }}},因為在設計初期本是希望進來的內容都是從text傳進來,意指都是透過使用者打字進來,那 bottender 都會透過傳 match 參數進來(參考),而解析完 Messenger 的 postback 時也得需要符合這樣的格式,因此就這樣設計,當然如果有更好的設計歡迎送 PR 🎉。

Chatbot MVC

Model

  • Twitch: 這裡我設定 userId 為 unique,針對每個平台有自己的 id,每個平台都可以綁定 twitch 帳號,進而去做相關操作。
  • Game: 因為 Twitch 套件這部分是read only,因此我就在建立一個一樣的 model 來處理。

Twich id 是全開放的,只要知道其他使用者的 id 就可以查詢某些功能(參考),這邊我就使用查詢追隨的功能。

View

這裡設計上只要屬於送出訊息相關的樣板(FlexGeneticText)接放在此。

  • common: 作者資訊(author) 以及 幫助我(help) 的相關
  • LINE: Flex message 相關樣板
  • messenger: Genetic template 相關樣板

Controller

在呼叫 twitch api 出來的資料有時候會大於 LINE 以及 Messenger 的樣板限制,因此所有計算服務都會放在這邊,剛好就搭上 Controller 的對應功能。

  • common: 負責將 controller 處理完的內容送至 View
  • twitch: 處理最多人看得遊戲以及搜尋指定遊戲
  • user: 處理綁定以及查詢追隨

結論

基本上做到這如果之後要擴增Telegram或是Slack都可以很快速擴充,只要把相對只要把相對的 Action 加在 App.ts 就行了。

藉由這次也學到跨平台的機器人真的不好做,每家廠牌的行為都不一樣,所幸 bottender 已經處理大部分平台間的問題,接著我們只需要將欲輸出的內容處理好對應框架所提供的功能去回傳給使用者,讓整體的開發可以更專注在開發功能上並更有彈性。

歡迎各位幫我按個星星 ⭐️➡️ https://github.com/louis70109/Twitch-Bot
也一起支持開源專案 bottender⭐️➡️ https://github.com/Yoctol/bottender

Header

其實本來也沒打算在我的部落格發這種 mur mur 文,但總覺得在前往更高層級時總是需要思考一下自己在做的事情是不是對的,接下來就在描述一下好了 😆

Body

就在今天跟著大家一起看GitHub 全球最大軟體工程師交友平台的排名網站(可惜我貢獻還不夠多沒排進去 😭)
https://commits.top/taiwan.html

看著各位為大神的排行榜中點來點去就碰巧點到了這篇文章
https://jaceju.net/be-a-senior-engineer/

而最近也是一直在想這問題

「如何才有資格稱為資深工程師」

以前總想著只要工作一陣子之後我就可以號稱我是 senior
但工作後又跑社群發現認識的一個比一個還強都覺得離他們好遠啊 👀

但看完這篇之後也很慶幸自己身邊有那麼多貴人在幫助我往資深工程師的方向在走
但不管職稱是什麼我認為「自我反省」是一件很重要的事
小到文件錯字、大到系統 runtime 爆炸 🐛
都得從中去找到錯誤去找出讓自己成長的契機

這是最近做了一個類似 AWS IoT Job 的 mur mur 🤣
讓自己成為更好的軟體工程師~

前言

最近在實作需要處理字串並且把它改成真實的判斷式,找 google 的過程中找到了一篇解答,當中剛好看到 Operator 這個套件,以下就用一些範例來示範~

實作

處理的範例: 1=1AND2<5OR8>9

可以使用re把字串處理成陣列

1
2
re.split("AND|OR", payload)
# -> [1=1, 2<5, 8>9]

接著再使用迴圈+同樣的方法去處理掉=><

在處理完之後就可以找到所有的 key 以及 value 的陣列

接著就是要把 ANDOR 找出來存下來,這邊我會會使用split('AND', 1)只切第一個找到的AND字串,因為像是 SQL Where 子句的功能要一個一個處理才行,否則最後判斷會出問題。

1
2
3
4
5
while re.search('(AND)|(OR)', payload) is not None:
result = payload.split('AND', 1)
if result[0] and ... # 判斷 AND 字串
result = payload.split('OR', 1)
if result[0] and ... # 判斷 OR 字串

接著再切完之後會拿到operator_list知道裡面有哪些運算子的陣列,以原本的資料來說可以用迴圈去處理,這邊我就用簡單的範例來表示operator可以處理的東西。

像是operator除了可以判斷純數字以及純字串以外,也可判斷英文+數字的組合。

1
2
3
4
5
6
7
operator_list = ['>', '<', '=']
if operator_list[0] == '>':
result = operator.gt(2, 0)
if operator_list[1] == '<':
result = operator.lt('z01', 'z99')
if operator_list[2] == '=':
result = operator.eq('A', 'A')

使用以上方法可以比對運算子,而使用以下方法則可以比對AND以及OR

1
2
3
4
5
ex = ['AND', 'OR']
if ex[0] == 'AND':
result = operator.and_(1, 1)
if ex[1] == 'OR':
result = operator.or_(1, 0)

結論

透過 operator 這個函示庫讓我處理運算子們可以少很多事,藉此紀錄一下工作上使遇到的一些方法以及問題,最驚訝的就是可以比對英文+中文的判斷式,以一些韌體的本版資訊來說他們會擁有這樣的組合,在上傳之後要比對時用這樣的方法就很方便,若有什麼問題歡迎留言給我 😊

參考

前言

為什麼會想玩這個呢,因為之前串了一隻介接 Twitch API 的聊天機器人,不過最近因為 API 似乎已經翻新了,所以那隻機器人暫時死亡了 😆,但是聊天機器人不限定只有在 facebook、LINE 通訊軟體上,因此就寫了這篇文章來紀錄一下使用過程。

文件 & 申請密鑰

Twitch Chatbot IRC 官方文件

要使用的朋友在 google 時可能會找到另一套,不過他也說請大家來用 tmi.js 了XD

首先要先申請一個 OAUTH 的密鑰,網址在這邊

套件安裝

透過npm安裝tmi.js

1
npm i tmi.js

IRC 通訊

接著需要寫一個腳本透過IRC的方法來與 Twitch 的聊天室連結:

IRC(Internet Relay Chat)是一個位於應用層的協定。其主要用於群體聊天,但同樣也可以用於個人對個人的聊天。

新增 bot.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const tmi = require('tmi.js');
const client = new tmi.Client({
options: { debug: true },
connection: {
reconnect: true,
secure: true,
},
identity: {
username: 'YOUR_NAME',
password: 'oauth:YOUR_KEY',
},
channels: ['yuniko0720'], // 想加入的聊天室
});
client.connect();
client.on('message', (channel, tags, message, self) => {
if (self) return;
if (message.toLowerCase() === '87') {
client.say(channel, `@${tags.username}, 你是第 N 個說 87!`);
}
});

設定檔

  • username: 設定你的使用者名稱
  • password: 申請的 oauth 放在這邊,格式皆是oauth:XXXXX
  • channels: 一般會進入自己的頻道,當然也可以去其他頻道
1
2
3
4
5
identity: {
username: 'YOUR_NAME',
password: 'oauth:YOUR_KEY',
},
channels: ['yuniko0720'],

連結聊天室

這部分則是開始監聽聊天室的內容,所有的範例連結皆在此

1
2
3
4
5
6
client.on('message', (channel, tags, message, self) => {
if (self) return;
if (message.toLowerCase() === '87') {
client.say(channel, `@${tags.username}, 你是第 N 個說 87!`);
}
});

成果

執行node bot.js會看到以下的畫面

其實這樣監聽看起來真的是讓我這個宅心噴發 🚀🚀,可以透過IRC收到聊天室訊息後看後續要怎麼處理,像有些實況主與觀眾的互動可能是輸入+1這樣的文字來獲得獎賞(抑或是互罵 🤣),在很多情境下這樣用會讓觀眾除了跟實況主互動以外還有個對象能玩 👾。

結論

而在這樣監聽下若人數很高之後其實可以收集到更多的資訊去做利用,也許想是開啟訂閱者模式後讓訂閱者輸入特定文字後將資訊傳入Google sheet之類的資料表,在做抽獎等活動性質的內容,對整個遊戲實況內容行銷都會是滿好與觀眾互動的方式~

大概就玩到這邊了,下次想到什麼應用再來說 😆

前言

大家好,我是 NiJia

下半年度辛苦來參加小聚的各位了,最後一季場地一直變更,因為每次小聚都有熱情的夥伴提供場地給小聚,也一起尋找最適合各位的一個場地,若造成困擾小弟深感抱歉 <(_ _)>

2019 年度活動資訊請參考 ➡️Chatbot Taiwan GitHub

小聚活動 & 簡報連結

meetups 14

Topic Speaker Link
What’s new in LIFF v2 and TECHPULSE 2019 preview Evan Lin link
Bottender 一路走來 林承澤(C.T Lin) link
如何使用 kamigo 快速開發 LINE bot 林家煜(NiJia Lin) link

心得

當時 LIFF v1 只能透過 Deep link 的方式讓很多需要 Web view 的動線設計上增加了許多困擾,而在 v2 上線後改善了這方面的問題,且也對外公布支援 Query string 讓開發者可以帶參數進去(雖然之前就已經在偷用了 😆),雖然很不幸的因為 iOS 的政策問題導致 Scan QR code 的功能在接下來的版本硬生生被拔除,但整體下來其實 LIFF v2 對於在 LINE 上開發功能的朋友可以更有彈性的去應用了。

iOS 政策問題參考

Bottender 是在某次小聚聊到時才知道原來有這麼酷的開源專案,我覺得最特別的除了可以支援多平台之外,就是 console 模式了,像我最近做的 ABL 台灣隊賽程機器人就只使用到純文字的功能,在console模式下很快速的就把功能弄好,部署時只要把相關 Key 放進去功能就完成了,這部分是真的很驚艷 😆
上週去參加 GDG Hackthon 時介紹給其他寫前端的朋友使用,從來沒寫過後端的他們也覺得使用上真的很快就上手,所以有寫Javascript的朋友不妨參考試試囉!

當初因為訓練時沒有一個可以幫我儲存記錄的地方,因此想到想做 LINE bot 來解決我的問題,但同時我又不喜歡處理一大堆 JSON 的問題,因此選用 Kamigo 作為我開發 bot 的框架,除了框架幫忙轉打請求轉到對應的controller讓我不用寫一堆if else以外,最好用的功能就是 Kamiflex 了,當我們在 Flex Message Simulator 弄了一堆 JSON 出來時,同時我們需要從資料庫拉資料出來組裝產生大量的 JSON,在組裝時程式碼會很髒亂,所以使用 Kamiflex 來幫忙轉譯讓我在寫 Flex message 時可以更像在寫 Ruby code,讓整個 JSON 可能幾千行瞬間被濃縮成 100 行時這段 Flex message 在維護時也能更好的維護。

Kamigo 流程

Topic Speaker Link
LINE Push / Message 開發經驗談 Caesar Chi
邊緣人救星!用 Laravel 打造私人定製的聊天機器人 李小胖 link
Chatbot builder Penny Sun link

心得

我平時就有在看一些社群行銷朋友分享他們是如何對行為下標籤tag以及發放對應內容給使用者進而提升購買力,在 Caesar 大大的快速了解到這些商業行為背後的開發經驗為何,從設計到大量推送所需注意的問題以及服務器之間的溝通問題…在這次的議程中讓我學到很多付費內容才會聽到的題材,讓我對於 Push message 這件事情的做法與思路又重新被教育了一次 😆

這次要請到一個月有一半時間都在參加小聚的小聚王小胖來為各位分享如何用Laravel做一隻專為自己客製化的機器人,雖然 Live demo 過程中了魔咒,好險後來也解決的一些 live demo 才會遇到的問題 🤣,總之若有使用 php 的朋友歡迎參考簡報哦~

最後由 LINE 的資料工程師-Penny 為我們帶來在 LINE techpulse 上的議程,從介紹中 NLU 部分看起來功能會像是 Dialogflow 一樣,而在其他的部分則是提供一個介面讓使用者可以更快速的在網頁上設定自己想要的機器人樣式,只是目前功能只提供給合作的商家,期待未來 release 時的效益 🚀

官方帳號已經有套用囉!想收到啲一手資訊或是想玩玩的朋友可以掃描加入 👾

所有小聚資訊接收藏在 -> Chatbot Taiwan GitHub 上哦!

閃電秀

14

Speaker Topic Link
陳佳新 LINE Beacon,數位導覽的小幫手!
Eric Flex box, 一起收集分享 Flex message link
徐郁涵 食農飽可夢
郭佳甯 午餐隊長 link

15

Speaker Topic Link
Calvin LINE Beacon,數位導覽的小幫手! link
JT ChatOps & Rasa link
NiJia ABL 台灣隊查詢機器人 link
郭佳甯 Bottender & Kamigo 介紹 link1link2

短評

除了內容豐富的主議程外,閃電秀就是在每個社群最特別的部分了,從每次的閃電秀中都讓我對於 side project 的想法又重新被定義了一番,常常會看到很多不同的應用情境以及特殊的用法,歡迎大家踴躍報名閃電秀來互相交流,多方交流或許有機會蹦出不一樣的想法並讓機器人的其他用途 😁

結論

在舉辦每次的小聚活動都希望可以給每個來的朋友最豐富的內容,以及提供 Lighting talk 的舞台讓有作品的朋友可以上來分享,而最近有與其他社群的朋友聊到,發現似乎把議程塞太緊,導致影響到會眾與講者交流以及回家休息的時間,所以之後的小聚改善這個狀況並調整時間,再麻煩大家多支持 Chatbot Taiwan🚀🚀🚀

大家好,我是 Chatbot Taiwan 的 NiJia,最近我們 Github 開張囉,想找之前演講的紀錄都會在此哦!

前言

事情是這樣的,本週末 12/13-12/15 我要去 GDG Taichung Hackthon Party 上當導師,礙於我實在想不出有個題目可以分享給其他同好,碰巧之前跟過 Wolke 大大的 Dialogflow + LINE bot 工作坊,憶起當時的明媚風景,接著前陣子寫鐵人賽也有跟到 C.T Lin 大大寫的使用 Modern Web 技術來打造 Chat App,如此一來我就乾脆把這兩個東西合在一起弄個東西!

又這麼剛好的我最近瘋狂看 ABL 球賽(支持本土球隊!),剛好週末也沒事,就順手把它做出來啦~有在看球賽的朋友別錯過這隻機器人囉!他可以告訴你下一場比賽是哪時候~

以下記錄著我踩雷的過程 🤣,詳細 bottender 使用手冊還是參考 -> 使用 Modern Web 技術來打造 Chat App

本篇文章的程式碼已經開源囉 Taiwan-ABL-Games

建立專案

1
npx create-bottender-app taiwan-abl-games

接著會看到以下的畫面,bottender 很貼心的列出一些大家平常比較會使用的平台,這邊我們先選擇 LINE,後續會再告訴大家如何增加其他平台選項。

Dialogflow 注意事項與串接

這邊一定是要參考主要開發者所寫的文章啊 -> Day25
大致上創建服務的過程差不多,這個部分就簡單帶過比較重要的部分。

在處理意圖部分有個小撇步,若鼠標從左邊到右邊,到第二個時會把第一個意圖覆蓋掉,這邊我都是從右邊到左邊一個一個選,才不會遇到覆蓋的問題。

接下來再處理 Dialogflow API 的問題,Day 25 文章中所提到的地方 Google UI 似乎有換位置,我找到的位置在此 -> URL

從頁面的藍色按鈕(建立服務金鑰)後會遇到下圖畫面,第一個紅色框框需要注意是不是剛剛建立的 Dialogflow 的 agent。
而第二個下拉式選單選擇 Dialogflow Integrations,下面則選擇 JSON 格式。

在一開始建立完之後若是要回來管理的話,直接到 GCP 的主控台左側的API & ServicesCedemtials來管理金鑰。

把下載的檔案放在主目錄下,接著要在.env下新增以下兩個 key,GOOGLE_APPLICATION_CREDENTIALS則需要打上絕對路徑,後面再部署會再解釋這段。

1
2
GOOGLE_APPLICATION_CREDENTIALS=/Users/.../taiwan-abl-games/YOUR_KEY.json
GOOGLE_APPLICATION_PROJECT_ID=taiwan-abl-games-mpmomf

PROJECT_ID要去 GCP 主控台查詢。

實作

這邊就先複製原作者的 code 並稍做修改至 /src/index.js:

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
const dialogflow = require('dialogflow'); // 記得安裝
const { format } = require('date-fns'); // 記得安裝

const PROJECT_ID = process.env.GOOGLE_APPLICATION_PROJECT_ID;
const sessionClient = new dialogflow.SessionsClient();

module.exports = async function App(context) {
if (context.event.isText) {
const sessionPath = sessionClient.sessionPath(
PROJECT_ID,
context.session.id
);

const request = {
session: sessionPath,
queryInput: {
text: {
text: context.event.text,
languageCode: 'zh-tw',
},
},
queryParams: {
timeZone: 'Asia/Taipei',
},
};

// 透過 dialogflow 偵測意圖
const responses = await sessionClient.detectIntent(request);
const { intent, parameters } = responses[0].queryResult;

if (intent.displayName === 'dreamer-next-game') {
//...YOUR CONTEXT
} else {
await context.sendText('您輸入的內容我不懂哦~🏀');
}
}
};

藉由上述程式碼會發現藉由 Dialogflow 判斷出來的 intent 到底是要填入什麼?intent 顯示的字串就如下圖所示,我們在 Dialogflow 裡建立的 Intent 的名字就是最後 API 會回傳給我們的判斷字串,這邊就正規化一下自己所要提供的意圖吧!

bottender.config.js

最後這邊提一下如何設定給多平台使用,一開始藉由npx create-bottender-app xxx幫我們建立 app 時這個檔案就會自動生成,bottender 很好心的把所有明台所會用到的 key 全部都會放在這裡面。

以我為例我就有使用到 Messager 來試玩,這邊就需要把enabled啟動(false -> true),實際建立粉絲頁相關可以參考

題外話: 最後再使用npx bottender messenger webhook set就會幫我們把 localhost 串接到 Message 上,實在是太讚了!!!

部署注意事項

我是使用 Heroku 當作我部署的環境,請服用這篇Heroku 部署

在本地端測試時會用到很多的環境變數,而 Dialogflow API json 的路徑我是透過環境變數去取得,在 Heroku 上會把專案的內容放在/app底下(不管你專案叫什麼名字),因此環境變數GOOGLE_APPLICATION_CREDENTIALS需要輸入/app/xx-ooo.json這樣就會取到當前目錄下的檔案囉!

結論

經由這次試玩這個 project 讓我覺得 bottender 這個 chatbot 框架整合真的是厲害,幫開發者處理掉很多跨平台上的問題,我認為最酷的就是 console mode,在測試程式邏輯上可以不用每次都一直用手機開 LINE 或是 FB 來測試機器人狀態,而是在終端機就解決這件事,這是我開發機器人到目前為止覺得最酷的模式 👍!

而在這個專案上我使用到 Dialogflow,除了參考的原文畫面 (GCP 頁面) 已有些許更動外,其實介接的速度是非常快的,此外我很認同原文作者的觀點,把 Dialogflow 接在程式後面才去處理這件事會增加很多彈性,畢竟當一個回應來的時候可能會想做其他不同的應用,此時若是模型就在前面先把訊息回覆掉,會增加很多開發上的問題(當然若簡單的功能單人沒問題 🤣)。

總而言之~誠心推薦大家去玩玩 bottender,也許你會跟我一樣發現了新大陸 😎。

參考

Dialogflow
GCP
Bottneder 建立專案
Heroku 部署