2019/06/25 補充:
自從 LINE@ 改成 2.0 後,原本用的開發用方案一律轉成輕量型,即便是用程式自動推播,也算進訊息數裡,而輕量型一個月最多就 500 則。
因此很遺憾的,自動推播功能得取消,目前這篇實作出的機器人,會是自動回覆用,像是回覆當地天氣、回覆目前Google trend、回覆目前幾個新聞的 RSS……等,程式碼一樣可以執行,但在排程的部份將會取消。
2019/07/03補 充:
推播功能逐步移至 Telegram 機器人上,可看這篇:
https://www.letswrite.tw/telegram-bot-gcp/
本篇用到的資源
實作完上一篇的「用LINE Bot API 建立 LINE@ 圖文選單」以後,開始對 LINE API 的說明文件有些了解,這篇紀錄的是實作一個推播天氣機器人的甘苦路。
實作一個「每日早上 7 點,自動推播天氣預報」的機器人,用了以下資源:
- 程式語言:Node.js
- 主機:Google Cloud Platform(GCP)
- 排程:GCP cron
- 資料庫:Firebase
- 氣象資料:中央氣象局
之前陸陸續續寫的幾篇筆記文,就是在為這個功能做準備,可以參考:
Node.js / GCP:用 Google Cloud Platform(GCP)建 Node.js 網站
氣象資料:接氣象局 API、跨網域 AJAX 資料
資料庫:如果對 Firebase 不熟,也可以考慮 如何用 Google Excel 當作資料庫
這篇不會像之前幾篇一樣,完整的列出所有程式碼,因為實作完後,總共寫了四百多行,都是血淚,但會寫出幾個踩到的坑及要注意的事。
先提供實作後的成果:
https://line.me/R/ti/p/%40gnp0209t
npm init
完後,要安裝 4 個套件:
- express
- request:跟氣象局要資料
- line bot sdk nodejs:LINE push
- firebase admin:存取 Firebase 資料庫
npm install express request @line/bot-sdk firebase-admin --save
安裝完後,開一個 index.js 引用上面 4 個套件:
拿訂閱者的基本資料
當使用者傳訊息給 LINE 的機器人,LINE API 會回傳幾項資訊:
{ "destination": "xxxxxxxxxx", "events": [ { "replyToken": "0f3779fba3b349968c5d07db31eab56f", "type": "message", "timestamp": 1462629479859, "source": { "type": "user", "userId": "U4af4980629..." }, "message": { "id": "325708", "type": "text", "text": "Hello, world" } } ] }
replyToken
直接存成變數就行,不用存進資料庫。
message.text
看個人,這篇是拿使用者打的字當關鍵字,讓機器人讀到後可以自動回覆用的,所以只在使用者選擇了地區後才會存結果。
source.userId
,這個必存,他就像 Web Push 時的 token 一樣,是要發送對象的 id,沒有這個 id,Push 時會沒有目標可以 Push。
從 LINE 的 Get profile 文件 中可以知道,有了 userId
以後,可以拿到使用者的公開資訊,一共有 3 個:名稱、大頭照、個人訊息:
{ "displayName": "LINE taro", "userId": "U4af4980629...", "pictureUrl": "https://obs.line-apps.com/...", "statusMessage": "Hello, LINE!" }
userId,是這個機器人給這個帳號的 id,所以每個帳號對不同的機器人會有不同 id。
這一步驟的流程如下:
- 當使用者傳了訊息給機器人 →
- 拿到 userId →
- 用 userId 拿公開資料 →
- 存入 Firebase
前 3 步會用到 LINE Reply API,這部份可以直接看說明文件的範例碼:
ttps://line.github.io/line-bot-sdk-nodejs/guide/webhook.html#error-handling
第四步存到 Firebase 的資料架構如下:
firsebase_id: { userId: { name: encodeURIComponent(line.displayName), img: line.pictureUrl, status: encodeURIComponent(line.statusMessage), location: encodeURIComponent('預設地區') } }
拿 userId 當 key,是之後在跑迴圈發 Push 時,抓資料的地方比較單純,不用一層一層找。
name、status、location 這 3 個,因為值通常是中文,所以編碼後再存。
*踩坑處:userLocation 的部份,要先提供一個預設值,以免有人沒填地區,回傳的值變成 undefined 時,會讓迴圈卡住,造成 Push 失敗。
實際存到 Firebase 的結果:
存使用者選擇的地區
這步驟的流程如下:
- 用圖文選單讓使用者發送「我想選擇預報地區」 →
- 回傳 carousel 樣式,讓使用者選擇地區 →
- 當使用者發送的訊息有「區」這個字以後,更新到 Firebase
建立圖文選單的部份,可以直接用 LINE@ 後台製作。也可以用 API 設定(用 LINE Bot API 建立 LINE@ 圖文選單)。
當 message.text 是「我想選擇預報地區」,用 Reply API 去回傳訊息讓使用者點選地區,Replay API 就一行:
client.replyMessage(event.replyToken, message);
message 部份,就是一個物件,比較特別的是,用 LINE 的後台發訊息,格式很固定,但如果是用 API 的方式發,樣式就多了起來。
除了一般的文字、圖片,用 API 的方式還有 2 種很特別的:template、flex。
這 2 種樣式,怕截圖下來如果因為侵權被吉就糟了,所以直接附上網址,可以點進去看:
https://developers.line.biz/en/docs/messaging-api/message-types/#template-messages
這 2 種物件的寫法,說明文件如下:
這篇選用 carousel 來製作地區選單,格式如下:
在 LINE 上呈現的結果如圖:
從氣象局拿資料
取資料的方式在「接氣象局 API、跨網域 AJAX 資料」這篇就有說明。
這段就寫取哪些資料,跟整理完後的資料格式。
每日天氣預報的部份,預計要發送的資料有 2:氣溫、降雨機率。
所以用氣象局 API 拿的資料就這兩個。
在時間的參數上,因為主要是想讓大家出門上班前,可以決定要不要帶傘,及要不要穿外套,所以時間是抓 0600–2100 這段時間,包含了大部份上下班的時間。
氣象局的 API,氣溫跟降雨機率給的時間參數是不一樣的,因為區間也不同。
- 降雨機率是每 6 小時為一段,氣溫則是每 3 小時。
- 降雨用的是 startTime / endTime,氣溫用的是 dataTime。
因為兩者參數不同,所以在 Request 時也要分開給,最後的處理方式是:
- 用一個 function 處理「今天」的日期 →
- 拿「今天」的日期去寫進 Requset 的 URL 裡 →
- 整理資料
在處理「今天」的 function,有 2 個坑:
月、日的部份,氣象局的要是 2 位數,因此要記得補 0。
function 如下:
這個 index.js 裡,要用到「今天」的日期時,就用 today
這個變數就行。
主機所在地會影響 new Date 的日期。
因為用的是 GCP 的主機,為了一年後還有免費額度存在,所以主機所在地選擇了美洲。但美洲跟台灣是有時差的,上面的 funciton,在 get Date()
時會出現美洲的時間,因此必須寫好時區。
方法是在 index.js 裡加一行:
process.env.TZ = 'Asia/Taipei';
這行必須在 get Date()
之前。
一直寫到現在,整份 index.js 的架構如下:
整理氣象資料後的結果:
以台北市各區當 key,Push 到各人時比較好抓資料。
LINE Push API
LINE Push API 很簡單,就一行:
client.pushMessage(userId, message);
userId
在上面就有取到,並且存在 Firebase 了,只要把 Firebase 的資料抓下來,用迴圈就可以抓完所有存到的 userId
。
推播的 message 選用 bubble,最後會出現的樣式如圖:
建立排程
當存了 userId
,也寫好要 Push 的 message object 後,最後一步就是建一個排程,定時每日早上 7 點執行 function,Push 天氣預報了。
一開始排程是用 Node.js 的一個套件,但後來看了一下 GCP 的後台,發現 GCP 本身就有提供排程功能,免費版的可以設定 20 個排程。
進到 GCP 的後台,到專案的資訊主頁,可以看到選單上有一個「Cron工作」:
有設排程的話,可以看到排程的設定。
排程無法在後台用 UI 來設定,需要上傳一個 cron.yaml 到 GCP 上。
首先在跟 app.yaml 同層的位置新增一個 cron.yaml,檔案內容如下:
url
就是要執行的網址,index.js 就寫:
app.get('/weather', (req, res) => { triggerPushWeather(); res.send('succsee'); });
triggerPushWeather
裡就是寫拿資料、推播的內容,就會執行這個 function 了。
cron.yaml 建立完後,這個檔案要另外傳到 GCP 上,用 gcloud cli 的命令如下:
gcloud app deploy cron.yaml
按下 Enter,在按 y 確認,就會傳到 GCP 上,成功的話,再進到 GCP →Cron 工作,就會看見排程在上面:
之後每天早上 7 點,就會執行 function,推播天氣預報到有加入好友並且選擇地區的使用者了。
Let’s Write 生活小幫手這邊加
https://line.me/R/ti/p/%40gnp0209t
你好,想請問你的line主題是什麼名字? 非常好看!
這個:
[史努比★單寧風]
https://line.me/S/shop/theme/detail?id=32e7367e-8580-438f-9cf0-0f1e94e85404&lang=zh-Hant&ref=lsh_themeDetail
搜嘎,謝謝你的回覆!
大大有學python嗎? 有考慮出python系列的嗎?
沒耶~
python網路上也蠻多大神的,可以Google一下。
請問字對不齊,有辦法解決嗎
像效果圖中的
06:00 30%
12:00 10%
上下看起來就會歪一點,沒辦法對齊
無法耶,因為那是字型造成的。
如果真的很介意對齊,那只能把數字都改成全型,這樣會對齊,但全型數字看起來有點怪就是了。