用 LINE Push Messaging API 推播每日氣象預報

用line push messaging api推播每日氣象預報
用line push messaging api推播每日氣象預報

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 的結果:

存userId、公開資料進到firebase
存 userId、公開資料進到 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 種物件的寫法,說明文件如下:

template message
flex message

這篇選用 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工作」:

GCP Cron工作
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

Summary
用 LINE Push Messaging API 推播每日氣象預報
Article Name
用 LINE Push Messaging API 推播每日氣象預報
Description
本篇大綱:本篇用到的資源、安裝所需套件、拿訂閱者的基本資料、存使用者選擇的地區、從氣象局拿資料、LINE push api、建立排程。這篇紀錄的是實作一個推播天氣機器人的甘苦路。實作一個「每日早上7點,自動推播天氣預報」的機器人。之前陸陸續續寫的幾篇筆記文,就是在為這個功能做準備。
Augustus
Let's Write
Let's Write
https://letswrite.tw/wp-content/uploads/2020/08/logo_512.jpg
訂閱
通知
guest

7 Comments
最舊
最新
Inline Feedbacks
看所有留言
Rongson
Rongson
4 年 之前

你好,想請問你的line主題是什麼名字? 非常好看!

Rongson
Rongson
4 年 之前

搜嘎,謝謝你的回覆!

Hank
Hank
4 年 之前

大大有學python嗎? 有考慮出python系列的嗎?

lee
lee
4 年 之前

請問字對不齊,有辦法解決嗎
像效果圖中的
06:00 30%
12:00 10%
上下看起來就會歪一點,沒辦法對齊